Patrolling Enemy AI with Phaser

Last blog, I detailed how the bullets were implemented on A Modest Platformer; today, I will be talking about the enemy patrol AI. In the demo, Slimes patrol on platforms without falling over the edges. They also change direction when bumping into other Slimes.

Slime = function (game, x, y, destination) {
    Phaser.Sprite.call(this, game, x, y, "slime");
    game.physics.enable(this, Phaser.Physics.ARCADE);
    this.collideWorldBounds = true;
    this.enableBody = true;
    this.animations.add('right', [0, 1, 2, 3, 4], 5, true);
    this.animations.add('left', [5, 6, 7, 8, 9], 5, true);
    this.body.gravity.y = 800;
    this.body.bounce.y = 0;// 0.7 + Math.random() * 0.2;
    this.body.bounce.x = 1;
    this.body.collideWorldBounds = true;
    this.body.velocity.x = 80;
};
Slime.prototype = Object.create(Phaser.Sprite.prototype);
Slime.prototype.constructor = Slime;

Similar to what we did to the Bullets, we converted the Slimes to extend Sprites. Here, we simply moved all the slime-related initialization from the game’s global create function into the Slime’s constructor.

Slime.prototype.update = function () {

    game.physics.arcade.collide(this, platforms, function (slime, platform) {
        // if slime is moving to the right, 
        // check if its position greater than the width of the platform minus its width
        // if slime is moving to the left, 
        // check if its position exceeds the left-most point of the platform
        if (slime.body.velocity.x > 0 && slime.x > platform.x + (platform.width - slime.width) ||
                slime.body.velocity.x < 0 && slime.x < platform.x) {
            slime.body.velocity.x *= -1; 
        } 
        if (slime.body.velocity.x > 0) {
            slime.animations.play('right');
        } else {
            slime.animations.play('left');
        }
    });

    game.physics.arcade.collide(this, slimes, function (slime, slimes) {
        slime.body.velocity.x *= -1.0001;
    });

};

We also moved all the collision logic which was previously in the global update function into the object’s own update function. It still works the same way, as each Sprite’s update function is called each time the global update function executes.

We’re still using Phaser’s built in collision detection via game.physics.arcade.collide, but with the addition of an anonymous function callback wherein we define how the each object (slime or platform) would react when a collision takes place.

Each time a collision between a Slime and a Platform takes place, we perform an ‘edge check’ to determine if the Slime is about to fall off. We do this by checking the Slime’s position against the position and dimensions of the Platform it is currently colliding with; if it exceeds the left-most or right-most point of the Platform, we reverse the Slime’s direction and update its animation cycle.

For clarity (since I too had problems getting the slimes to go up to the edge before going back), here’s how each ‘edge check’ works:

  • If the slime is moving to the the right (determined by a positive velocity.x)
  • Check if its right-most point exceeds the length of the platform (platform.x + platform.width) minus the width of the slime (slime.width)

It is important to note that slime.x refers to the slime’s left-most point. With the addition of the ‘minus slime.width‘, we are effectively checking if the  slime’s right-most edge is over the platform. You could also modify the check to do (slime.x + slime.width) > (platform.x + platform.width) instead and you’ll end up with the same results. In retrospect, this is much better than what I did for build 2.

  • If the slime is moving to the the left (determined by a negative velocity.x)
  • Check if the the slime’s left-most point is about to exceed the left-most point of the platform (slime.x < platform.x)

This one is definitely much simpler since we didn’t have to translate slime.x to something else.

game.physics.arcade.collide(this, slimes, function (slime, slimes) {
    slime.body.velocity.x *= -1.0001;
});

Handling collisions between slimes is even simpler; we just use the built-in collide function to check if a slime comes into contact with any of the other slimes. When two collide, we simply reverse their direction and their inherent bounciness (which we defined in the constructor) does the rest.

That’s it for A Modest Platformer’s patrol AI. Drop a slime into the game-world, and if it lands on a platform the slime-vs-platform handling does the rest. For the next iteration, we’ll be converting everything to Sprites, and adding a camera which follows the player around the game world.

References

http://www.emanueleferonato.com/tag/drop-wizard/ Emanuele, just in case you see this using your site’s analytics  – thanks!

Resources

The Free Pixel Project, Images used under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License


Leave a Reply

Your email address will not be published. Required fields are marked *