Phaser Coding Tips 4

Published on 30th January 2015

Phaser Coding Tips is a free weekly email - subscribe here.

phaser-tips-header1

Welcome!

This week we carry on building on our set of platformer game tools. Last time we created platforms with specific friction, but this week we're creating platforms you can ride: commonly known as "cloud platforms".

Get the source

I'm only going to highlight the most important parts of the code here. So please always look at the source first. If you've questions about something I didn't cover then ask on the forum. Run / Edit the code on jsbin or codepen or clone the phaser-coding-tips git repo.

lets-code-header

"Cloud" Platforms

A "Cloud platform" is a special kind of platform in your level. It has two distinct characteristics: First it follows its own fixed motion path. And secondly, the player can ride it. For our purposes here is our cloud platform:

cloud-platform

It doesn't have to look like an actual cloud of course! In the Mario games they take on all kind of visual forms, but it helps get the message across for our needs.

CloudPlatform

Because we want to have more than just one of these types of platform they are a good candidate for a custom object: CloudPlatform. This is a special object that extends Phaser.Sprite. Here is its constructor:

CloudPlatform = function (game, x, y, key, group) {

    if (typeof group === 'undefined') { group = game.world; }

    Phaser.Sprite.call(this, game, x, y, key);

    game.physics.arcade.enable(this);

    this.anchor.x = 0.5;

    this.body.customSeparateX = true;
    this.body.customSeparateY = true;
    this.body.allowGravity = false;
    this.body.immovable = true;

    this.playerLocked = false;

    group.add(this);

}; 

You pass in the same parameters as if you were defining a normal Sprite. The difference is that it enables itself for physics, defines an anchor and sets some body properties up. As all CloudPlatforms need the same settings it saves some time to do this here. Here is how we create one:

var cloud1 = new CloudPlatform(this.game, 300, 450, 'cloud-platform', this.clouds);

So far, so good. But they're still just normal sprites. What we need now is to define their motion.

addMotionPath

The CloudPlatform object has a special method called addMotionPath. It expects an array filled with path data:

cloud1.addMotionPath([
    { x: "+200", xSpeed: 2000, xEase: "Linear", y: "-200", ySpeed: 2000, yEase: "Sine.easeIn" },
    { x: "-200", xSpeed: 2000, xEase: "Linear", y: "-200", ySpeed: 2000, yEase: "Sine.easeOut" },
    { x: "-200", xSpeed: 2000, xEase: "Linear", y: "+200", ySpeed: 2000, yEase: "Sine.easeIn" },
    { x: "+200", xSpeed: 2000, xEase: "Linear", y: "+200", ySpeed: 2000, yEase: "Sine.easeOut" }
]);

What it then does is build an internal set of tweens that moves the platform based on its starting coordinates and the data given above. The above code moves the platform in an almost circular path. The faded sprites showing the path the platform took: path Note how the coordinates are given as relative values, i.e. "+200" - this means they are all based off the platforms starting position. If you need to tweak the position you don't have to redo all the motion values too. Here's an example of a vertically moving platform:

cloud2.addMotionPath([
    { x: "+0", xSpeed: 2000, xEase: "Linear", y: "+300", ySpeed: 2000, yEase: "Sine.easeIn" },
    { x: "-0", xSpeed: 2000, xEase: "Linear", y: "-300", ySpeed: 2000, yEase: "Sine.easeOut" }
]);

By default the CloudPlatform runs on a loop. So in the example above it moves down by 300 pixels (using Sine.easeIn) and then moves back up by 300 pixels as well.

Locking the Player

The cloud platforms are checked in the update loop via a normal collide call:

this.physics.arcade.collide(this.player, this.clouds, this.customSep, null, this);

The difference is that we've got a collision callback handler customSep which looks like this:

customSep: function (player, platform) {

    if (!this.locked && player.body.velocity.y > 0)
    {
        this.locked = true;
        this.lockedTo = platform;
        platform.playerLocked = true;

        player.body.velocity.y = 0;
    }

},

We test the velocity. If the player is in a jump and falling down (> 0) and not currently locked to a platform then we tell the game the player is now locked. The platform he landed on is stored in lockedTo and his velocity is reset. Why do we lock him? The reason is that the platform is being moved by a tween, not by any kind of velocity or physics operation. It's following a fixed path, so we need to ensure that the player is repositioned to match that path every frame. This is done in the preRender function:

preRender: function () {

    if (this.game.paused)
    {
        //  Because preRender still runs even if your game pauses!
        return;
    }

    if (this.locked || this.wasLocked)
    {
        this.player.x += this.lockedTo.deltaX;
        this.player.y = this.lockedTo.y - 48;

        if (this.player.body.velocity.x !== 0)
        {
            this.player.body.velocity.y = 0;
        }
    }
}
...

The deltaX of the platform is added to the players x position. While its y position is adjusted to match the top of the platform (lockedTo.y). The -48 is just the height of the player sprite. The player is still free to run around. But that all takes place as part of the update and postUpdate parts of the game loop. And in preRender we adjust him based on the platform he's locked to.

