Wednesday, July 15, 2015

Phaser Isometric Alternative

After my first experience with a Phaser game using the Isometric plugin, I was troubled. I like isometric-style games, but this particular game wasn't really benefiting a whole lot from the isometric view. The performance issues on mobile and the bug with the tree boundaries also bothered me. I was wondering if I could create the same kind of game without using any plugins.

So of course I tried it.

And it actually worked pretty well.

First of all, I updated my game template. In Monday's example, I created the methods for the game scene with object prototypes but I have since learned that to be unnecessary. In my new format, I pass an object into the Phaser.Game method that specifies preload, create, update, and render functions.

Here is basically the same frog soccer game, but without the Isometric plugin.

var width = 800;
var height = 600;

var init = function () {

    var game = new Phaser.Game(width, height, Phaser.AUTO, 'test', {
        preload: preload,
        create: create,
        update: update,
        render: render
    });

    function preload() {

        game.load.image('tree2', 'images/tree2.png');
        game.load.image('ball', 'images/ball.png');
        game.load.image('frog', 'images/frog.png');

    }

    var frog;
    var tree;
    var ball;
    var group;
    var cursors;

    function create() {

        group = game.add.group();

        game.stage.backgroundColor = "0x409d5a";

        frog = group.create(200, 200, 'frog');
        game.physics.enable(frog, Phaser.Physics.ARCADE);
        frog.body.drag.set(300);
        frog.body.setSize(60, 25, 0, 42);
        frog.body.collideWorldBounds = true;

        tree = group.create(100, 300, 'tree2');
        game.physics.enable(tree, Phaser.Physics.ARCADE);
        tree.body.setSize(79, 25, 0, 98);
        tree.body.immovable = true;
  
        ball = group.create(300, 300, 'ball');
        game.physics.enable(ball, Phaser.Physics.ARCADE);
        ball.body.bounce.set(1);
        ball.body.drag.set(20);
        ball.body.setSize(45, 35, 0, 8);
        ball.body.collideWorldBounds = true;
        
        group.sort();

        cursors = game.input.keyboard.createCursorKeys(); 

    }

    function update() {

        if (cursors.left.isDown) {
            frog.body.velocity.x = -150;
        } else if (cursors.right.isDown) {
            frog.body.velocity.x = 150;
        } else if (cursors.up.isDown) {
            frog.body.velocity.y = -150;
        } else if (cursors.down.isDown) {
            frog.body.velocity.y = 150;
        }

        group.sort('bottom', Phaser.Group.SORT_ASCENDING);
        game.physics.arcade.collide(group);

    }

    function render() {

        //game.debug.body(frog); // un-comment to see the boxes
        //game.debug.body(ball);
        //game.debug.body(tree);

    }

};

window.onload = init;

As I mentioned, the new game constructor format is on lines 6 through 11. The functions that implement the parts of the scene appear below it.

On line 13 is the preload function. This function is very simple, as it just loads the three sprite images and assigns them reasonably named keys.

Lines 21 to 25 declare some variables that need to be referenced in both the create and update functions.

The create function, on lines 27 through 55, is very similar to the one from the isometric version of the game. There are minor differences in how the group and sprites are created. On lines 33, 39, and 44, I create sprites directly in the group object by calling group.create and passing in the coordinates and the image key.

The calls to .body.setSize that appear on lines 36, 41, and 48 reduce the size of the physics collision areas of the sprites to boxes that approximate their footprint if they existed in 3 dimensions instead of 2. This is very important to make the game appear like it did in the isometric version. With the collision boxes confined to the lower portions of the sprite images, it can appear as though the frog can walk behind the tree but not through the tree. The following image, with the bounding box debug turned on, illustrates the issue.



What ensures that as the sprites move around they get ordered in such a way as to continue this illusion of the third dimension? The magic is on line 82, in the update function. The call to group.sort re-orders the z-value for all the sprites so that the correct ones are on top. It takes two parameters - the first is the field on the sprite that will be used to order the sprites and the second is the sort order. As you can see, I specify the field bottom as the value on which to sort. As soon as the bottom of a sprite goes lower than another sprite, it moves to the front. This successfully recreates the same effect I was getting using the Isometric plugin.

The rest of the update function, on lines 57 - 72, just handles setting the frog's velocity based on the cursor keys and doing the collision check.

The render function can be left blank, or optionally turn on collision box visualization.

My second attempt at this Phaser game has turned out better, in my opinion. I think I am going to move forward with this one instead of my first attempt. I left out the grass tiles today but I intend to add them back in, as well as add appropriate animation and directional images for the frog. I should have more progress to report on Friday!

Soon, you'll be able to play this game where you control a frog hopping through a world that looks just like the comic.

Amphibian.com comic for 15 July 2015