Wednesday, February 3, 2016

Ride Moving Platforms in a Phaser Game

Another staple of classic platformers that my Phaser game has been missing is the moving platform. You know, those floating platforms that go up and down or side to side and upon which you can ride? I had to add some. It wasn't that hard, but there were some tricks to it.

This is the frog's new cloud platform.

First, I had to add the sprites for the moving platforms. I did this by adding them to my map in Tiled as objects, the same way I added the enemies (see that blog post here).

platforms = game.add.group();
platforms.enableBody = true;
map.createFromObjects('others', 6573, 'cloud-blue', 0, true, false, platforms);
platforms.forEach(function(p) {
    
    game.physics.enable(p, Phaser.Physics.ARCADE);
    p.body.allowGravity = false;
    p.body.immovable = true;
    
    var t = this.add.tween(p.position);
    t.to({ y: p.position.y - 200 }, 3000, Phaser.Easing.Linear.None, true, 0, -1, true);
    
}, this);

Every object with id 6573 was assigned the "cloud-blue" sprite image key and put in the platforms group. Then I iterate over the group to set them all up with physics so they can collide with the frog. I turn off gravity on them so they don't fall off the screen, and I set body.immovable to true. This isn't to say that they can't move - it just means that they won't react to being pushed down when the frog lands on them. The movement of the platforms is controlled by Tweens. In the above code snippet, all of the platforms are given the same Tween. This one example Tween will move the platform up 200 pixels from its start position in 3 seconds and then loop forever. In my finished game I'll use different Tweens for different platforms.

After that, adding a collision check in the update function between the frog and the platforms group was all it took to make the platforms solid.

I now had moving platforms upon which the frog could land, but the behavior wasn't correct. As the platform moved up it would push the frog up with it, but on the way down the frog would keep falling and hitting the platform over and over again. It didn't look like he was riding it. The solution was to create a special collision callback function which "locks" the frog to the platform upon which he stands.

function platformSep(s, platform) {

    if (!s.locked) {
        s.locked = true;
        s.lockedTo = platform;
        s.body.velocity.y = 0;
    }

}

function update() {

    this.physics.arcade.collide(frog, platforms, platformSep, null, this);

    // ... other stuff ...

    if (frog.locked) {
        if (frog.body.right < frog.lockedTo.body.x || frog.body.x > frog.lockedTo.body.right) {
            frog.locked = false;
            frog.lockedTo = null;
        } else {
            frog.x += frog.lockedTo.deltaX;
            frog.y += frog.lockedTo.deltaY;
        }
    }

    // ... other stuff ...

}

Now, when the frog and a platform are in collision, the frog will get a locked flag set to true, and the lockedTo field will be set to the platform sprite on which he stands. The frog's y-velocity is also set to 0 here to stop any potential falling that might occur otherwise.

Once I had these additional fields, I could perform a check to see if the frog is currently locked to a platform. If he is, but his current position says that he's walked off of it, I cancel the lock and his behavior goes back to normal. Otherwise, the frog's position gets updated to match the movement deltas of the platform. This keeps him riding it, no matter which in direction it moves.

These moving platforms really add a lot of classic platformer feel to the game, and thanks to Phaser they weren't too difficult to add. Remember, you can see the complete source code to the game on GitHub and play the current version at this link!

There are some major upgrades going on in the comic today, so don't forget to read it!

Amphibian.com comic for 3 February 2016