Removing movieclip in timeline - really gone? - actionscript-3

I want a explosion to play when a object hits another object. I do this so that when hitTestObject is true, I run this function
function createExplosion():void {
var explosion:Explosion = new Explosion(enemy.x, enemy.y);
this.parent.addChild(explosion);
}
Explosion class only consists of setting the input to it's x and y values. Then in explosion movieclip I have a few frames of a animation. It ends in a keyframe (i.e., as an action in that frame) with the following code:
stop();
this.parent.removeChild(this);
My question is. Is it really gone now? I had to add stop() to not get error 1009. That makes me suspect some event timer is still running around?

Removing a display object from the display list, doesn't stop the animation or remove it from memory. It just removes it from the display list. Yes, you need to stop the animation and remove any event listeners that might be active for that object.
A display object is never truly gone until it is garbage collected. But that will not happen until no references to that object remain. So if you have a variable called explosion that references your display object, you need to set it to null after removing it from the display list :
explosion.stop(); // stop the animation
removeChild(explosion); // remove from display list
explosion = null; // remove variable reference
// explosion now qualifies for garbage collection.
Keep in mind that this doesn't immediately remove the object from memory, it just makes it fair game for the garbage collector. You can google "AS3 Garbage Collection" to get more information on that process.

Related

Deleting everything from the stage AS3

So, I am wanting to clear my whole stage. I already searched through the internet, and unfortunately nothing has worked for my situation.
Basically, what I am doing is a somewhat complex maze generator and before I create a new one, I want to get rid of everything I created prior to that. So far, I hear that the best way to remove movieclips from the stage is buy using:
while(numChildren > 0)
removeChildAt(0);
However this only works for the current movieclip I call it in, which doesn't include the maze I generated. I just want to get rid of absolutely everything.
Any ideas on how to do this?
You're thinking along the right lines, you can use numChildren and removeChildAt however you need to call them in the scope of the stage:
while(stage.numChildren > 0)
{
stage.removeChildAt(0);
}
To just remove it from the stage:
stage.removeChildren();
Just removing clips from stage isn't always equal of removing them from memory
removeChildren, removeChild or removeChildAt does not actually remove an Sprite or any other DisplayObject from memory, it only removes it from the displaylist. That means if you create 1000 sprites and add them to the stage (displaylist), and then use removeChildren they could still exist in memory (forever). Then you have a memory leak.
To remove it from memory, all objects with a relation to the displayObject should be set to null. This includes event listeners and relations from / to non-displaylist related objects.
If you want to be sure all related stuff should be gone, just null it and check these things:
Remove it from the displaylist using removeChild or removeChildAt or removeChildren. (note this can be done from the stage)
Remove all eventListeners that are attached to the clip, or use weak event listeners.
If you used a reference in an Array, Vector, Dictionary or any other object, remove it from the object, set it to null or splice it using Array.splice()
setTimeout/setInterval should be cleared
Set the object = null
You can profile the memory with Mr Doob stats or performance stats from the Temple Library. You should see a drop (garbage collection) after a while when removing all clips. After removing multiple times the memory indicator should not be higher.

ActionScript 3 Removing All RESIZE Event Listeners

