HTML5 Canvas Game Programming Lesson 4

Press ESC to pause / unpause, and Enter, Space or click while the box is falling to make it jump back up.

Menus and Game Flow

And this is the JavaScript file, js/Lesson.js:

var MainGame = null;
var MainMenu = null;

function StartGame()
{
    GameLoopManager.stop();
    MainMenu = null;
    MainGame = new Game();
    // Async load audio and images, start gameplay when loaded
    MultiStepLoader( [
        [ "audio", function(cb, i) {
            AudioManager.load({
                'ping'   : 'sound/guitar',
                'jump'   : 'sound/jump',
                'bounce' : 'sound/bounce1'
            }, function() {
                cb(i); } ) } ],
        [ "images", function(cb, i) {
            LoadImages(MainGame.images, {
                sun: "images/sun.png"
            }, function() {
                cb(i); } ) } ],
    ], function() {
        // All done, go!
        InputManager.reset();
        GameLoopManager.run(function(elapsed) { MainGame.Tick(elapsed); });
    } );
}

function StartMainMenu()
{
    GameLoopManager.stop();
    MainGame = null;
    // Async load audio and start menu when loaded
    MultiStepLoader( [
        [ "audio", function(cb, i) {
            AudioManager.load({
                'blip'   : 'sound/blip',
                'select' : 'sound/select'
            }, function() {
                cb(i); } ) } ],
    ], function() {
        // All done, go!
        InputManager.reset();
        MainMenu = new Menu("Lesson 4",
                [ "Play", "Settings", "Help", "Credits" ],
                "(C) Copyright 2011 by Javier Arevalo",
                70, 50, 400,
                function(numItem) { if (numItem == 0) StartGame(); },
                null);
        GameLoopManager.run(function(elapsed) { MainMenu.Tick(elapsed); });
    } );
}

Here you can see how we use the MultiStepLoader() utility function to perform a series of asynchronous operations, each of which is programmed to run a callback when it is complete. Then, when all the steps are done, the function will call another callback. Before we start the loading process for the game or the menu, we stop the GameLoopManager so that there's no per-frame code running, and we destroy the other object (game or menu). Note that we don't physically destroy the object, just assign null to its variable so the object will be garbage-collected when JavaScript deems it appropriate.

The syntax for the parameters to the MultiStepLoader() function can be quite convoluted, but with proper indentation it is easy to follow - and you won't be writing many of these calls in a game. The first parameter to this function is an array of steps. Each step is itself an array of two elements: the first is a name for the step (printed in the JavaScript console log) and the second is a function that gets called to perform the step. Normally this function will just be a call to another async-loading function (like AudioManager.load()) that receives a callback. Inside the callback for each step, we should call the callback 'cb(i) that we are given as a parameter for the step. This crazy callback-inside-callback scheme lets the MultiStepLoader() function be informed of which steps have been completed.

One peculiarity is that the MainMenu object is created after all async loading operations are finished, whereas the MainGame object is created before async loading. There's no particular reason for either in this case, but it's an interesting point to consider: should you load all assets and only then create the object, or should you create your object so assets can be loaded into it? For the menu, the assets are loaded globally inside the AudioManager, so the MainMenu object doesn't need to exist. However, for a more complex object like the MainGame (it is simple now but will become very complex in a real game), it's likely that we will need to load game-specific assets like the map.