views:

44

answers:

2

In my Flash project I have a movieclip that has 2 keyframes. Both frames contain 1 movieclip each.

frame 1 - Landing
frame 2 - Game

The flow of the application is simple:

  1. User arrives on landing page (frame 1)
  2. User clicks "start game" button
  3. User is brought to the game page (frame 2)
  4. When the game is over, the user can press a "play again" button which brings them back to step 1

Both Landing and Game movieclips are linked to separate classes that define event listeners. The problem is that when I end up back at step 1 after playing the game, the Game event listeners fire twice for their respective event. And if I go through the process a third time, the event listeners fire three times for every event. This keeps happening, so if I loop through the application flow 7 times, the event listeners fire seven times. I don't understand why this is happening because on frame 1, the Game movieclip (and I would assume its related class instance) does not exist - but I'm clearly missing something here.

I've run into this problem in other projects too, and tried fixing it by first checking if the event listeners existed and only defining them if they didn't, but I ended up with unexpected results that didn't really solve the problem.

I need to ensure that the event listeners only fire once. Any advice & insight would be greatly appreciated, thanks!

+2  A: 

If you have two frames with different clips on the same layer, each time that frame "enters", that clip is created. When it "leaves" the clip is removed, but it still will be kept around and not be garbage collected. So the next time the frame enters a different clip is "created" and it gets its own listener. Your best bet is to remove the listeners when you change frames. I usually get around this kind of stuff by having a listener for Event.REMOVED_FROM_STAGE in each class. If it's removed, then you clean up the remaining listeners.

You may also want to experiment with "weak listeners":

addEventListener(GameEvent.GAME_START, gameStartedHandler, false, 0, true);

The "true" makes the link "weak" so if an object is removed, the garbage collector can pick it up. I wouldn't rely on this completely though. Better to manually remove references so you can be sure.

This answer assumes a lot of course. It'd be helpful if you posted code/screenshots/flas to better diagnose.

Typeoneerror
This makes a lot of sense. I'm reading up about GC right now, and I made sure to remove the event listeners and it seems to be working fine now. I like your idea of using Event.REMOVED_FROM_STAGE. Thank you!!
Arms
+1. The same as I suggested, but you beat me for 30 seconds!
Juan Pablo Califano
Hehe, I've learned to post my answer when I'm about half-way done with my thought. Then go back and finish it. Sneaaaaky, charlie.
Typeoneerror
@Juan Pablo Califano, if I could, I would mark both of your answers as the accepted ones, but Typeoneerror did beat you by a few seconds ;)
Arms
Weak refs are not of much help here, I'm afraid, since the problem seems to be that code in the Game class continues running. And it will continue to run until the MC is collected. So, you're better off just removing the listener.
Juan Pablo Califano
I think Juan Pablo Califano is correct about the weak refs not working - I tried setting them as weak, yet they still fired. But still a good thing to keep in mind for future code.
Arms
@Arms. No worries. I'm not obsessed about rep ;)
Juan Pablo Califano
+2  A: 

Without seeing the code or knowing what events you are listening for and who/what fires them, it's kind of hard to know for sure.

But, my guess is that the movieclips are not being collected (this is not neccesarily a memory leak!) and so, they're still allive and kicking. You should probably have a method that sets them to an "idle" state, so to speak. Meaning, you remove listeners, stop timers, etc. The idea is to put your object in a state where it doesn't run any code.

A simple approach for movieclips and other display objects, which is often good enough, is listening for the ADDED_TO_STAGE and REMOVED_FROM_STAGE events. The idea here is that your object "activates" when it's added to the stage, which in your case would be when you reach frame 2 for your Game class; it "deactivates" when it's removed, that is, when you go back to frame 1.

Something along these lines:

public class Game extends MovieClip {

    private var _timer:Timer;

    public function Game() {
        addEventListener(Event.ADDED_TO_STAGE,init);
        addEventListener(Event.REMOVED_FROM_STAGE,destroy);
    }

    private function init():void {
        //  your init code goes here
        //  just an example:
        _timer = new Timer(33);
        _timer.addEventListener(TimerEvent.TIMER,mainLoop);
        _timer.start();
        trace("init");
    }

    private function destroy():void {
        _timer.stop();
        _timer.removeEventListener(TimerEvent.TIMER,mainLoop);
        trace("destroy");
    }

    private function mainLoop(e:TimerEvent):void {
        //  code for main loop here...
        trace("mainLoop");
    }
}

Check the traces to see if its working correctly. You should see "init" traced out, then "mainLoop" as long as you stay in frame 2 and "destroy" when you go back to frame 1. "mainLoop" should stop tracing at this point.

In addition to that, you might want to check that there's no memory leak (the fact that you have 7 instances does not neccesarily mean you have a leak; but at some point, if the GC runds, at least some of them should be released; if this never happens, it's a sympthom of a leak; try forcing a GC to see if the number of allive instances goes down; if not, it's quite likely that you have a leak).

Juan Pablo Califano
Thank you for the great code example!
Arms