I'm working on a Flash project which is separated into separate scenes.
In Scene 1 I have multiple MovieClips (which include event listeners for RESIZE (and others) inside them).
In Scene 2 I have a few common MovieClips and new ones (which also include event listeners for RESIZE (and others) inside them).
After clicking a button from Scene 1 to go to Scene 2, it's fine, except for if I resize the stage and then I get the following error:
TypeError: Error #1009: Cannot access a property or method of a null object reference.
I know it's related to the event listeners, but it would be unrealistic to remove them each individually as it's expected there will be many.
If I undertand your situation correctly, I think you will in the end need to remove each listener individually, or add the resize listener only once. Since you mentioned scenes, am I right to assume you are working on the timeline? I also am assuming the null object reference error comes from a scene that has been removed from the stage, making reference to a display object that is no more, a ref to the stage after the scene has been removed, or just calling a function (the resize handler) on an object that no longer exists.
Some way to deal with this are:
Add some checking in the listener handler functions
if (!this.stage) return
To avoid the errors, but will not help if the object the function is a method of has been removed.
To avoid needing remember to remove hundreds of listeners, create removeAllListeners and addCustomEventListener functions. Instead of the usual addEventListener, call you addCustomEventListener which in turn will call addEventListener. Have addCustomListener store the target, listener function and event string in a dictionary or array of objects. removeAllListeners can loop through the dictionary or array and remove all your listeners. It is a bit like setting up an event hub, but does not go quite that far.
Instead of adding the RESIZE event listener to each scene, add it only once. Then in the listener function call a function on whichever scene is the active scene or view.
This last one is the approach I have seen most often, and is the most bullet proof. It may be tricky to implement on the time line, I have always been a little hazy on timeline variable scope.
Yes, so far as I know there is no good automated way to do this, however it would be a good practice to create a registerAllListeners and a removeAllListeners methods that manually add and remove the appropriate listeners to your object.

MovieClip on Flash stage does not re-instantiate when leaving keyframe and returning

