PDA

View Full Version : Help removingChild IF child has been added!


rogueblade
02-22-2009, 07:16 AM
- So I have 4 functions called by their own buttons.
Each function calls a movie clip from the library with addChild.
- When I click on one of these 4 functions I want it to remove the added children if any of the other functions had been clicked prior.
- Problem is, removeChild at the start of each function results in errors if a child had never been called yet. It's trying to remove something that isn't there.
- So I tried making a 'flag:number = 0' for each of the 4 functions. Each function begins with settings its flag to '1'
- I put a for loop at the beginning of each function saying IF any of the flags are = to 1, then remove the children.
- I don't see why that should not work, I think the error lies in my array or my for loop if statement as I still don't fully understand them.
So I will just post the code relating to the array and loop:

var flagH:Number = 0;
var flagLi:Number = 0;
var flagBe:Number = 0;
var flagBr:Number = 0;
var flagArray:Array = new Array(flagH, flagLi, flagBe, flagBr);

//hydrogenOn is the first of four each one essentially doing the same thing
function hydrogenOn(event:MouseEvent):void {
flagH = 1;
for (var i:int = 0; i<flagArray.length; i++) {
if (flagArray[i].Number==1) { //<-- this cant be right
table_mc.removeChild(lithium_mc); //<--each of these mc's flag=1 when called
table_mc.removeChild(beryllium_mc);
table_mc.removeChild(bromine_mc);
}
}
table_mc.addChild(hydrogen_mc);
// and the rest of the function.....

panel
02-22-2009, 08:08 AM
You could write simple function to validate if specified object is child


function isChildOf(displayObjectContainer:displayObjectCont ainer, displayObject:DisplayObject):Boolean
{
//using for lop and numChildern property you can loop
//through all childrens of displayObjectContaiiner
}

rogueblade
02-22-2009, 09:06 AM
Sorry man but I dont really understand whats going on there, it's a a bit beyond me.... displayObjectContainer is that supposed to be my movieclips in there that I want to remove? Can it be put in more detail?
And I'm kind of wondering if my function is fine and if just this part is wrong:
for (var i:int = 0; i<flagArray.length; i++) {
if (flagArray[i].Number==1) {

btw this is the error report:

Error #1069: Property Number not found on Number and there is no default value.
at PhoneApp_fla::MainTimeline/hydrogenOn()

panel
02-22-2009, 01:09 PM
//function
function isChildOf(displayObjectContainer:displayObjectCont ainer, displayObject:DisplayObject):Boolean
{
for(var i:uint = 0; i < displayObjectContainer.numChildern; i++)
{
if(displayObjectContainer.getChildAt(i) == displayObject)
return true;

}

return false;
}

//usage
trace(isChildOf(displayObjectContainerWihSomeChild erns, childrenYouWantToDelete));

displayObjectContainer is movieclip with your childrens

Mazoonist
02-22-2009, 01:38 PM
Should be:
if (flagArray[i]==1)

rogueblade
02-22-2009, 07:49 PM
Ok this is what I have now. Here's all the code relating to just these two functions.
.swf returns NO errors yet the for loop & if statement still wont do anything.


var flagH:Number = 0;
var flagLi:Number = 0;
var flagArray:Array = new Array(flagH, flagLi);

table_mc.otherNonMetals_mc.H_btn.addEventListener( MouseEvent.CLICK, hydrogenOn);
table_mc.alkaliMetals_mc.Li_btn.addEventListener(M ouseEvent.CLICK, lithiumOn);
hydrogen_mc.addEventListener(MouseEvent.CLICK, hydrogenOff);
lithium_mc.addEventListener(MouseEvent.CLICK, lithiumOff);



function hydrogenOn(event:MouseEvent):void {
flagH = 1;
for (var i:int = 0; i<flagArray.length; i++) {
if (flagArray[i]==1) {
table_mc.removeChild(lithium_mc); //<--no matter what I put here, it never executes
flagArray[i]==0;
}
}
table_mc.addChild(hydrogen_mc);
var TweenX:Tween = new Tween(hydrogen_mc,"scaleX",None.easeNone, 0,1,.3,true);
var TweenY:Tween = new Tween(hydrogen_mc,"scaleY",None.easeNone, 0,1,.3,true);
}

function hydrogenOff(event:MouseEvent):void {
var TweenX:Tween = new Tween(hydrogen_mc,"scaleX",None.easeNone, 1,0,.3,true);
var TweenY:Tween = new Tween(hydrogen_mc,"scaleY",None.easeNone, 1,0,.3,true);
TweenY.addEventListener(TweenEvent.MOTION_FINISH, doneH);
}

function doneH(event:TweenEvent):void {
flagH = 0;
table_mc.removeChild(hydrogen_mc);
}

///////////

function lithiumOn(event:MouseEvent):void {
flagLi = 1;
for (var i:int = 0; i<flagArray.length; i++) {
if (flagArray[i]==1) {
table_mc.removeChild(hydrogen_mc);
flagArray[i]==0;
}
}
table_mc.addChild(lithium_mc);
var TweenX:Tween = new Tween(lithium_mc,"scaleX",None.easeNone, 0,1,.3,true);
var TweenY:Tween = new Tween(lithium_mc,"scaleY",None.easeNone, 0,1,.3,true);
}

function lithiumOff(event:MouseEvent):void {
var TweenX:Tween = new Tween(lithium_mc,"scaleX",None.easeNone, 1,0,.3,true);
var TweenY:Tween = new Tween(lithium_mc,"scaleY",None.easeNone, 1,0,.3,true);
TweenY.addEventListener(TweenEvent.MOTION_FINISH, doneLi);
}

function doneLi(event:TweenEvent):void {
flagLi = 0;
table_mc.removeChild(lithium_mc);
}


Mazoonist I changed what you said and now get no errors when running the swf yet my if statement in the for loop does nothing despite what different things I try telling it to do....I'm hoping that is all I need to fix to have this all work!

Now panel, I've tried playing around with that code you gave me but I don't understand how to use displayObjectContainer and how it compares to displayObject. I don't know which of those I'm supposed to rename with my hydrogen_mc or lithium_mc. I kind of get the idea of what the function is doing but not well enough to implement it myself.

Mazoonist
02-22-2009, 08:15 PM
var flagH:Number = 0;
var flagLi:Number = 0;
var flagArray:Array = new Array(flagH, flagLi);
trace(flagArray); //traces 0, 0
flagLi = 1;
trace(flagArray); //traces 0, 0

As you can see from the above, the numbers in your array are stored by value, not reference. When you change the value of flagLi, the corresponding array value is not altered like you think it ought to be. Objects are stored in an array by reference, but not the primitive types, of which Number is one.

Edit: [Store by reference would mean that both flagLi and flagArray[0] were pointing at the same object. Store by value means that another copy of the variable's value is made and that is what is stored in the array.]

Change the value of the array slot instead of altering the original variable, and it will work. Instead of:
flagH = 1;
do this:
flagArray[0] = 1;
One other thing you're no doubt not aware of: you need to declare your tweens outside of the functions, then instantiate them in the function that's going to use them. Otherwise the garbage collector can delete your variables before the tweens have a chance to complete. It's a known issue with AS3 (it wasn't an issue in AS2).

var tweenX:Tween;
var tweenY:Tween;
function hydrogenOff(event:MouseEvent):void {
tweenX = new Tween(hydrogen_mc,"scaleX",None.easeNone, 1,0,.3,true);
tweenY= new Tween(hydrogen_mc,"scaleY",None.easeNone, 1,0,.3,true);
tweenY.addEventListener(TweenEvent.MOTION_FINISH, doneH);
}

Also, it's a bad idea to defy convention. Variable names and function names should begin with a lowercase letter, to distinguish them from classes, which should always begin with an uppercase letter. It's merely a convention, and your code will still work if you go against it, but there are good solid reasons for it.

rogueblade
02-22-2009, 08:44 PM
awsome, great points Mazoonist. The array issue totally makes sense now. I know I should be declaring my tweens outside the function, I did not in this case as I'm just trying to make a functioning function. Will change it though. Also I do know of naming conventions but as this is the first time I've used a class, I didn't know classes had a naming convention!

Now, there's still what I'm guessing is a small problem, after doing what you said (picture the code above with the flags fixed)I get this error:

ArgumentError: Error #2025: The supplied DisplayObject must be a child of the caller.
at flash.display:: DisplayObjectContainer/removeChild()
at PhoneApp_fla::MainTimeline/hydrogenOn()

Soo I think something is still wrong with the way I'm trying to remove the child which is where panel's idea came into play?

See if I do this for each function it works:

function hydrogenOn(event:MouseEvent):void {
flagArray[0] = 1;
for (var i:int = 0; i<flagArray.length; i++) {
if (flagArray[1]==1) {
table_mc.removeChild(Lithium_mc);
flagArray[1]=0;
}
}
as compared to:

function hydrogenOn(event:MouseEvent):void {
flagArray[0] = 1;
for (var i:int = 0; i<flagArray.length; i++) {
if (flagArray[i]==1) {
table_mc.removeChild(Lithium_mc);
flagArray[i]=0;
}
}
which does not work

Mazoonist
02-22-2009, 08:45 PM
I should also say that panel's solution would also work for you. To implement it, just paste the function into your code (anywhere except inside another function, make it a function of its own):
function isChildOf(displayObjectContainer:displayObjectCont ainer, displayObject:DisplayObject):Boolean
{
for(var i:uint = 0; i < displayObjectContainer.numChildren; i++)
{
if(displayObjectContainer.getChildAt(i) == displayObject)
return true;

}

return false;
}
(small typo, "numChildern" corrected in the above)
Then, at that point in your code where you are testing to see if your lithium_mc object is on the display list of table_mc:
if (isChildOf(table_mc, lithium_mc)) {
table_mc.removeChild(lithium_mc);
}
See, panel is a good programmer, and as such, he is trying to provide you with a general purpose function that you can use over and over. You could use the above and then you wouldn't even need your array.

Mazoonist
02-22-2009, 08:51 PM
You get the error you mentioned when you attempt to remove a child and it wasn't really a child. So, yeah, there must be something wrong with your logic. If you would care to upload your file, I would give it a once-over for you.

rogueblade
02-22-2009, 09:04 PM
Ok, I'm trying this method but the first line of this isChildOf function results in:

1046: Type was not found or was not a compile-time constant: displayObjectContainer.

Is there something I need to import? import fl.something?

But I'm still also wondering why the original method with the flagArray is not working...

Mazoonist
02-22-2009, 09:20 PM
Is all of your code in a class? If so, then yes, you need to import any classes that aren't in the current package:

Try this:
import flash.display.*;

rogueblade
02-22-2009, 09:22 PM
Here's my file

Ignore the missing fonts

ALL RELEVANT code starts line 559 ends 647

file 100kb too big to upload here, so:
http://rapidshare.com/files/201328464/PhoneApp.zip.html

Mazoonist
02-22-2009, 10:15 PM
You're setting the flag too soon. You're setting the flag right away, then running through the loop testing for the value. Naturally you're going to find it, because you just set it.

Don't set your flag until after you do the addChild.

Mazoonist
02-22-2009, 10:19 PM
Here's the lithiumOn function, with this change implemented:
function lithiumOn(event:MouseEvent):void {
//don't set the flag here, because the following loop will catch it
for (var i:int = 0; i<flagArray.length; i++) {
if (flagArray[i]==1) {
table_mc.removeChild(Hydrogen_mc);
flagArray[i]==0;
}
}
table_mc.addChild(Lithium_mc);
//set the flag here, right after addChild
flagArray[1] = 1;
var TweenX:Tween = new Tween(Lithium_mc,"scaleX",None.easeNone, 0,1,.3,true);
var TweenY:Tween = new Tween(Lithium_mc,"scaleY",None.easeNone, 0,1,.3,true);
Lithium_mc.x = -181;
Lithium_mc.y = -100;
Lithium_mc.buttonMode = true;
table_mc.alkaliMetals_mc.Li_btn.doubleClickEnabled = false;
}

rogueblade
02-23-2009, 12:21 AM
Ah thank you Maz, that was pretty obvious I should have been able to realize that myself...but now being the idiot I am didn't consider I was just testing this with two classes. Once I include the third one and onwards the function becomes useless. Original:
for (var i:int = 0; i<flagArray.length; i++) {
if (flagArray==1) {
table_mc.removeChild(Lithium_mc);
flagArray[i]=0;
}
}

So I tried the below but now realize that this cannot work because it is saying to [I]remove these children if any flag =1 when I need it to remove all the flags that = 1
for (var i:int = 0; i<flagArray.length; i++) {
if (flagArray[i]==1) {
table_mc.removeChild(Lithium_mc);
table_mc.removeChild(Bromine_mc);
table_mc.removeChild(Hydrogen_mc);
flagArray[i]=0;
}
}

So I tried below but it did not work (putting Classes inside an array?):
var classmcArray:Array = new Array(Hydrogen_mc, Lithium_mc, Beryllium_mc, Bromine_mc);

for (var i:int = 0; i<flagArray.length; i++) {
if (flagArray[i]==1) {
for (var j:int = 0; j<classmcArray.length; j++) {
table_mc.removeChild(classmcArray[j]);
}
flagArray[i]=0;
}
}

So now I realize I pretty much have to do what panel said. I understand the if statement but not the function. I think I could just use the if statement like so:
if (p.contains(c)){
p.removeChild(c);
}

This will safely remove c if it is a child of p, and do nothing if it isn't.
But once again I need value 'c' to be my array that holds my 4 classes var classmcArray:Array = new Array(Hydrogen_mc, Lithium_mc, Beryllium_mc, Bromine_mc);


So ultimately I want this...yet it did not work. But some variation of this Must be able to do it!:

for (var i:int = 0; i<classmcArray.length; i++) {
if (table_mc.contains(classmcArray[i])){
table_mc.removeChild(classmcArray[i]);
}
}
table_mc.addChild(Hydrogen_mc);
//rest of function

Error #2007: Parameter child must be non-null.

Mazoonist
02-23-2009, 12:48 AM
There's a certain technique I use a lot whereby you just create a general purpose variable of the MovieClip type, which I usually call currentClip. The idea is that once you make this variable, your other movie clips can take turns "being" the currentClip. So, in your application, let's say the Lithium button is clicked. The lithium_mc clip is added to the display list (with addChild) and animates onto the screen. At that point in the code, an assignment statement like this would appear:
currentClip = lithium_mc;
which would set the currentClip variable equal to lithium_mc.

Later, when the Hydrogen button is clicked, we need not worry about figuring out which clip we need to remove from the screen, we just do this:
removeChild(currentClip);
Then add the hydrogen_mc clip to the display, animate it to the center of the screen, then set it to be the currentClip. And so on.

rogueblade
02-23-2009, 01:03 AM
Ok that sounds good, but that means at the start of each function for each movieclip im going to have removeChild(currentClip);
Which means I would get that error because the first time any one is clicked there will be no child to remove:
Error #2007: Parameter child must be non-null.

What is your work around for that?

Mazoonist
02-23-2009, 01:22 AM
It's not really a workaround. When you first declare such a variable, you don't instantiate it, it's just:
var currentClip:MovieClip;
So, of course it's value is null. So you just test for that. For example, here's how the first few lines of the hydrogenOn function might appear:
if(currentClip != null) {
removeChild(currentClip);
}
currentClip = Hydrogen_mc;

This allows for the fact that the first time something is clicked on, currentClip won't have a value yet. From then on, it will, though. And from that point on in the function, you can reference currentClip instead of Hydrogen_mc. You can set the x and y and tween it using that reference instead.

In fact, all your listeners in this section are doing essentially the same thing, and all that code could be written into just one listener that they could all use. I wonder if you'd be interested in knowing how?

rogueblade
02-23-2009, 01:43 AM
Dam you're a tease, I would be veryyyy interested to know :D
I have the vaguest idea of how but not really......

I now have this at the start of each function with the currentClip after the addChild and it works :) Just have now make a loop that re-enables the double clip for the buttons as you may have seen each function disables the double click.
function hydrogenOn(event:MouseEvent):void {
if(currentClip != null) {
table_mc.removeChild(currentClip);
for (var i:int = 0; i<allArray.length; i++) {
allArray[i].doubleClickEnabled = true; //<--now fully functional
}
}
table_mc.addChild(Hydrogen_mc);
currentClip = Hydrogen_mc;
flagArray[0] = 1;
var TweenX:Tween = new Tween(Hydrogen_mc,"scaleX",None.easeNone, 0,1,.3,true);
var TweenY:Tween = new Tween(Hydrogen_mc,"scaleY",None.easeNone, 0,1,.3,true);
Hydrogen_mc.x = -181;
Hydrogen_mc.y = -100;
Hydrogen_mc.buttonMode = true;
table_mc.otherNonMetals_mc.H_btn.doubleClickEnable d = false;
}

Mazoonist
02-23-2009, 03:25 AM
Well, that took a little while, but here you go:
var currentClip:MovieClip;
var tweenX:Tween;
var tweenY:Tween;

var Hydrogen_mc:hydrogen = new hydrogen();
var Lithium_mc:lithium = new lithium();
// ^^ instantiate more clips here ^^

var buttonArray:Array = [table_mc.otherNonMetals_mc.H_btn,
table_mc.alkaliMetals_mc.Li_btn];
var clipArray:Array = [Hydrogen_mc,
Lithium_mc];
// ^^ parallel arrays, one for buttons and one for the associated mcs ^^

for(var i:int = 0; i < buttonArray.length; i++) {
buttonArray[i].addEventListener(MouseEvent.DOUBLE_CLICK, itemOn);
buttonArray[i].doubleClickEnabled = true;
}
for(var j:int = 0; j < clipArray.length; j++) {
clipArray[j].addEventListener(MouseEvent.DOUBLE_CLICK, itemOff);
clipArray[j].doubleClickEnabled = true;
clipArray[j].buttonMode = true;
clipArray[j].x = -181;
clipArray[j].y = -100;
}

function itemOn(event:MouseEvent):void {
enableAllButtons();
if(currentClip != null) {
table_mc.removeChild(currentClip);
}
var clickedIndex:int = buttonArray.indexOf(event.currentTarget);
currentClip = clipArray[clickedIndex];
table_mc.addChild(currentClip);
tweenX = new Tween(currentClip,"scaleX",None.easeNone, 0,1,.3,true);
tweenY = new Tween(currentClip,"scaleY",None.easeNone, 0,1,.3,true);
event.currentTarget.doubleClickEnabled = false;
}

function itemOff(event:MouseEvent):void {
tweenX = new Tween(currentClip,"scaleX",None.easeNone, 1,0,.3,true);
tweenY = new Tween(currentClip,"scaleY",None.easeNone, 1,0,.3,true);
tweenY.addEventListener(TweenEvent.MOTION_FINISH, done);
}

function done(event:TweenEvent):void {
enableAllButtons();
}
function enableAllButtons():void {
for(var i:int = 0; i < buttonArray.length; i++) {
buttonArray[i].doubleClickEnabled = true;
}
}
To add to this system, you would instantiate more mc's, adding their reference to the clipArray, and adding the button you want to associate each one with to the buttonArray. These two parallel arrays should always have the same number of items. Each button listed is thus associated with each clip listed in the other array.

What's cool is that you can add more buttons and clips without touching any of the other code (unless you want to).

rogueblade
02-23-2009, 04:56 AM
my god Maz that is amazing! It's so much cleaner and it makes So much sense. I love it.
Just to make sure I understand correctly
var clickedIndex:int = buttonArray.indexOf(event.currentTarget);
currentClip = clipArray[clickedIndex];

That is telling clipArray to select its item based on the same numeric position of the item from buttonArray and assign it to currentClip?
I'm pretty sure I get it.
I also see now how I can use the way you wrote this for the rest of code, it'll clean up real nice with your methods.

I owe you huge, thanks a million

Mazoonist
02-23-2009, 05:21 AM
You got it! event.currentTarget gets whatever Button was clicked on. buttonArray.indexOf() finds its position in the buttonArray. Then, yes, the same index is used to assign the corresponding MovieClip instance (from the clipArray) to currentClip.

Those two lines:
var clickedIndex:int = buttonArray.indexOf(event.currentTarget);
currentClip = clipArray[clickedIndex];
could actually be condensed into one without the need for a clickedIndex variable:
currentClip = clipArray[buttonArray.indexOf(event.currentTarget)];
But the former does the same thing and seems easier to read.

I'm glad you liked it. Your application looks great, so I guess it's a nice fit. :)

rogueblade
02-23-2009, 07:43 PM
Wow can't believe I'm bringing this back to life but I've got a new question for you Maz (and everyone else who may know)

Lets take the parallel array

var buttonArray:Array = [table_mc.otherNonMetals_mc.H_btn, table_mc.alkaliMetals_mc.Li_btn];
var clipArray:Array = [Hydrogen_mc, Lithium_mc];


So each button talks to its associated movie clip. But lets say I wanted the associated movie clip to be something else: like an associated array :o

So now I want each button to talk to its associated array.
Meaning a button click will effect everything within that array.
This means at some point, some where, I think I need a for loop to go through the associated array applying the button click event to all the associated array items.

SO....where do I need to put this loop, and how exactly to go about it?
I know this is a more open ended question that my previous ones so it may be a b*itch to answer but I hope you guys can help!

rogueblade
02-23-2009, 11:10 PM
Ok well to give a better idea of what I'm trying to do:

var currentClip2:MovieClip;

var rightMenuArray:Array = [table_mc.solid_btn, table_mc.liquid_btn, table_mc.gas_btn, table_mc.unknown_btn];
var elementmcArray:Array = [solidsArray, liquidsArray, gasesArray, unknownsArray];
//four buttons in rightMenuArray & four arrays in elementmcArray
//solidsArray, liquidsArray etc all contain a series of buttons

for (var i:int = 0; i < rightMenuArray.length; i++) {
rightMenuArray[i].addEventListener(MouseEvent.CLICK, groupOn);
}

function groupOn(event:MouseEvent):void {
var clickedIndex:int = rightMenuArray.indexOf(event.currentTarget);
for (var a:int = 0; a < elementmcArray.length; a++) {
for (var b:int = 0; b < elementmcArray[a].length; b++) {
currentClip2 = elementmcArray[a,b][clickedIndex];
}
}
//rest of function, tweens etc
So above what I'm "trying" to say is loop through my array of arrays (var a). Then loop through each array inside the array (var b). Then make currentClip2 = elementmcArray[nested array, items inside that array].
Now I know my syntax is probably totally wrong from the point where I declared the array but that is what I'm trying to achieve.


I also tried it like this:

function groupOn(event:MouseEvent):void {
var clickedIndex:int = rightMenuArray.indexOf(event.currentTarget);
for (var i:int = 0; i < elementmcArray[0].length; i++) {
currentClip2 = elementmcArray[0,i][clickedIndex];
}
var i loop through the first item in elementmcArray and then currentClip2 = elementArray[0, items within that aray]

Both of these results in a ridiculous amount of errors pertaining to:
stacking scoping getlocal pushlocal pushscope pushbyte setlocal getproperty callproperty convert_i jump initproperty inclocal_i and a few others...

I'm also wondering if instead of trying to loop through arrays of buttons inside an array, I should loop through classes of buttons inside an array.

Ok have just realized
currentClip2 = elementmcArray[a,b][clickedIndex];
and
currentClip2 = elementmcArray[0,i][clickedIndex];
come a lot closer to being functional when I use
[a+b] & [0+i] instead of commas.

now the error report is just states:
Error #1034: Type Coercion failed: cannot convert flash.display::SimpleButton@37d72e1
which I believe has something to do with my nested arrays......?

and another update. instead of [a+b] or [a,b]
currentClip2 = elementmcArray[a][b][clickedIndex];
seems like a possibility with the error:
Error #1069: Property 0 not found on flash.display.SimpleButton and there is no default value.

Mazoonist
02-23-2009, 11:45 PM
Congratulations, you managed to lose me completely. I have no idea what you are trying to accomplish, but I didn't really study the rest of your application much, so I'm guessing you're trying to make some super-system for the whole thing, which is admirable.

Anyway, you might be striving for some syntax kind of like this:
elementmcArray[0][1]

The numbers in the above are just arbitrary; the idea is to put the brackets one set right after the other.

rogueblade
02-23-2009, 11:56 PM
Ok, as simply as I can put this is as follows:

Remember the code you gave me with the parallel arrays and you said

"parallel arrays, one for buttons and one for the associated mcs"

So instead of a button click at index 1 affecting the parallel mc at index of 1

Imagine the parallel mc contains lots of items such as buttons

So I want the button click to effect the parallel item, that being all the items inside that item

Mazoonist
02-24-2009, 12:14 AM
I'm sure that could be accomplished.