how to remove a child and center after that - actionscript-3

Im struggling with a problem and dont know how to solve it.
I want every time i click the mouse the cards to be 1 less from the previous time, and at the same time to center the new (in my case when i click ill have -> 3 cards) again on the stage.
but i can only think of doing this by pushing the newly created cards in a Sprite but dont know how to remove the last card after that.
i did try just myArray[lastElement] but it gives me
ArgumentError: Error #2025: The supplied DisplayObject must be a child of the caller.
at flash.display::DisplayObjectContainer/removeChild()
at MainClass/onClick()
CardClass is representing a simple picture of a card
so i have this code so far:
public class MainClass extends MovieClip
{
private var myArray:Array = new Array();
private var myContainer:Sprite = new Sprite();
public function MainClass()
{
for (var i:int=0; i<4; i++)
{
myArray[i]= new CardClass();
myArray[i].x = myArray[i].width * i + i * 10;
myContainer.addChild(myArray[i]);
}
myContainer.x = stage.stageWidth / 2 - myContainer.width / 2;
myContainer.y = 40;
this.addChild(myContainer);
stage.addEventListener(Event.ENTER_FRAME, onEveryFrame);
}
private function onEveryFrame(ev:Event):void
{
stage.addEventListener(MouseEvent.CLICK, onClick);
myContainer.x = stage.stageWidth / 2 - myContainer.width / 2;
myContainer.y = 40;
}
private function onClick(evt:MouseEvent):void
{
var lastElement = myArray.length - 1;
trace(lastElement);
this.removeChild(myArray[lastElement]);
myArray.pop();
}
}
}
if i try just to add the cards on the stage directly (not like here -> in the Container) it does what i want from it (it removes the last card), but then i cant figure out how to center the whole array of cards on the stage.
any ideas ?

Try this in your onClick method:
myContainer.removeChild(myArray[lastElement]);
You are adding the cards to myContainer, so you remove them from myContainer as well.

You add cards to myContainer element, but remove them from this element, but this didn't consist from this objects.
Your code:
private function onClick(evt:MouseEvent):void
{
var lastElement = myArray.length - 1;
trace(lastElement);
this.removeChild(myArray[lastElement]);
myArray.pop();
}
Replace on this:
private function onClick(evt:MouseEvent):void
{
var lastElement = myArray.length - 1;
trace(lastElement);
myContainer.removeChild(myArray[lastElement]);
myArray.pop();
}

Related

Why am I unable to access the methods of an object via is ObjectContainer

first of all, i'm not a native english speaker but, still, i'll try my best to be understandable and as clear as possible.
So, in my programming class, I need to make a Tile based game (like zelda, for exemple) with animate cc (flash). On a map, I want to make a dance floor with tiles that changes on the rhythm of a music. these tiles are movieclip with two frame, one white and one red.
This is how the tiles are generated:
private function createGrid(): void {
grid = new MovieClip();
addChild(grid);
for (var r: int = 0; r < nbRow; r++) {
for (var c: int = 0; c < nbCol; c++) {
var t: Tiles = new Tiles();
t.x = t.width * c;
t.y = t.height * r;
grid.addChild(t);
}
}
grid.x = 15; //center the grid on x
grid.y = 35; //center the grid on y
}
This is the Tiles Class :
package {
import flash.display.MovieClip;
import flash.events.*;
public class Tiles extends MovieClip {
private var rand:int;
public function Tiles() {
// constructor code
getTiles();
}
public function getTiles():void {
random();
setColor();
}
private function random() : void{
rand = Math.floor(Math.random()*100)+1;
}
private function setColor() : void{
if(rand<=30){
gotoAndStop(8); //red frame
}else{
gotoAndStop(7); //white frame
}
}
}
}
createGrid() place the tiles as soon as the map is placed on the stage and stock every tiles in the MovieClip grid. Now, I want the tiles to change randomly between red and white on the beat of a streamed music (and keep the ratio of 30% red tiles and 70% white tiles)
var s: Sound = new Sound();
var sc: SoundChannel;
s.load(new URLRequest("GameSong_mixdown.mp3"));
sc = s.play(0, 1000);
I know i need the leftpeek properties of my soundchannel to achieve that but,for now, I do my test with a button that trigger this function:
private function setTiles(e: Event): void {
// loop through all child element of a movieclip
for (var i: int = 0; i < grid.numChildren; i++) {
grid.getChildAt(i).getTiles();
}
}
Right now, the problem is : I'm unable to acces my Tiles method. I did a trace on grid,getChildAt(i), and saw all instances of my tiles in the console. So, i know for sure that every instances of my tiles are stored in grid. But, I don't know why, grid.getChildAt(i).getTiles(); doesn't work (and every other method from Tiles). The error message is: Call to a possibly udefined method getTiles through a reference with static type flash.display:DisplayObject
Does someone know what i'm doing wrong ?
ps: I translated all my class name, var name, etc from french to
english to make the code clearer.
Your mistake is that getChildAt(...) method has a return type of DisplayObject which is neither dynamic (will not let you access random properties) nor it have DisplayObject.getTiles() method.
All you need is to tell the program that this object is actually of Tiles class:
private function setTiles(e:Event):void
{
// loop through all child element of a movieclip
for (var i: int = 0; i < grid.numChildren; i++)
{
// Cast display objects to Tiles class.
var aTiles:Tiles = grid.getChildAt(i) as Tiles;
// Call the method.
aTiles.getTiles();
}
}

