removing children from an Array works... sometimes - actionscript-3

function addEffect()
{
var thisEffect = new MyEffect; //Simple movieClip
addChild(thisEffect);
effectArray[0] = thisEffect;
}
the above works fine. And later I remove it... The Below works fine also.
function removeEffect()
{
if(effectArray[0] != null)
{
removeChild(effectArray[0]);
}
}
However, after I use the functions again, somtimes the next turn, sometimes two turns later I get:
Error #2025: The supplied DisplayObject must be a child of the caller.
Oddly, I'm using the same technique to add and remove other movieclips, and it is working fine for everything else. I am not referencing the effectArray or anything inside it, outside of these two functions, which are both inside Main.as

That's quite normal.
When you add myEffect to array, only a reference to myEffect is stored in the array.
And when you try to reach that object by array, everything works correct, and reference to myEffect in array points to object correctly each time.
But what is not going right each time is whether there is an object where the reference points or not. If it has deleted, it cannot be child of the target object no more, so you'll get that error.
What you do in removeEffect function is to check if the reference is there, not the object. And reference is always there if you do not remove it from your array. After you remove the object, remove the reference also. And everything will work correct.
function removeEffect()
{
if(effectArray[0] != null)
{
removeChild(effectArray[0]);
effectArray[0] = null;
}
}

Related

MovieClip(root) line seems to be crashing my game