checkLock and cancelLock

We need to free the player from the platform in 2 cases: 1) He jumps and 2) He walks off the edge:

checkLock: function () {

    this.player.body.velocity.y = 0;

    if (this.player.body.right < this.lockedTo.body.x || this.player.body.x > this.lockedTo.body.right)
    {
        this.cancelLock();
    }

},

If the player walks off the side of the platform then we no longer consider them locked to it. Equally the jump function cancels the lock as well. This works well for specific case, but you may want to tailor this action for your needs.

platforms Run the code to play this demo.

Parallax Background

Last week we used a single TileSprite to create a nice scrolling effect in the background. This week we're using two :) In the create method you'll see we make background and trees. These are two horizontally repeating images that we wish to scroll at different rates as the camera moves:

this.background.tilePosition.x = -(this.camera.x * 0.7);
this.trees.tilePosition.x = -(this.camera.x * 0.9);

With the above values the background (i.e. the sky layer) will scroll slower than the trees. But both scroll slower than the actual camera does, giving a nice 3-way parallax effect as you traverse the level.

Brain Food

Platforms like this are really useful, however here are some ideas to enhance this approach: * Rather than use the deltaX value you could drop an anchor when the player lands on the platform. Then adjust it as they move, but use its coordinate in preRender to re-position the player. This will give for a smoother end result as you won't be dealing with floats and rounding. * Right now all the platforms start moving automatically, but they have a start method. You could activate this the moment the player lands on the platform, i.e. it moves on contact. * Equally the platforms don't have to run on a loop. They could simply move from A to B and then stop. This requires careful level design, but is often seen in platform games.

this-week-header

Here is what's new in the Phaser world this week:

 Phaser 3 Development Log 2 Bunnies, instances and text  Speed-up Phaser Group vs. Group Collision With this 1D Sort  Cómo Crear un Juego Móvil con HTML5 y Cordova (Spanish)  Understanding hexagonal tiles By Emanuele Feronato  Digicat the Thief Steal the diamonds in this cute open-source game  Discover Phaser A French multi-part series  Lava Run - open-source endless runner By OttoRobba  Phaser + RequireJS and Underscore Template npm install and deploy * Making Games with Phaser From Philly Gameworks Got something you'd like featured? Get in touch.

feedback-header

If you've questions or comments about the code then please ask. Equally if there are things you'd like me to write about then tell me! Until next week, happy coding. Discuss: HTML5 Game Devs Forum Tweet @photonstorm (hashtag #phaserjs)

Phaser Coding Tips is a free weekly email - subscribe here.

phaser-tips-header1

Welcome!

This week we carry on building on our set of platformer game tools. Last time we created platforms with specific friction, but this week we're creating platforms you can ride: commonly known as "cloud platforms".

Get the source

I'm only going to highlight the most important parts of the code here. So please always look at the source first. If you've questions about something I didn't cover then ask on the forum. Run / Edit the code on jsbin or codepen or clone the phaser-coding-tips git repo.

lets-code-header

"Cloud" Platforms

A "Cloud platform" is a special kind of platform in your level. It has two distinct characteristics: First it follows its own fixed motion path. And secondly, the player can ride it. For our purposes here is our cloud platform:

cloud-platform

It doesn't have to look like an actual cloud of course! In the Mario games they take on all kind of visual forms, but it helps get the message across for our needs.

CloudPlatform

Because we want to have more than just one of these types of platform they are a good candidate for a custom object: CloudPlatform. This is a special object that extends Phaser.Sprite. Here is its constructor:

CloudPlatform = function (game, x, y, key, group) {

    if (typeof group === 'undefined') { group = game.world; }

    Phaser.Sprite.call(this, game, x, y, key);

    game.physics.arcade.enable(this);

    this.anchor.x = 0.5;

    this.body.customSeparateX = true;
    this.body.customSeparateY = true;
    this.body.allowGravity = false;
    this.body.immovable = true;

    this.playerLocked = false;

    group.add(this);

}; 

You pass in the same parameters as if you were defining a normal Sprite. The difference is that it enables itself for physics, defines an anchor and sets some body properties up. As all CloudPlatforms need the same settings it saves some time to do this here. Here is how we create one:

var cloud1 = new CloudPlatform(this.game, 300, 450, 'cloud-platform', this.clouds);

So far, so good. But they're still just normal sprites. What we need now is to define their motion.

addMotionPath

The CloudPlatform object has a special method called addMotionPath. It expects an array filled with path data:

cloud1.addMotionPath([
    { x: "+200", xSpeed: 2000, xEase: "Linear", y: "-200", ySpeed: 2000, yEase: "Sine.easeIn" },
    { x: "-200", xSpeed: 2000, xEase: "Linear", y: "-200", ySpeed: 2000, yEase: "Sine.easeOut" },
    { x: "-200", xSpeed: 2000, xEase: "Linear", y: "+200", ySpeed: 2000, yEase: "Sine.easeIn" },
    { x: "+200", xSpeed: 2000, xEase: "Linear", y: "+200", ySpeed: 2000, yEase: "Sine.easeOut" }
]);

What it then does is build an internal set of tweens that moves the platform based on its starting coordinates and the data given above. The above code moves the platform in an almost circular path. The faded sprites showing the path the platform took: path Note how the coordinates are given as relative values, i.e. "+200" - this means they are all based off the platforms starting position. If you need to tweak the position you don't have to redo all the motion values too. Here's an example of a vertically moving platform:

cloud2.addMotionPath([
    { x: "+0", xSpeed: 2000, xEase: "Linear", y: "+300", ySpeed: 2000, yEase: "Sine.easeIn" },
    { x: "-0", xSpeed: 2000, xEase: "Linear", y: "-300", ySpeed: 2000, yEase: "Sine.easeOut" }
]);

By default the CloudPlatform runs on a loop. So in the example above it moves down by 300 pixels (using Sine.easeIn) and then moves back up by 300 pixels as well.

Locking the Player

The cloud platforms are checked in the update loop via a normal collide call:

this.physics.arcade.collide(this.player, this.clouds, this.customSep, null, this);

The difference is that we've got a collision callback handler customSep which looks like this:

customSep: function (player, platform) {

    if (!this.locked && player.body.velocity.y > 0)
    {
        this.locked = true;
        this.lockedTo = platform;
        platform.playerLocked = true;

        player.body.velocity.y = 0;
    }

},

We test the velocity. If the player is in a jump and falling down (> 0) and not currently locked to a platform then we tell the game the player is now locked. The platform he landed on is stored in lockedTo and his velocity is reset. Why do we lock him? The reason is that the platform is being moved by a tween, not by any kind of velocity or physics operation. It's following a fixed path, so we need to ensure that the player is repositioned to match that path every frame. This is done in the preRender function:

preRender: function () {

    if (this.game.paused)
    {
        //  Because preRender still runs even if your game pauses!
        return;
    }

    if (this.locked || this.wasLocked)
    {
        this.player.x += this.lockedTo.deltaX;
        this.player.y = this.lockedTo.y - 48;

        if (this.player.body.velocity.x !== 0)
        {
            this.player.body.velocity.y = 0;
        }
    }
}
...

The deltaX of the platform is added to the players x position. While its y position is adjusted to match the top of the platform (lockedTo.y). The -48 is just the height of the player sprite. The player is still free to run around. But that all takes place as part of the update and postUpdate parts of the game loop. And in preRender we adjust him based on the platform he's locked to.

checkLock and cancelLock

We need to free the player from the platform in 2 cases: 1) He jumps and 2) He walks off the edge:

