Friday, December 18, 2015

Using Game States in Phaser

The game's (temporary) Title Screen
If you've tried playing the sandbox level for the 8-bit style platformer I've been building with Phaser, you have seen that it just drops you right into the stage. There's no splash screen or level selector or how-to-play instructions. It's just *BAM* do something! Today I decided to change that.

Making different types of screens with Phaser means working with different States. What is a State? In its simplest form, a State is just a set of functions - preload, create, update, and (optionally) render. When you first switch to a State, preload and create are called. Then update is called continuously until you do something to change the State. If you create a very simple game (like mine has been so far) you might only have one State and you might not even call it that - you can just pass in an anonymous State object when you create the game!

Here's what I mean... Look at this example of what my code was like before:

function preload() {
    // ... do preload stuff ...
}

function create() {
    // ... do create stuff ...
}

function update() {
    // ... do update stuff ...
}

// ... define a bunch of other functions and variables ...

var game = new Phaser.Game(width, height, Phaser.CANVAS, "gameArea", {
    preload: preload,
    create: create,
    update: update
});

Basically, I was creating a State object inline with the call to the Game constructor. It was a simple object that defined the 4 functions I mentioned previously. As soon as the Game was created, this State started running. It's possible to create multiple States using different combinations of globally-defined functions, like the code above, but that would get ugly fast. I know this isn't exactly OO, but I still want to practice good encapsulation. I want separation of concerns.

What did I do? I created two functions which generate the two different states! One for the simple title screen and one for the game as it has been up to this point. The title screen state just shows a single image and waits for the player to push the 'S' key. Once pushed, the update function in the title state changes the game state to the playable sandbox state.

Here's a much simplified version of the code:

/**
 * Creates the game state which defines the title screen.
 */
function titleState() {

    var keys;
    var splash;
    
    function preload() {
        this.load.pack("main", "assets/titlePack.json");
    }
    
    function create() {
        splash = this.add.image(0, 0, "title");
        keys = this.input.keyboard.addKeys({
            's'  : Phaser.KeyCode.S
        });
    }
    
    function update() {
        if (keys.s.isDown) {
            this.state.start("sandbox");
        }
    }
    
    return {
        preload: preload,
        create: create,
        update: update
    };
    
}

/**
 * Creates the game state which defines the playable level.
 */
function playingState() {

    // ... define private variables ...
    
    function preload() {
        this.load.pack("main", "assets/pack.json");
    }
    
    function create() {
        // ... all the game creation stuff ...
    }
    
    function update() {
        // ... all the game update stuff ...
    }
    
    return {
        preload: preload,
        create: create,
        update: update
    };
    
}

// Now we create the states and the game

var title = titleState();
var sandbox = playingState();

var game = new Phaser.Game(width, height, Phaser.CANVAS, "gameArea", title);

game.state.add("sandbox", sandbox);

The "main" game setup is much simpler now - it just creates the state objects and then the Game. The game starts with title as the running state, but adds the sandbox state with the key "sandbox." The key is important because that's what you have to use when you want to switch states.

The other thing to note is that in the State preload, create, and update functions, the this variable will refer to the Game object. That fact becomes important when you are creating in-game objects in these functions.

Check out the game with its fancy new title screen here: http://amphibian.com/eight-bit/, and visit the project page on GitHub if you'd like to see the complete source code. Over at Amphibian.com, today is Chapter 2 of my Christmas story - give it a quick read!

Amphibian.com comic for 18 December 2015

No comments:

Post a Comment