Inside my classes, I invoke this function
MovieClip(root).increaseScore();
which handles the score in the main .as file.
It all works fine during the execution of the level. However when the level is finished and the screen goes to another frame, the game crashes and gives me this error
TypeError: Error #1009: Cannot access a property or method of a null
object reference.
on the line above.
How do I fix this?
Thanks
edit:
This is were I tell it to addScore, this is in the GameController.as file
private function removeBubble(bubble, addScore:Boolean)
{
var delay:Timer = new Timer(200, 1);
delay.addEventListener(TimerEvent.TIMER_COMPLETE, function(e:TimerEvent)
{
if(bubble.parent==mcGameStage)
{
var j:int = bubbleList.indexOf(bubble);
bubbleList.splice(j,1);
if(addScore) bubble.addScore();
mcGameUI.txtScorePlayer.text = String(playerScore);
mcGameStage.removeChild(bubble);
}
e.currentTarget.removeEventListener(e.type, arguments.callee);
checkWin();
});
delay.start();
}
here is the checkWin function:
private function checkWin()
{
if (playerBlue + playerRed + playerYellow + playerOrange + playerPurple + playerGreen == 0)
{
gameWin();
}
}
private function gameWin()
{
while (bubbleList.numChildren > 0)
{
bubbleList.removeChildAt(0);
}
mcGameUI.btnMixBlue.removeEventListener(MouseEvent.CLICK, mixBlue);
mcGameUI.btnMixRed.removeEventListener(MouseEvent.CLICK, mixRed);
mcGameUI.btnMixYellow.removeEventListener(MouseEvent.CLICK, mixYellow);
mcGameUI.btnNeedle.removeEventListener(MouseEvent.CLICK, activateNeedle);
mcGameStage.removeEventListener(Event.ENTER_FRAME,update);
mcGameStage.removeEventListener(MouseEvent.CLICK, checkToHit);
removeEventListener(Event.ADDED_TO_STAGE, gameAddedToStage );
stage.removeEventListener(KeyboardEvent.KEY_DOWN,keyDownHandler);
stage.removeEventListener(KeyboardEvent.KEY_UP,keyUpHandler);
if (mouseCursor != null)
{
mouseCursor.removeEventListener(Event.ENTER_FRAME,followMouse);
mouseCursor = null;
}
gotoAndPlay("level1win");
}
And inside my classes,
public function addScore()
{
root["increaseScore"]();
}
This is what increaseScore does
public function increaseScore()
{
playerScore += 1000;
}
So where is the null object? D:
Also I am very inexperienced using the debugger so I apologize if this could be easily solved with that. I tried it and couldn't figure it out before coming here.
What is OPP method?
What is FrameScript?
Also, the class is MovieClip
thanks :)
Fixing this is done with the help of the debugger!
The error is telling you that something is null and you're trying to access it. That's not good, so let's think what could be null?
1) Your movie clip instance might be null.
2) increaseScore() method of your movie clip instance might try to access something that is null
You didn't post any code that I could analyse at the time I'm writing this answer, so I can't say for sure.
Some possible problems:
Your class is not called MovieClip, but you're just trying to cast your root object to a MovieClip. Thing is, MovieClip's don't have a increaseScore() method. You should instead call the increaseScore() method with
root["increaseScore"]();
This will call the method of your root timeline, but since we are using weak-typing, you might have problems with debugging it later. But I guess that's the price you pay when writing all the code in a frame instead of using OPP approach.
The true reason here is said in the error: Cannot access a property or method of a null object reference.
What this means is that MovieClip(root) is null (it doesn't even bother to check if the function is present). The reasons for this might be three:
Your document class is not MovieClip (could be Sprite).
The class you use root in, is not DisplayObject. The property is part of the DislpayObject class and so if you use it on other kind of classes that does not extend it, it won't work.
You are using this piece of code before you have added the instance to the stage (most probable cause). The root property represents the top-most display object in the tree. If you haven't added the child to the tree, it has no roots :) Check out parent - the error should be the same.

in AS3, removeEventListener(Event.ENTER_FRAME) is not working

I have been dealing with this problem for days already. I am at my wits' end!
I can't seem to find a definitive answer anywhere on any of the forums, documentation, etc.
Everything looks fine at first run, or when I load a next level for the user to play. But if the user hits the ESC key to load a different level, the ENTER FRAME listener does not get removed and it duplicates all the triggers in it, showing the player going really fast, and all funky, because it builds on top of the previously instantiated ENTER FRAME listener.
I don't know if I have a problem of an anonymous function, or an unknown instance being referenced in my removeEvent... command... Bottom line, I give up and I need this working HELP!!!
Here's the code:
function initPlay():void
{
//code here determining what display object to add to the list and assign it to the currentLevel variable (a movieclip)
if(userIsLoadingOtherLevel){
removeEnterFrameListener();
addChild(currentLevel);
}
if(userIsGointToNextLevel)
addChild(currentLevel);
currentLevel.addEventListener(Event.ENTER_FRAME, onEnterFrame);
function onEnterFrame(event:Event):void
{
//collision detection, parallax scrolling, etc, etc is done here.
if(allCoinsCollected)
loadNextLevel();
if(ESCKeyPressed)
ESCKeyPressHandler();
}
function loadNextLevel():void
{
removeChild(currentLevel);
newLevelToLoad++
removeEnterFrameListener();
initPlay();
}
function ESCKeyPressHandler():void
{
removeChild(currentLevel);
initPlay();
}
function removeEnterFrameListener();
{
currentLevel.removeEventListener(Event.ENTER_FRAME,onEnterFrame)
trace("currentLevel.hasEventListener(Event.ENTER_FRAME) = "+currentLevel.hasEventListener(Event.ENTER_FRAME)); //outputs TRUE if called from loadNextLevel but FALSE if called from initPlay() !!!
}
}
I also tried to add and remove the eventListener to stage, MovieClip(Root), or nothing at all and the result is always the same.
I know that there may be other ways to design such a process, but please note I am not really flexible at the moment on doing this because the project is very long (about 4000 lines of code) and removing the ENTER FRAME this way, crazy or not should still work!!
THANK YOU in advance for anyone willing to help.
The problem appears to be the nested functions inside the initPlay() method.
Each time you call initPlay() you are defining new functions. Some of these nested functions call initPlay() themselves.
Functions are objects (memory references). So each time you call initPlay() you are making new references to new functions. So when you try to remove an event listener, you're only able to remove one of these event handlers (the one in the current scope of execution).
I'm not sure if I'm explaining this clearly, perhaps this example will help. I'll use numbers to represent the references to each function, and a simple scenario that is similar to yours:
function example():void
{
addEventListener(MouseEvent.CLICK, mouseClickHandler);
function mouseClickHandler(event:Event):void
{
if (someCondition)
{
example();
}
else
{
removeEventListener(MouseEvent.CLICK, mouseClickHandler);
}
}
}
When we run this function the first time, a new function is defined within the scope of the example() function. Lets use the number 1 to represent the reference to this nested function. someCondition is true on the first time around, and so the example() function is called again.
On the second execution of the example() function, a new reference to the mouse event handler is created (#2). We also add the event listener again. At this point, there are two event handling functions in memory, and both will be executed when the event is dispatched.
Let's say that in the second invocation of example() that someCondition is false and now we want to remove the listener. When we call:
removeEventListener(MouseEvent.CLICK, mouseClickHandler);
It's referring to event handler #2. Event handler #1 still exists, and because it's hidden in the scope of the first invocation of example() it can't be removed here.
My simple example breaks down after this... but I hope it makes it clear why your event handlers shouldn't be nested inside a function. Admittedly, this is difficult to describe and even more so in a real world example like yours. But I'm pretty confident that this is the source of most, if not all, of the issues you describe.
Here's how I was able to get around this without changing the scope of the nested functions (although I agree that would be the preferred solution) by creating a boolean variable called "loadingNewGame" and changing it to true from outside the onEnterFrame (in fact, this assignment was done from initPlay() and then from onEnterframe I called removeEnterFrameListener() function. This did the trick.
here's the code in case anybody is interested:
// package, and other code here.
var loadingNewGame:Boolean = new Boolean(false);
function initPlay():void
{
//code here determining what display object to add to the list and assign
//it to the currentLevel variable (a movieclip)
if(userIsLoadingOtherLevel)
{
loadingNewGame = true;
removeEnterFrameListener();
addChild(currentLevel);
}
if(userIsGointToNextLevel)
addChild(currentLevel);
loadingNewGame:Boolean = false;
currentLevel.addEventListener(Event.ENTER_FRAME, onEnterFrame);
function onEnterFrame(event:Event):void
{
if(loadingNewGame)
removeChild(currentLevel);
//collision detection, parallax scrolling, etc, etc is done here.
if(allCoinsCollected)
loadNextLevel();
if(ESCKeyPressed)
ESCKeyPressHandler();
}
function loadNextLevel():void
{
removeChild(currentLevel);
newLevelToLoad++
removeEnterFrameListener();
initPlay();
}
function ESCKeyPressHandler():void
{
initPlay();
}
function removeEnterFrameListener();
{
currentLevel.removeEventListener(Event.ENTER_FRAME,onEnterFrame)
trace("currentLevel.hasEventListener(Event.ENTER_FRAME) = "+currentLevel.hasEventListener(Event.ENTER_FRAME));
//outputs true
}

Can't remove child from the stage - if(stage.contains(child1)){removeChild(child1);} doesn't work

It may sound stupid, but how can I remove a definite child from the stage?
e.g.
function giveMeResult(e:MouseEvent):void
{
if(stage.contains(result))
{removeChild(result);}
addChild(result); // this part works fine, but it adds one over another
}
it adds one result on the top of the previous one.
I want the function "giveMeResult: to remove "result" if is on the stage and add a new one.
UPDATE:*
result is a TextField, and result.txt ="" changes from time to time ...
trace (result.parent); /// gives [object Stage]
trace (result.stage); /// gives[object Stage]
trace (result.parent != null && result.parent == result.stage); // gives true
when
result.parent.removeChild(result);
is written without if statement - gives error TypeError: Error #1009: Cannot access a property or method of a null object reference.
when written inside:
if (result.parent !=null && result.parent == result.stage)
{
result.parent.removeChild(result);
}
nothing happens and new child is added on the top of the previous one.
Thanks to all!!!
The result is simple :)
All I had to do is just change result.txt without even removing it from the stage :)
You need to type stage.removeChild(result); and subsequently stage.addChild(result);
Edit:
Looking at a function similar to yours:
private function func(e:Event) : void {
if(stage.contains(result)) {
stage.removeChild(result);
}
stage.addChild(result);
}
The only way this would add a new instance of the TextField result to the stage, without removing the old one, would be if result has changed. See this flow of execution:
var result : TextField = new TextField();
// An event occurs and func get's called.
// now result will be added to stage.
result = new TextField();
// An event occurs and func get's called again
// This time the stage.contains(..) will return false, since the current result
// is not actually on stage. This will add a second TextField to the stage.
If I am clearly understood what you want, then this code can help you:
if (result.parent != null && result.parent == result.stage)
{
// stage itself contains result
result.parent.removeChild(result);
}
From the docs for DisplayObjectContainer.contains(): "Grandchildren, great-grandchildren, and so on each return true."
So contains means anywhere on the display list, not just direct children. What you want is the parent check Manque pointed out, or simply to remove result from anywhere it might be:
if (result.parent) {result.parent.removeChild(result); }
Though oddly, addChild(result) will automatically remove it from its previous parent - a DisplayObject can only be at one place in the DisplayList at a time, so I'm not sure why you're seeing multiple results...
Is it possible that the "result" you've passed in isn't the result that's already on the stage?
Have you tried?
MovieClip(root).removeChild(result)
[EDIT]
function giveMeResult(e:MouseEvent):void{
if(result.parent != null && result.parent == result.stage){
stage.removeChild(result);
}
stage.addChild(result);
}
All I had to do is just change result.txt without even removing it from the stage

Problem to move a MC on foreground

i've a problem to a set a different child index of a Movieclip. This is the code:
function processMusica():void
{
var loadStatus:int=0
var lastHeight:int=0
for (var m=0; m < myXML.BLADE[sup].child("brano").length(); m++)
{
var titolobrano:TextField=new TextField
bladearray[sup].contenitore.addChild(titolobrano)
titolobrano.text=myXML.BLADE[sup].brano[loadStatus].titolo
lastHeight=titolobrano.height
titolobrano.doubleClickEnabled=true
titolobrano.addEventListener(MouseEvent.DOUBLE_CLICK, riproducibrano)
loadStatus+=1
}
if (isPlaying==false)
{
var riproduzioneDetails:MovieClip=new MovieClip
riproduzioneDetails.name="riproduzioneDetails"
var artista:TextField=new TextField
artista.name="artista"
bladearray[sup].contenitore.addChild(riproduzioneDetails)
riproduzioneDetails.x=475
riproduzioneDetails.addChild(artista)
}
setChildIndex(bladearray[sup].contenitore.riproduzioneDetails, bladearray[sup].contenitore.numChildren-1) //<------ PROBLEM HERE!
I want to move "riproduzioneDetails" MC on foreground, but when i attempt to launch application, it give me this error: TypeError: Error #2007: Parameter child must be non-null
I can see two potential problems, but it is hard to say because all the variables are not declared in your code. There may be a problem with your test condition:
if (isPlaying==false)
{
...
}
If "isPlaying" is true, your object "riproduzioneDetails" is never created, so of course it will be null. You have to create your object outside the "if" condition, before testing "isPlaying".
Another potential problem is the way you access your object, with "bladearray[sup].contenitore.riproduzioneDetails". As you used the "addChild" method to store your object, there should logically be a kind of "getChild" method. For instance,
setChildIndex(bladearray[sup].contenitore.getChildByName("riproduzioneDetails"),bladearray[sup].contenitore.numChildren-1)
... may be better.

checking if child exists

Hello i have a function as following:
private function seatClickHandler(e:MouseEvent):void{
var check:Check = new Check();
if(e.target.contains(check)){
e.target.removeChild(seat);
}else{
e.target.addChild(check);
}
}
basicly i want to check if e.target contains a child called check. If it does i want e.target to remove the child, else i want to add the child. But the method i tried doesnt seem to work although i think this is the way to go. Any suggestions?
When you declare your Check object, Actionscript creates a reference code for that specific object.
So the first time your code is run, your Check object could be given a reference of #c0ecc29. Your if statement checks to see if #c0ecc29 is a child component of target. It won't be, so the Check object with reference #c0ecc29 is added to target.
The second time the clickHandler is called, a new instance of the Check object is created which will have a new reference id. Your target has the original Check object with the #c0ecc29 reference so it won't get removed.
The correct way to get this working depends on what target is (DataGrid, Group, etc.).
EDIT:
Based on your comments, I would try something like this. It checks to see if the Check object is a child of target and adds it if needed. Then when the Check object is clicked, it will toggle its visibility.
public var check:Check = new Check();
private function seatClickHandler(e:MouseEvent):void
{
if(!e.target.contains(check))
{
check.addEventListener(MouseEvent.CLICK, check_handleClick);
e.target.addChild(check);
}
}
protected function check_handleClick(event:MouseEvent):void
{
check.visible = !check.visible;
}
If you need to actually remove the Check object from target instead of just changing its visibility, you could try this:
public var check:Check = new Check();
private function seatClickHandler(e:MouseEvent):void
{
if(!e.target.contains(check))
{
e.target.addChild(check);
}
else
{
e.target.removeChild(check);
}
}
If the child is named 'check' then you should be able to use getChildByName(). See flash.display.DisplayObject.name
If you happen to have the child in memory, you can use getChildIndex()
check is a new object in the scope of that function, so it will not be a child of the event target.
What you want to do is declare check as a global variable (And also cast target as DisplayObjectContainer).
e.g.
private function seatClickHandler(e:MouseEvent):void{
if((e.target as DisplayObjectContainer).contains(check)){
(e.target as DisplayObjectContainer).removeChild(seat);
}else{
(e.target as DisplayObjectContainer).addChild(check);
}
}
However I'm not sure if this is exactly what you want to do (There can only be one check). A better approach would be to have a function (maybe toggleCheck) on the target, and have that display object responsible for rendering the check (And removing it)
This worked perfectly fine for me in my situation:
if(possibleChild.parent == holder)
holder.removeChild(possibleChild)
It may or may not be exactly what you're looking for.