I've been debugging the following issue for quite awhile now and have hit a wall.
I've set up a project in Flash (CS4, btw) that has a set of keyframes that I move between to represent the various screens of a game. One of them has a MovieClip defined (with children inside it) representing an option menu, that appears on a couple of different keyframes.
The problem I'm having is that this MovieClip reference seems to be accessible when I first enter the keyframe (using "gotoAndStop"), and occassionally when I move to other frames and back. But in at least one case, when I exit the frame and come back, I get a null reference error (TypeError: Error #1009: Cannot access a property or method of a null object reference). when I try and access it (getChildByName("optionMenuTitle")). I've even tried having the system iterate from 0 to numChildren and print out the name of each object, but it returns NULL at position 7 despite returning numChildren as 9. Does anyone have any idea why this particular MovieClip reference is NULL only in this case??
Here is a basic (abbreviated) rundown of the process occurring:
//set up function to be fired on frame construction
addEventListener(Event.FRAME_CONSTRUCTED, fadeIn, false, 0, true);
public function fadeIn(event:Event):void {
_handler.handle(); //this function is called which runs the debug statement below
trace (mainDoc.numChildren); //displays 9
for (var i = 0; i < mainDoc.numChildren; i++) { trace(mainDoc.getChildAt(i).name); } //throws null when it gets to 7
optionMenuTitle = OptionMenu(mainDoc.getChildByName("optionMenuTitle")); //the original failed call that caused me to debug
}
edit: One other potentially useful bit of information. If I comment out the getChild commands above that error, the frame loads and I can see the MovieClip visually displayed on the stage (although it's not interactive and is constantly cycling through the frames of its child objects). Still can't access it programatically though.
another edit: The object in question is a subclass of MovieClip that I named "OptionMenu". I put a breakpoint in the OptionMenu constructor, and when the frame loads correctly, that breakpoint is hit. When I get the error above, the breakpoint in the constructor is never hit. The debugger doesn't seem to give me access to see what's going on inside Flash's mind when it's instantiating the frame, however, so I can't see the logic as to why the constructor is never called.
Well this one has been driving me crazy. I could not workout why it does not reference your optionMenuTitle when you go back to the frame called title a second time.
The only way I could work around it was to take the 3 buttons out of the OptionMenu MovieClip and put them on the stage with the grey background underneath, essentially doing away with OptionsMenu.
So I moved all initialization code from OptionMenu to your TitleHanlder and also added the destroy calls to your destroy method in TitleHandler for each of the 3 buttons.
I also changed the refs from root to mainDoc:
sound.initialize(LogicGameMain(mainDoc).soundOn);
music.initialize(LogicGameMain(mainDoc).musicOn);
This worked for me as you can still interact with the buttons the second time around. It definately seems like there is some bug with these buttons being nested.
I hope this is useful for you.

Unloading external SWF files

I'm loading multiple swf files from the main menu which is never unloaded. I've done this with the following code... Only issue is that instead of unloading to the main menu I just see a white screen as if nothing is loaded.
function BackToMenu(i:MouseEvent):void
{
var BaseMovie:MovieClip = parent.parent as MovieClip;
BaseMovie.parent.removeChild(BaseMovie);
}
EDIT: I'll explain from the start I have a MainMenu.swf. The games are loaded from MainMenu.swf when the button relating to the game is clicked. When a game button is clicked on MainMenu.swf the game loads. When the player completes a game they are presented with the exit button which unloads the current game and shows the MainMenu.swf without having to re-load it.
First, you should remove one parent to make sure you are actually removing only the game:
function BackToMenu(i:MouseEvent):void
{
var BaseMovie:MovieClip = parent as MovieClip;
BaseMovie.parent.removeChild(BaseMovie);
}
This should take care of your most pressing problem, and allow you to return to the menu. You have, however, not really unloaded the game, but only removed it from the display list. This often means, that there are still sounds running, active key and/or mouse listeners, etc. - these must all be taken care of!
And, like I said, this will only fix your immediate problem. It is, however, neither a permanent solution, nor a good one: Since the main SWF is responsible for loading the games, it should also be responsible for disposing of them. You should put cleanup code into your game, but it should only be concerned with stopping any running scripts, sounds, etc. - simple rule: anything that is started within the game, should be stopped within the game. But it should not try to access objects further up in the display hierarchy, or try to unload itself.
The much better way to do this is by replacing all the above code, and letting the main SWF take care of removing the game, as well as unloading it from memory. For this, you have to do three things:
Instead of writing actual removeChild calls, etc., let your button dispatch a custom event to notify the main SWF that it should now be removed:
function onBackButtonClicked( event:MouseEvent ):void {
destroyGame(); // this would be the function that stops all the scripts
dispatchEvent( new Event( "FINISH_GAME", true ) );
}
Note that "FINISH_GAME" is now a "bubbling" event, i.e. it travels downward in the display hierarchy. We can now listen for this event in any ancestor display object containing the game.
In your main SWF, add an event listener to the Loader when the game was successfully loaded. This is done in the event listener that is called when the load process completes:
function onLoadComplete( event:Event ):void {
var loader:Loader = event.target.loader;
loader.addEventListener( "FINISH_GAME", onFinishGame, true );
}
Use the corresponding event handler to remove the game clip:
function onFinishGame( event:Event ):void {
var loader:loader = event.currentTarget;
loader.parent.removeChild( loader );
loader.unloadAndStop();
}
A few more things to consider:
The naming conventions in ActionScript advise us to use lower case names for methods and variables, and upper case only for types.
The same naming conventions suggest we use either "on" or "handle" as a prefix for event listeners, along with the name of the event. Thus, it should be onBackToMenu or rather, onBackButtonClicked, etc.
Since I don't know anything about the code you use for loading, I just assumed you have a complete listener, and you don't keep references to the loader. If you use a member variable, you can use that instead of event.target, resp. event.currentTarget.

hitTestObject, stopDrag stops drag on two movieclips even though function states one movieclip to stop drag

I have a function that states when movieclip1 is dragged and hits a line then it stops the drag, however it seems to stop the entire drag function in the swf on the other movieclips even though they arent called in the function. Can somebody please help me with this.
Regards
T
Here is the code:
function hitTest(event:Event):void
{
if (movieclip1.hitTestObject(line))
{
movieclip1.stopDrag();
}
else
{
}
}
Are you absolutely positive you only have one instance of movieclip1 on your stage? Definitely double check. Are you creating them dynamically, or are they preloaded when your SWF loads?
If they're preloaded:
Perhaps during testing you made some quick copies of it, and now those copies have the same name and they're all responding the same. That's my first guess.
If they're loaded dynamically:
Check the function where they're being created. If you're naming them in a loop (with a number on the end like the above), be sure that you're properly increasing the numeric value used on the end.