Friday, December 4, 2015

Better Encapsulation of Enemy Logic

Remember on Wednesday when I wrote about adding some actual behavior to the enemies in the platformer I'm making with Phaser? I said that it bothered me how the code which defined the toad jumping behavior was applied to everything in the enemies group. It prohibited me from adding different kinds of enemies. I couldn't stop thinking about it, and came up with a solution.

What I needed was better encapsulation of the enemy logic. Instead of the update function having to know how to deal with all different types of enemies, the enemies should know how to deal with themselves. I shouldn't need a jumpToad function global to my game - I should just need to add a jump function to the toad objects. Not all enemies will jump...but all enemies will be updated. The answer: make sure all enemies are given an update function that does whatever makes sense for that enemy. With Phaser, the game loop automatically calls update (if defined) on all Sprite objects. So I have to do is to add enemy update functions and everybody takes care of themselves.

Let's take a look at it. First, I added the setupToad function that will take a Phaser Sprite object and add in the toad-specific functions. This must include update, but can be whatever else is needed - private variables, other functions, etc. If I add another type of enemy, let's say moles, I would add a setupMole function that gives Sprites mole-specific stuff.

function setupToad(obj) {

    game.physics.enable(obj, Phaser.Physics.ARCADE);
    obj.body.setSize(60, 25, 0, 38);

    obj.jumping = true;

    obj.jump = function() {
        this.frame = 1;
        this.body.velocity.y = -600;
        this.jumping = true;
    };

    // will be called by Phaser with each 
    // game loop update
    obj.update = function() {

        if (this.body.onFloor() && this.jumping) {
            this.jumping = false;
            this.frame = 0;
            game.time.events.add(5000, function() {
                this.jump();
            }, this);
        }

    };

}

As you can see above, I give toad objects an update function, as well as a jump function. I also give them a jumping flag. The update function uses that flag and other Phaser functions to call jump under certain conditions. All this code was spread around before, but now it is all contained right here.

The process of creating toads looks much simpler:

function create() {

    // ... other stuff ...

    var toads = game.add.group();
    map.createFromObjects('others', 6571, 'toad', 0, true, false, toads);

    toads.forEach(function(t) {
        setupToad(t);
    }, this);

    enemies.addMultiple(toads);

    // ... other stuff ...

}

The best part is that I can create more types of enemies and the update function won't change at all. The game plays exactly the same as it did before, which you can prove to yourself by playing it here: http://amphibian.com/eight-bit/. You can also view the complete source code on GitHub.

Also, you should look at today's comic.

Amphibian.com comic for 4 December 2015