Best way to restart an app

erik's Avatar

erik

25 Feb, 2011 09:40 AM

Hi,

I'm building a game, which will be running all day on a touch panel. When 1 group is done playing, the next group will start playing and the game needs to be restarted. So what I want to do is completely set the application to its initial state when the new group is about to play.

I was thinking that I'd simply create a new ApplicationContext and get rid of the old one by creating a public static method on the document class which kills the existing Context and initiates a new one. But that's not a really nice and classy way of solving this problem.

Anyway, I want to prevent writing a lot of code in exiting models and mediators to reset all their states, because I feel it could be done way easier than that by instantiating a fresh set of rules and instances.

Thanks for building robotlegs! It's the best thing ever since Flash was invented :-) I'm giving introductory workshops these days and everyone is always astounded by the sheer power en simplicity of it. Keep up the good work!

  1. Support Staff 1 Posted by Stray on 25 Feb, 2011 10:11 AM

    Stray's Avatar

    Hi there,

    I had to deal with this for restarting my strategy game - the solution I came up with was that the ResetGameCommand dispatches an event on the contextView.

    The contextView is actually a child of the root view. The root view picks up this event and then chucks away the old context and contextView, waits for a moment, and then makes a completely new once.

    All the code is on github (the whole game):

    https://github.com/Stray/robotlegs-demo-StrategyGame/blob/master/sr...

    But the key bits are the the command:

    public class RestartGameCommand extends Command
    {
    
        override public function execute():void 
        {
            var evt:GameEvent = new GameEvent(GameEvent.GAME_RESTARTED);
            contextView.dispatchEvent(evt);
        } 
    }
    

    And the way the root view adds the context etc and listens for the event:

    public class PyramidGame extends Sprite {
    
        protected const PAUSE_FRAME_COUNT:uint = 25;
    
        protected var _context:PyramidGameContext;
        protected var _contextView:Sprite;
    
        protected var frameCount:uint;
    
        public function PyramidGame() {
            createNewContext();
        } 
    
        protected function listenForRestart():void
        {
            _contextView.addEventListener(GameEvent.GAME_RESTARTED, restartHandler);
        }
    
        protected function restartHandler(e:GameEvent):void
        {
            removeChild(_contextView);
            _contextView = null;
            _context = null;
            restartAfterPause();
        }
    
        protected function restartAfterPause():void
        {
            frameCount = 0;
            addEventListener(Event.ENTER_FRAME, enterFrameHandler);
        }
    
        protected function enterFrameHandler(e:Event):void
        {
            frameCount++;
            if(frameCount > PAUSE_FRAME_COUNT)
            {
                removeEventListener(Event.ENTER_FRAME, enterFrameHandler);
                createNewContext();
            }
        }
    
        protected function createNewContext():void
        {
            _contextView = new Sprite();
            addChild(_contextView);
            _context = new PyramidGameContext(_contextView);
            listenForRestart();
        }
    }
    

    I found that I needed to allow about a second for garbage collection to fully run - that's what the 25 frame pause is for. It actually feels more 'right' as a player as well.

    So - really it's achieving the same but you don't need to use a nasty static public method :)

    Stray

  2. 2 Posted by Weyert on 25 Feb, 2011 10:19 AM

    Weyert's Avatar

    The current approach we have taken at work for console games is to have a RestartGameCommand which will basically display the title screen of the game. On the title screen we have a big start button which will as you can imagine start the game. If the user clicks on the button the GameEvent.START_GAME is getting dispatches which gets handled by the StartGameCommand which update the StatisticsModel and then recreate the game model for the new game session. At the end of the command it will open the appropriate game screen/view.

    As you can see we are currently recycling the application context and only cleanly dispose the views and recreate the game session models and re-initalise it. The statistics model wont get recreated:

      // update the game statistics data
     model.gameStarted = new Date().getTime() / TimeUnits.SECOND;
     model.gameEnded = 0;
     model.gameScore = 0;
     model.gameWon = false;
     model.statTotalGames++;
    
     screenManager.openScreen( GameScreen, true, true ); // opens a new screen (view) 
     dispatch( new GameEvent( GameEvent.GAME_STARTED ) );
    

    The nice thing about this that you can have idle detection in your application mediator and then let it dispatch GameEvent.RESTART_GAME when idling has (n seconds of no user input) which reset the game and show the title screen. You can then even do a forced garbage collection.

  3. 3 Posted by Weyert on 25 Feb, 2011 10:25 AM

    Weyert's Avatar

    The only problem I am experiencing what the best way is to open different screens of the game. Such as, title screen, highscore, help, score submission screen etc. Currently we have a class called ScreenManager which manages the screens. It's doing things as playing a sound effect when switching screens, doing a transitions like cross fades and then add/remove the screens.

    Maybe I should replace the openScreen-method with an event which gets received by the application mediator which calls the screen manager or something.

  4. 4 Posted by erik on 25 Feb, 2011 11:51 AM

    erik's Avatar

    Thanks for all your very swift help guys! Cool thing is that I've already built Stray's method myself, because it triggers a complete fresh game. But it was missing 2 things:
    1) the command dispatching the event
    2) the delay for garbage collection.

    FDT profiler showed the old context and it wasnt GCed, so I'm gonna try that delay now first. Hope it'll work.

    @Weyert: you should take a look at Navigator for AS3. That will solve your navigational issues for sure! You can check it out here: https://github.com/epologee/navigator-as3

  5. 5 Posted by Weyert on 25 Feb, 2011 12:04 PM

    Weyert's Avatar

    Aah yes, we weren't able to use Stray's approach because we need to keep tracking of the played game sessions statistics during the duration the application is running. I think it's a bit daunting to keep saving statistics back to the database before recreating the context and then loading it in again.

    I will have a look at navigator-as3

  6. Support Staff 6 Posted by Stray on 25 Feb, 2011 12:46 PM

    Stray's Avatar

    Nice one Erik - sounds like you were almost there.

    Weyert - In your situation another option would be to pass the data you wish to save out to your parent view, and then send it back in to the new context.

    Eg - make your context constructor like

    public function GameContext(contextView:DisplayObjectContainer, reusableGameObjects:GameObjectsVO, autoStartup:Boolean = true):void
    {
        if(reusableGameObjects == null)
        {
            // do this extra stuff...
        }
    }
    

    Then add a function to be run before the context is disposed

    public function getReusableGameObjects():GameObjectsVO
    {
        // return the stuff you want to use again
    }
    

    Then you pass it back in

    context = new GameContext(contextView, reusableGameObjects);
    

    Personally I don't like conditionals much, so I'd likely have 2 contexts - GameContext and FirstGameContext and FirstGameContext would just extend GameContext and add the extra wiring to get the stats etc that are needed from the server.

    Maybe the way you are doing it is better :) - was just a thought.

  7. 7 Posted by Weyert on 25 Feb, 2011 01:35 PM

    Weyert's Avatar

    Well, easiest way for me to do it ;)

    A bootstrap context and separate application context is an interesting thought!!! The bootstrap context could do all the daunting work like loading the language files, settings files, sound data, asset packages etc. Stuff that only should be loaded once and then afterwards the application context kicks in. You can then recreate the latter context when the game needs to be restarted. The bootstrap context could then just inject utilities or data into the application context. Like the settings and language model from the bootstrap context

    Otherwise you have all kind of issues when settings files are being changes between game sessions e.g. maximum game session duration or score ranges. This would be a bit unfair when you would reload the whole application. Also in off-line games you don't want to reload megs of assets etc. when restarting the game.

  8. 8 Posted by erik on 25 Feb, 2011 04:17 PM

    erik's Avatar

    Hmmmm, restarting seems to work fine, but I've got a problem. When using the FDT profiler, it seems the nullified context and contextView instances aren't getting garbage collected, causing my memory consumption to rise to high levels. When tracing the member values, they return 'null' , but they still don't get garbage collected...

    @stray: did you ever profile this solution yourself when building that pyramid game?

    Is it just friday that I'm missing something?!!?!

  9. 9 Posted by erik on 25 Feb, 2011 04:33 PM

    erik's Avatar

    Little update: Using Mr Doobs Stats class, I get completely different memory results than with the FDT profiler.

    Also, I can't imagine Stray's solution isn't garbage collected since all references are removed in the restart() method. I get the feeling that something isn't working properly in the FDT profiler. Gonna send Meinhard Gredig an email :-)

    Cheers and happy weekend.

  10. 10 Posted by Stray on 25 Feb, 2011 04:55 PM

    Stray's Avatar

    Hi erik,

    I don't use a profiler (I build in TextMate rather than any of the eclipse based monsters) but using the standard mac app profiler I don't find the memory footprint growing over restarts - it seems rather constant.

    Let me know what you find - I can't think of any reason why it wouldn't be GC'd ...

    Stray

  11. Stray closed this discussion on 13 Mar, 2011 08:40 PM.

Comments are currently closed for this discussion. You can start a new one.

Keyboard shortcuts

Generic

? Show this help
ESC Blurs the current field

Comment Form

r Focus the comment reply box
^ + ↩ Submit the comment

You can use Command ⌘ instead of Control ^ on Mac