checkLock: function () {

    this.player.body.velocity.y = 0;

    if (this.player.body.right < this.lockedTo.body.x || this.player.body.x > this.lockedTo.body.right)
    {
        this.cancelLock();
    }

},

If the player walks off the side of the platform then we no longer consider them locked to it. Equally the jump function cancels the lock as well. This works well for specific case, but you may want to tailor this action for your needs.

platforms Run the code to play this demo.

Parallax Background

Last week we used a single TileSprite to create a nice scrolling effect in the background. This week we're using two :) In the create method you'll see we make background and trees. These are two horizontally repeating images that we wish to scroll at different rates as the camera moves:

this.background.tilePosition.x = -(this.camera.x * 0.7);
this.trees.tilePosition.x = -(this.camera.x * 0.9);

With the above values the background (i.e. the sky layer) will scroll slower than the trees. But both scroll slower than the actual camera does, giving a nice 3-way parallax effect as you traverse the level.

Brain Food

Platforms like this are really useful, however here are some ideas to enhance this approach: * Rather than use the deltaX value you could drop an anchor when the player lands on the platform. Then adjust it as they move, but use its coordinate in preRender to re-position the player. This will give for a smoother end result as you won't be dealing with floats and rounding. * Right now all the platforms start moving automatically, but they have a start method. You could activate this the moment the player lands on the platform, i.e. it moves on contact. * Equally the platforms don't have to run on a loop. They could simply move from A to B and then stop. This requires careful level design, but is often seen in platform games.

this-week-header

Here is what's new in the Phaser world this week:

 Phaser 3 Development Log 2 Bunnies, instances and text  Speed-up Phaser Group vs. Group Collision With this 1D Sort  Cómo Crear un Juego Móvil con HTML5 y Cordova (Spanish)  Understanding hexagonal tiles By Emanuele Feronato  Digicat the Thief Steal the diamonds in this cute open-source game  Discover Phaser A French multi-part series  Lava Run - open-source endless runner By OttoRobba  Phaser + RequireJS and Underscore Template npm install and deploy * Making Games with Phaser From Philly Gameworks Got something you'd like featured? Get in touch.

feedback-header

If you've questions or comments about the code then please ask. Equally if there are things you'd like me to write about then tell me! Until next week, happy coding. Discuss: HTML5 Game Devs Forum Tweet @photonstorm (hashtag #phaserjs)