How to delete all event listeners at once in AS3 - actionscript-3

I'm making a small game in as3.
The game contains 10 levels.
When i enter 1 level everything is alright. But when i enter the second level (frame) the event listeners from the first frame are still working and a recieve a warning saying ' Cannot access an object of null objct reference'. This is because i delete every object of the first level and th add the objects from stage 2.
I've tried using removeEventListeners, but it doesn't work, cause ENTER_FRAME Listeners work one more time after I remove the Event Listeners.
I've tried using different frames for different levels, bit it doesn't work. Also i tried using 1 frmae for all 10 frames, but i recieve much many warning and the Flash Loader is overloaded.
How can i switch through levels (back and forward)? Thanks in advance.
addEventListener(Event.ENTER_FRAME, subtracting2);
arrListeners.pop(); // poping it out of the array because it will be deleted after the count reaches 0
function subtracting2 (e:Event):void
{
count--;
var FAcoef:Number = count/30; //
FadeAway.alpha = FAcoef; // Some effect like FadeAway
setChildIndex(FadeAway, numChildren - 1); //
if(count == 0)
{
setChildIndex(FadeAway, 0);
removeEventListener(Event.ENTER_FRAME, subtracting2);
}
}

There is no built-in way to remove all listeners.
You could use weak references to let the listeners be removed when the object is Garbage Collected.
object.addEventListener( ......, ......., false, 0, true );
Or you could add the removeAllListeners functionality yourself, here is some info (Have a look at Ion comment)
But.. you shouldn't need any of the above if you take care to remove every event listener straight away when it is not needed any more.
If you have a class with one or more event listeners which are needed till the end of the instance's life, you should create a destroy() function. In that destroy() function you would remove all the event listeners.
In your case, you could call destroy() before you go to second level(frame).

Related

Does adding eventListener with same params null/replace previous eventListeners?

I've inherited a large, legacy Flex project and the deeper I get into the code, the more concerned I am becoming. For example, I am looking at code for a "window" type image viewer within the app. Every time it is displayed, the eventListeners below are added and never removed.
Since these are strong references and never removed, that is one problem but this repeatedly adding eventListeners is giving me pause. The "window" can be displayed and hidden many times in the lifecycle of the app.
My question: does this mean that is has n = (4 * number of times displayed) eventListeners? (...shudder).
This is a huge project revision on a tight budget so I am trying to determine if I fix this sort of thing or just let it go.
addEventListener(MouseEvent.MOUSE_OVER, mouseOverHandler);
addEventListener(MouseEvent.MOUSE_OUT, mouseOutHandler);
addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
If they are different eventlisteners, they will be added multiple times. If they all refer to the same function, it will be overridden each time, calling the specific function only once.
try out the following short example to see what i mean:
var s:Sprite = new Sprite(); //some sort of displayobject with EventDispatcher capabilities
s.addEventListener(MouseEvent.CLICK, onClick);
s.addEventListener(MouseEvent.CLICK, onClick);
function onClick(e:MouseEvent):void{
trace("hey");
}
pressing on the Sprite will give you a console output of "hey", not two "hey"s.
Now consider the following:
var s:Sprite = new Sprite();
s.addEventListener(MouseEvent.CLICK, onClick);
s.addEventListener(MouseEvent.CLICK, onClick2);
function onClick(e:MouseEvent):void{
trace("hey");
}
function onClick2(e:MouseEvent):void{
trace("sup");
}
This will give you an output of "hey" and "sup" once you press on the Sprite.
If you are really concerned, you could just give the event listener a weak reference. I don't know how complex the project is you're working on, but implementing something to get rid of all eventlisteners at once (like, waiting for Event.REMOVED_FROM_STAGE and then manually removing the listeners) shouldn't be too time-intensive.

AS3 Button to stop Movieclip after its finished playing