Easy way to make a lot of buttons share a function? AS3

I am trying to do a simple light-up pegboard in flash. I have finished the general logic for 1 peg but there will be a total of 2,300 pegs and I don't want to have to add an event listener to each movieclip.
Here is my code:
import flash.events.Event;
var my_color:ColorTransform = new ColorTransform();
movieClip_1.addEventListener(MouseEvent.MOUSE_UP, fl_MouseClickHandler);
function fl_MouseClickHandler(event:MouseEvent):void
{
if (my_color.color == 0)
{
my_color.color = 0x0000FF;
event.target.transform.colorTransform = my_color;
}
else if (my_color.color == 255)
{
my_color.color = 0x00FF00;
event.target.transform.colorTransform = my_color;
}
else if (my_color.color == 65280)
{
my_color.color = 0xFF0000;
event.target.transform.colorTransform = my_color;
}
else if (my_color.color == 16711680)
{
my_color.color = 0xFFFFFF;
event.target.transform.colorTransform = my_color;
}
else if (my_color.color == 16777215)
{
my_color.color = 0x000000;
event.target.transform.colorTransform = my_color;
}
else
{
trace(my_color.color);
}
}
[
Here are 3 ways to accomplish this:
Put the code on the peg's own timeline. (or make a class file, and attach it to your peg object). This will re-use the same code for each peg instance automatically. Just take the same code you have, but use the this keyword instead of a hard reference to the movie clip:
var my_color:ColorTransform = new ColorTransform();
this.addEventListener(MouseEvent.MOUSE_UP, fl_MouseClickHandler);
function fl_MouseClickHandler(event:MouseEvent):void
{
//.....
Make a container Sprite/MovieClip and have all the pegs be the sole children of it. Then iterate over all the children of that container and attach the listener:
//loop through all children of the container and add an event listener
var i:int = container.numChildren;
while(i--){
container.getChildAt(i).addEventListener(....);
}
This is good because you don't have to give them instance names, which would be quite tedious.
Attach a click listener to a common parent of all pegs, and use the target property of the event to see if the click was on a peg.
Assuming you have right-clicked your peg library object, gone to properties and checked "export for actionscript" and given it the Class name "MyPeg", you could do this:
commonParent.addEventListener(MouseEvent.CLICK, parentClick);
function parentClick(e:Event):void {
if(e.target is MyPeg){
//it's a PEG, do something
}
}
Now, depending on how your peg object is structured, target could also refer to a child of your peg (instead of the peg itself). To avoid this if it's applicable, you can disable mouse input on the children of the peg. So on the first frame of your peg object, you could this: this.mouseChildren = false;
Now, even better (less tedious) would be to instantiate your pegs through code too. So as mentioned earlier, export your peg for actionscript in it's properties, and give it a class name ("MyPeg" for my example). Then something along these lines:
var curRow:int = 0;
var curCol:int = 0;
var totalRows:int = 25;
var totalCols:int = 92;
var startingY:int = 10;
var startingX:int = 10;
var padding:int = 2; //gap between pegs
var curPeg:MyPeg;
while(true){
//create the peg, and add it to the display.
curPeg = new MyPeg();
addChild(curPeg);
//add the click listener to this peg
curPeg.addEventListener(MouseEvent.CLICK, fl_mouseClickHandler);
//assign the position of this peg
curPeg.x = startingX + (curCol * (curPeg.width + padding));
curPeg.y = startingY + (curRow * (curPeg.height + padding));
//increment the column
curCol++;
//check if we've reached the last column in the row
if(curCol >= totalCols - 1){
//yes, so reset the column to 0 and increment the row
curCol = 0;
curRow++;
//break out of the loop if the curRow exceeds or is equal to the total rows var
if(curRow >= totalRows) break;
}
}
This way you could change your grid size simply by modifying the number assigned to totalCols and totalRows - no need to tediously move around 2300 objects in FlashPro.
One way to do it is loop through all the children of the parent of your 2300 pegs.
for (var i:int=0; i<numChildren; i++) {
var clip = getChildAt(i);
if (clip.name.indexOf('movieClip_')==0) {
clip.addEventListener((MouseEvent.MOUSE_UP, fl_MouseClickHandler);
}
}
Another way to do it is to add a handler to the entire parent clip and then in the handler check and see if what was clicked is one of your pegs. But you have to disable mouseChildren on the child clips for that to work.
Note that you may want to look at replacing that big if/then statement with switch/case, which is clearer and more compact in this type of situation.

HitTest for objects not yet on Stage

I need to add a MovieClip to stage, the limitation being that it should only be added to an empty area on the stage. The stage itself either contains complex shapes or is manipulable by the user i.e. he can drag/move objects to change the empty area. The hitTest and hitTestObject methods need DisplayObject already available on the stage. What is the right way to go - the only solution I can imagine is having added my object on the stage and then repeatedly doing hit tests?
[Imagine it to something like adding sprites in a video game - they must spawn in empty regions; if they pop out from inside of each other, then it'll look really odd.]
Well, when you create a new class, just turn it off with a variable and set the visibility to false, then loop until there is no hitTest.
A silly example:
public class someClass extends Sprite
{
private var objectsOnStage:Array;
public function someClass(objectsArray:Array) {
objectsOnStage = objectsArray;
visible = false;
addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event){
removeEventListener(Event.ADDED_TO_STAGE, init);
addEventListener(Event.ENTER_FRAME, SEARCH);
}
private function SEARCH(e:Event) {
var doesHit:Boolean = false;
x = Math.round(Math.random() * (550 - 0)) + 0;
y = Math.round(Math.random() * (400 - 0)) + 0;
for (var i:int = 0; i < objectsOnStage; i++) {
if (doesHit) break;
if (this.hitTestObject(objectsOnStage[i])) {
doesHit = true;
}
}
if (doesHit) return;
placedInit();
}
private function placedInit() {
visible = true;
removeEventListener(Event.ENTER_FRAME, SEARCH);
//now init the stuff you want.
}
}
You just check if bounding boxes of both clips overlaps. Like this:
import flash.geom.Rectangle;
import flash.display.MovieClip;
// create simple movie clips that has a rectangle shape inside
var sym1 : MovieClip = new Sym1();
var sym2 : MovieClip = new Sym2();
// get a rectanle of both clipt
var boundingBox1 : Rectangle = sym1.getBounds(this);
var boundingBox2 : Rectangle = sym2.getBounds(this);
// check if bounding boxes of both movie clips overlaps
// so it works like hitTestObject() method
trace( boundingBox1.intersects( boundingBox2) )
I know this post is super old, but in case it helps anybody --
If you need to do a hit test on a movieclip that isn't on the stage. A workaround is to rasterize it to a bitmap first.
var bitmapData:BitmapData = new BitmapData(mc.width, mc.height, true, 0x0000000);
bitmapData.draw(mc);
if (bitmapData.getPixel32(x, y) > 0) {
// Hit true.
}

AS 3.0 Dynamic Instance Names

Hi i made a custom class where i would like to create x instances of a movieclip. But the following doesn't work:
package {
import flash.display.MovieClip;
public class CustomClass extends MovieClip {
public function CustomClass(amount:uint) {
var Collector:Array = new Array();
//Add and position Tiles to stage.
for (var i:uint = 1; i <= amount; i++){
var newMovieClip:MovieClip = new MovieClip;
newMovieClip.y = amount * 10;
Collector.push(newMovieClip);
}
addChild(Collector);
}
}
}
I would like to position them on the timeline with
var customClass_mc:CustomClass = new CustomClass(10);
addChild(customClass_mc);
//try to trace the x position of one of the instances.
trace(customClass_mc.Collector[5].x);
I keep getting the error: Scene 1, Layer 'Layer 1', Frame 1, Line 5 1119: Access of possibly undefined property Collector through a reference with static type CustomClass.
Firstly, you need to declare Collector as public:
public var Collector:Array = new Array();
Your Collector is an array, not a display object, and so it can't be added to the display tree. Instead you would push each newMovieClip onto the display of Custom class and position them inside your for loop. Then you don't need the collector at all, because you can target the movieclips using getChildAt():
trace(customClass_mc.getChildAt(5).x);
I found another answer myself which i think is even better!
You don't need the container at all.
when you use the following
package {
import flash.display.MovieClip;
public class CustomClass extends MovieClip {
public function CustomClass(amount:uint) {
//Add and position Tiles to stage.
for (var i:uint = 1; i <= amount; i++){
var newMovieClip:MovieClip = new MovieClip;
newMovieClip.y = amount * 10;
newMovieClip.name = "clip"+i;
addChild(newMovieClip);
}
}
}
}
No i can acces the movieclips by using:
var customClass_mc:CustomClass = new CustomClass(10);
addChild(customClass_mc);
//try to trace the x position of the fifth instance.
trace(customClass_mc.getChildByName("child5").y);
The variable 'Collector' is only available inside the constructor the way you have it. Collector has to be made public to be accessible from outside the timeline. The best thing to do would be to make a public getter method to access this. So something like:
import flash.display.MovieClip;
public class CustomClass extends MovieClip {
private var Collector:Array = new Array();
public function get Collector():Array
{
return Collector;
}
public function CustomClass(amount:uint) {
//Add and position Tiles to stage.
for (var i:uint = 1; i <= amount; i++){
var newMovieClip:MovieClip = new MovieClip;
newMovieClip.y = amount * 10;
Collector.push(newMovieClip);
}
addChild(Collector);
}
}

Multiple movieclips all go to the same spot; What am i doing wrong?

So I'm trying to shoot multiple bullets out of my body and it all works except I have an odd problem of just one bullet showing up and updating to set position for the new ones.
I have a move able player thats supposed to shoot and I test this code by moving the player and shooting. Im taking it step by step in creating this.
The result of tracing the bulletContainer counts correctly in that its telling me that movieclips ARE being added to the stage; I Just know it comes down to some kind of logic that im forgetting.
Here's My Code (The Bullet it self is a class)
UPDATE*
Everything in this code works fine except for I stated earlier some code seems reduntned because I've resorted to a different approaches.
BulletGod Class:
public class bulletGod extends MovieClip{
//Register Variables
//~Global
var globalPath = "http://127.0.0.1/fleshvirusv3/serverside/"
//~MovieCLips
var newBullet:bulletClass = new bulletClass();
//~Boolean
var loadingBulletInProgress:Number = 0;
var shootingWeapon:Number = 0;
//~Timers
var fireBulletsInterval = setInterval(fireBullets, 1);
var bulletFireEvent;
//~Arrays
var bulletArray:Array = new Array();
var bulletType:Array = new Array();
var bulletContainer:Array = new Array();
//~Networking
var netBulletRequest:URLRequest = new URLRequest(globalPath+"bullets.php");
var netBulletVariables:URLVariables = new URLVariables();
var netBulletLoader:URLLoader = new URLLoader();
//~Bullet Image Loader
var mLoader:Loader = new Loader();
var mRequest:URLRequest = new URLRequest();
public function bulletGod() {
//Load every bullet for every gun
//Compile data to be requested
netBulletVariables.act = "loadBullets"
netBulletRequest.method = URLRequestMethod.POST
netBulletRequest.data = netBulletVariables;
netBulletLoader.dataFormat = URLLoaderDataFormat.VARIABLES;
netBulletLoader.addEventListener(Event.COMPLETE, getBulletImages);
netBulletLoader.load(netBulletRequest);
}
private function getBulletImages(bulletImageData:Event){
//Request every bullet URL image
//Set vars
var bulletData = bulletImageData.target.data;
//Load images
for(var i:Number = 0; i < bulletData.numBullets; i++){
bulletArray.push(bulletData["id"+i.toString()]);
bulletType.push(bulletData["bullet"+i.toString()]);
//trace(bulletData["id"+i]+"-"+bulletData["bullet"+i]);
}
//All the arrays have been set start firing the image loader/replacer
var imageLoaderInterval = setInterval(imageReplacer, 10);
}
private function imageReplacer(){
//Check to see which image needs replacing
if(!loadingBulletInProgress){
//Begin loading the next image
//Search for the next "String" in the bulletType:Array, and replace it with an image
for(var i:Number = 0; i < bulletType.length; i++){
if(getQualifiedClassName(bulletType[i]) == "String"){
//Load this image
mRequest = new URLRequest(globalPath+"ammo/"+bulletType[i]);
mLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadImage);
mLoader.load(mRequest);
//Stop imageReplacer() while we load image
loadingBulletInProgress = 1;
//Stop this for() loop while we load image
i = 999;
}
}
}
}
private function loadImage(BlackHole:Event){
//Image has loaded; find which array slot it needs to go into
for(var i:Number = 0; i <= bulletType.length; i++){
if(getQualifiedClassName(bulletType[i]) == "String"){
//We found which array type it belongs to; now replace the text/url location with the actual image data
var tmpNewBullet:MovieClip = new MovieClip;
tmpNewBullet.addChild(mLoader);
//Add image to array
bulletType[i] = tmpNewBullet;
//Restart loadingBullets if there are more left
loadingBulletInProgress = 0;
//Stop for() loop
i = 999;
}
}
}
//###############################################################################################################################################
private function fireBullets(){
//If player is holding down mouse; Fire weapon at rate of fire.
if(shootingWeapon >= 1){
if(bulletFireEvent == null){
//Start shooting bullets
bulletFireEvent = setInterval(allowShooting, 500);
}
}
if(shootingWeapon == 0){
//The user is not shooting so stop all bullets from firing
if(bulletFireEvent != null){
//Strop firing bullets
clearInterval(bulletFireEvent);
bulletFireEvent = null
}
}
}
private function allowShooting(){
//This function actually adds the bullets on screen
//Search for correct bullet/ammo image to attach
var bulletId:Number = 0;
for(var i:Number = 0; i < bulletArray.length; i++){
if(bulletArray[i] == shootingWeapon){
//Bullet found
bulletId = i;
//End For() loop
i = 999;
}
}
//Create new bullet
//Create Tmp Bullet
var tmpBulletId:MovieClip = new MovieClip
tmpBulletId.addChild(newBullet);
tmpBulletId.addChild(bulletType[bulletId]);
//Add To Stage
addChild(tmpBulletId)
bulletContainer.push(tmpBulletId); //Add to array of bullets
//Orientate this bullet from players body
var bulletTmpId:Number = bulletContainer.length
bulletTmpId--;
bulletContainer[bulletTmpId].x = Object(root).localSurvivor.x
bulletContainer[bulletTmpId].y = Object(root).localSurvivor.y
//addChild(bulletContainer[bulletTmpId]);
}
//_______________EXTERNAL EVENTS_______________________
public function fireBullet(weaponId:Number){
shootingWeapon = weaponId;
}
public function stopFireBullets(){
shootingWeapon = 0;
}
}
}
BulletClass:
package com{
import flash.display.*
import flash.utils.*
import flash.net.*
import flash.events.*
public class bulletClass extends MovieClip {
public var damage:Number = 0;
public function bulletClass() {
//SOME MOVEMENT CODE HERE
}
public function addAvatar(Obj:MovieClip){
this.addChild(Obj);
}
}
}
Well ... if I may say so, this code looks quite wrong. Either something is missing from the code or this code will never make the bullets fly.
First off, you can set x and y of the new bullet directly (replace everything after "orientate this bullet from players body" with this):
tmpBulletId.x = Object(root).localSurvivor.x;
tmpBulletId.y = Object(root).localSurvivor.y;
Perhaps this already helps, but your code there should already do the same.
But to let these bullets fly into any direction, you also need to add an event listener, like so:
tmpBulletId.addEventListener(Event.ENTER_FRAME, moveBullet);
function moveBullet(e:Event) {
var movedBullet:MovieClip = MovieClip(e.currentTarget);
if (movedBullet.x < 0 || movedBullet.x > movedBullet.stage.width ||
movedBullet.y < 0 || movedBullet.y > movedBullet.stage.height) {
// remove move listener, because the bullet moved out of stage
movedBullet.removeEventListener(Event.ENTER_FRAME);
}
// remove the comment (the //) from the line that you need
MovieClip(e.currentTarget).x += 1; // move right
// MovieClip(e.currentTarget).y -= 1; // move up
// MovieClip(e.currentTarget).x -= 1; // move left
// MovieClip(e.currentTarget).y += 1; // move down
}
This example lets your bullet fly to the right. If you need it flying into another direction, just comment out the line with the "move right" comment and uncomment one of the other lines.
This is of course a very simple example, but it should get you started.
I hope this helps, and that my answer is not the wrong answer to the question.
As far as I have expirienced it you can have only one copy of MovieClip object added to specific child. Best approach is to use ByteArray for the clip source and instantiate new MovieClip and pass the ByteArray as a source. It have something to do with child/parent relation since a DisplayObject can have only one parent (and a way to detach the object from scene too).
Well i ended up writeing the whole code from scratch for a 3rd time and ran into a similar problem and just for reference to anybody else that comes to a problem thats random as this one i found that problem was likly does to a conversion error somewhere that doesn't necessarily break any compiling rules. Just that i was calling a movieclip and not the class it self.