Ok, so I'm a beginner at AS3 and Flash and I managed to put this code together for an animation. A Button called start_btn is supposed to start and stop a movieclip called main_mc. On the first click of the Button, the Movieclip is supposed to play (which it does), however on the second click, the movie stops in the middle of its animation (which I don't want). My question is, when you click the Button a second time, how can i get the Movieclip to finish playing its animation then stop on the last frame?
I thought about using if (main_mc.currentFrame == main_mc.totalFrames); {main_mc.stop(); but the Movieclip still does not stop on the last frame. The Movieclip itself also has a gotoAndPlay(2); command on the last frame so that the animation repeats before the Button is clicked a second time.
here is the code i have:
`start_btn.addEventListener(MouseEvent.CLICK, mainaniS);
function mainaniS(event:MouseEvent):void
{
main_mc.play();
start_btn.removeEventListener(MouseEvent.CLICK, mainaniS);
start_btn.addEventListener(MouseEvent.CLICK, mainaniSt);
}
function mainaniSt(event:MouseEvent):void
{
if (main_mc.currentFrame == main_mc.totalFrames);
{main_mc.stop();}
start_btn.removeEventListener(MouseEvent.CLICK, mainaniSt);
start_btn.addEventListener(MouseEvent.CLICK, mainaniS);
}`
Try main_mc.gotoAndStop(main_mc.totalFrames).
I was going to provide a quick and dirty solution, but decided instead to try and explain a few of the issues with your current implementation and attempt to refactor and explain and better one. Unfortunately I don't have access to Flash right now, so the code is untested.
You're adding and removing event listeners often, which is generally a bad idea. Instead, since you're using a single button to perform multiple functions it would make sense to track the button state in a separate variable. In this case, a boolean for whether or not the movieclip is currently playing.
var playing:Boolean;
Now we can combine the mainaniS and mainaniSt into one and perform a different action based on whether or not the movieclip is playing, and just keep the one eventlistener on the button. I've also taken the liberty of naming the method something more meaningful:
start_btn.addEventListener(MouseEvent.CLICK, onStartClick);
function onStartClick(event:MouseEvent):void
{
if(playing) {
playing = false;
}
else {
playing = true;
main_mc.play();
}
}
You may be wondering why we don't call main_mc.stop() in the first block: the reason is that you don't want to stop the movieclip as soon as you click the button, but after the movieclip has finished playing if the button has been clicked. Therefore, we just set playing to false to indicate that we want it to stop later.
Finally, we need to make sure the movieclip stops upon completion, but only if playing is false. To do this we add a listener to movieclip that is called every frame, and checks whether playing is false, and if it's on the last frame. Note that the last frame is actually totalFrames - 1: this is because the frame numbers start from zero rather than one (i.e. if totalFrames is 3, the frame numbers will be 0, 1, 2).
main_mc.addEventListener(Event.ENTER_FRAME, animate);
function animate(event:Event):void {
if(!playing && main_mc.currentFrame == main_mc.totalFrames - 1) {
main_mc.stop();
}
}
All the refactored code together:
var playing:Boolean;
start_btn.addEventListener(MouseEvent.CLICK, onStartClick);
main_mc.addEventListener(Event.ENTER_FRAME, animate);
function onStartClick(event:MouseEvent):void
{
if(playing) {
playing = false;
}
else {
playing = true;
main_mc.play();
}
}
function animate(event:Event):void {
if(!playing && main_mc.currentFrame == main_mc.totalFrames - 1) {
main_mc.stop();
}
}

Flex AS3 - About variable instances, event listeners and garbage collection

QUESTION ONE:
In the following example, ive added a listener to the foundMic. My question is if i re-run the foobar.initMic(); to reinitialize the microphone will i end up with a dead event listener floating in memory, and would it be picked up by garbage collection? Does calling the .getMicrophone() just reattach the same resource to foundMic or does it destroy the old foundMic and create a new instance of foundMic in memory.
As in if its the exact same foundMic then adding the event listener will silently fail and not create a second listener. But if foundMic becomes a new instance then it would be creating a new listener. Then back to the question about garbage collection, would it be considered unreferenced for clean up?
public class foobar {
public static var foundMic:Microphone = null;
public static function initMic():void {
foundMic = Microphone.getMicrophone();
foundMic.codec = SoundCodec.SPEEX;
foundMic.setSilenceLevel(0, 5000);
foundMic.gain = 50;
foundMic.setUseEchoSuppression(true);
foundMic.soundTransform.volume = 1;
foundMic.addEventListener(StatusEvent.STATUS, onMicStatusEvent);
return;
}
public static function onMicStatusEvent(event:StatusEvent):void {
if (foundMic && !foundMic.muted) someButton.enabled = true;
else someButton.enabled = false;
return;
}
} // END CLASS
QUESTION TWO:
Based on question one, if i added a remove listener just before adding the listener, does that actually remove the listener on the second time ran which was created when the method was ran the first time? Or is that foundMic already a new instance from the .getMicrophone() so nothing is removed because the first listener is already floating in memory associated with the previous instance of foundMic?
public static function initMic():void {
foundMic = Microphone.getMicrophone();
.
.
.
foundMic.removeEventListener(StatusEvent.STATUS, onMicStatusEvent);
foundMic.addEventListener(StatusEvent.STATUS, onMicStatusEvent);
return;
}
foundMic will just be assigned a new value which is a reference to to the Microphone - nothing should be garbage collected.
A case where the garbage collector should kick in is something like this:
var someVar:Sprite;
for(var i:int = 0; i < 5; i++)
{
someVar = new Sprite();
}
In this case you've created 5 new Sprite objects but ended up with only one assigned to the someVar variable - the other 4 should get garbage collected at some point because they are not referenced by any variable.
As far as question two, the code you have should give you an error the first time you call initMic() since at that point there is no onMicStatusEvent listener yet. Before trying to remove the event listener first test to see if it exists:
if(foundMic.hasEventListener(StatusEvent.STATUS)
{
foundMic.removeEventListener(StatusEvent.STATUS, onMicStatusEvent);
}
foundMic.addEventListener(StatusEvent.STATUS, onMicStatusEvent);
This code will always remove the event listener first if it exist and then subsequently add a new event listener. However, in this case I don't see any reason for this, as addEventListener by itself should work just fine - multiple calls are all on the same object, so no matter how many times you call initMic() you should only have one event listener running - there's no need to manually remove the listener and then recreate.
Here is what happens with event listeners. someObject.addEventListener('someEvent', someFunction) hands someObject a reference to someFunction. Because of the way scope works in ActionScript, the function will have access to everything in the scope of the code that created it. So until you call removeEventListener, the instance where the function exists will be held in memory as long as someObject is in memory.
However, in your case, there is no instance, because by using static functions your listeners will exist from the time your Class FooBar is first referenced until your swf is taken out of the computer's memory.
There is no net effect of removing the listener prior to adding it, because it is exactly the same function being handed to exactly the same object, which isn't going to call your function twice.
For more on how event listeners work in detail, check out this blog post.

Timeline instances not on first frame

It's been a while since I've had to write Actionscript that really needs to integrate with the timeline (in this case, controlling a series of frames that must happen in a certain sequence) and I am trying to figure out what to do.
In the first few frames, I have a button "next_1".
At frame 10, I need to have another button "next_2". I really really need this button to not be on frame one (I could possibly just make it invisible, but that's going to create a clickable area that I don't want).
The problem is, anything I don't put on "frame_1" renders as null in my Document class.
Is there any solution to this? I would rather not have to write my script on the timeline if possible (it seems easier in the long run to keep it in a document class)...
Items on the timeline are created on the fly, so if the playhead has not reached frame 10, next_2 is not created.
Easiest Document-class solution:
Create an array of frame labels like ["label1", "label2"]
Create sectionIndex var and set it to 0
Create a next button on its own layer so it is always showing.
When the next button is clicked, increment sectionIndex, then gotoAndPlay(myLabels[sectionIndex])
Okay, directly lifted from "Real World Flash Game Development":
/**************************************************
* FRAME LABELS *
**************************************************/
private function enumerateFrameLabels():void {
for each (var label:FrameLabel in currentLabels) {
addFrameScript(label.frame-1, dispatchFrameEvent);
}
}
private function dispatchFrameEvent():void {
dispatchEvent(new Event(currentLabel, true));
}
This dispatches an event at each frame label on the timeline.
Then you can just add event listeners for each frame:
addEventListener("name_of_my_framelabel", frameHandler);
addEventListener("another_framelabel", frameHandler);
And write a switch statement to add event listeners for the buttons when they actually show up on the timeline.
private function frameHandler(e:Event):void {
switch(e.type) {
case 'screen_2':
stop();
next_2.addEventListener(MouseEvent.CLICK, click2, false, 0, true)
break;
}
}

Skipping to next game level in AS3

I am in the process of building a game with around 20 levels. Now, as I was thinking of trying to add a "skip" option to the game with the space bar key. I have a little trouble, since holding down the spacebar invokes the KeyboardEvent.KEY_DOWN event multiple times.
The above scenario (Keydown and keyup) works just fine when I'm trying to move my player character on screen.
The one main concern that is lingering in my mind is "Is this going wrong because the KeyboardEvent listeners do get removed and added when changing levels?"
P.S: Adding the skip option as a button works just fine, but I'd really like to use Spacebar for the ease of use.
Listen for KeyboardEvent.KEY_UP as well, and keep a boolean that acts as a switch. If the keyboard is pressed AND the boolean is false, set the boolean to true and proceed. Then, when the key_up event callback is invoked, reset the boolean to false.
Does somethig like this work for you?
private function levelInit () : {
// ...
stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDownListener);
// ...
}
private function keyDownListener (e : KeyboardEvent) : void {
if (e.keyCode == Keyboard.SPACE) {
stage.removeEventListener(KeyboardEvent.KEY_DOWN, keyDownListener);
// add stuff to initiate the level skip
}
}
You could also call the stage.addEventListener from a setTimeout to delay adding the listener to protect things more.
Remove the key_down listener in the function and add a key_up listener. Then in the key_up function remove that listener and add back the key_down listener.