« Back to Blog

Phaser Platformer Series: 20 Collapsing Platforms

posted on 23 July 2021

Last tutorial we made one-way platforms. We add to that to create some collapsing platforms. These will start to wobble once our hero lands on them, and after a short amount of time they collapse and are destroyed forever.

Here is what we’ll be making (the collapsing platforms are on the far right of the level):

See the Pen
Phaser Platformer Series: 20 Collapsing platforms
by Digitherium (@digitherium)
on CodePen.

If you are viewing on a mobile, you can open a version of it here without all the codepen chrome taking up screen space so you play it in landscape mode.

First, we need to add a new global variable to store our collapsing platform sprites group.

collapsingPlatforms,

We’ll be using a different sprite for this type of platform and as usual, we need to it to the preload function:

this.load.image('collapsing-platform', 'collapsing-platform.jpg');

Next we create the collapsing platforms group and add our sprites in create:

collapsingPlatforms = this.physics.add.staticGroup();
collapsingPlatforms.create(1270, 290, 'collapsing-platform');
collapsingPlatforms.create(1180, 256, 'collapsing-platform');
collapsingPlatforms.create(1093, 222, 'collapsing-platform');

As usual, we are just placing the sprites by eye and giving them the correct x and y coordinates. We also need to add our collider between the player and this group:

this.physics.add.collider(player, collapsingPlatforms, shakePlatform, checkOneWay, this);

This time we are using two callbacks. We want these platforms to be one way as well as collapsing so we are still passing the checkOneWay function to be used as the processCallback. This ensures our hero can jump onto them from below. We also use the collideCallback, passing it the function shakePlatform. That looks like this:

function shakePlatform(player, platform) {
    //only make platform shake if player is standing on it
    if (player.body.blocked.down) {
        //do a little camera shake to indicate something bad is going to happen
        this.cameras.main.shake(200, 0.001);
        //we need to store the global scope here so we can keep it later
        var ourScene = this;
        //do a yoyo tween shaking the platform back and forth and up and down
        var tween = this.tweens.add({
            targets: platform,
            yoyo: true,
            repeat: 10,
            x: {
                from: platform.x,
                to: platform.x + 2 * 1,
            },
            ease: 'Linear',
            duration: 50,
            onComplete: function() {
                destroyPlatform.call(ourScene, platform);
            }
        });
    }
}

The first thing we do here is to check that the player is actually standing on the platform with if (player.body.blocked.down) as we only want the platform to collapse if it’s stood on. Our checkOneWay callback looks at the height of the player and only allows this shakePlatform function to run if they are higher than it, so it can be jumped onto from below. It may seem that checking the player is standing on the platform is an unnecessary check if we are already checkOneWay, but it helps us deal with some edge cases. For instance, the player might be overlapping with the platform, jump higher than it, but not actually land on it as they move to the side at the last second. This extra check ensures that it’s only when the player stands on the platform that its destruction is triggered.

Next, we add a little camera shake just for a bit of juicing and to make it obvious something is about to happen. We also play an animation of the platform shaking. On complete of this, we call another function destroyPlatform that will perform the animation of the platform actually collapsing and being destroyed. In order to call this function successfully and to nicely chain our tweens, we are storing the scope of ‘this’ in a variable called ourScene.

//we need to store the global scope here so we can keep it later
var ourScene = this;

This takes us off on a little scope tangent…

All Phaser games are made up of ‘scenes’, and in these basic tutorials, we’ve only ever been using one scene for our whole game. In a more complex game (or even just one that is less of a demo) you might have a scene for the main menu, a scene for the level select, a scene for the game, and a scene for the end screen etc. Scene management is a whole other topic, but when your phaser game just consists of one scene only, you can ignore it.

That’s exactly what happens in most Phaser demos. To hide a bit of the complexity and the sheer number of ways you can code stuff in Phaser, they just use a single scene, and you’ll be used to seeing the same three main functions preload, create, and update. You define these in a config, create a game, and off you go. That’s the bare minimum you need to create a Phaser game.

Phaser makes a single scene using the three functions you passed in. When we are in any of these three main functions the keyword this refers to the scene. Through this we can get to tweens, physics, and all sorts of info about our scene. These are essential all the things we need to know about our game, but it’s actually the scene we are getting this info from.

If you think about it, from one level to the next, and certainly from the title screen to the level itself, you need different things. One scene might need physics, another might not. So when we think of game-related things like physics, tweens, etc, they are actually on a scene level, not a game level.

In all our code so far, this has referred to the scene in all our main functions (preload, create, update) and likewise in any callback functions setup using the built-in arcade physics colliders and overlap functions.

The big issue for us, is that in some of our callback functions such as shellHitBaddie, we then call *another* function that is nothing to do with Phaser, and those functions have no idea what this refers to!

Let’s take shellHitBaddie as an example. When a shell hits a baddie, we tween the baddies death animation and the shell destruction animation. The shell destruction animation/tween is used in several places… when a shell hits a baddie, but also if a shell hits a spike or a wall. Due to this, we moved it into its own function destroyShell. This means that the tween that plays on the destruction of a shell is not in the shellHitBaddie function but in a function we call from there, called destroyShell.

The destroyShell function we created isn’t some built in Phaser function or a callback from a collider. It has no context of the scene and it doesn’t know what this is! If we tried to create a tween in destroyShell without the scope sorted it wouldn’t work because of this line:

var destroyShell = this.tweens.add({

That would fall over. It doesn’t know what this refers to, so this.tweens doesn’t exist! To fix this we have to pass in the scope i.e. we tell the function that this = our scene. One way of doing this in javascript is to use the call function. This lets you trigger a function AND specify what this should refer to inside of that function. In shellHitBaddie we use call to pass the scope of our scene (stored in this) to destroyShell like this:

destroyShell.call(this);

So we’re made it so in our brand new function unrelated to Phaser it still understands this.tweens.add because it knows the this = our scene. Phew. Going back to the collapsing platform code and scope:

function shakePlatform(player, platform) {
    //only make platform shake if player is standing on it
    if (player.body.blocked.down) {
        //do a little camera shake to indicate something bad is going to happen
        this.cameras.main.shake(200, 0.001);
        //we need to store the global scope here so we can keep it later
        var ourScene = this;
        //do a yoyo tween shaking the platform back and forth and up and down
        var tween = this.tweens.add({
            targets: platform,
            yoyo: true,
            repeat: 10,
            x: {
                from: platform.x,
                to: platform.x + 2 * 1,
            },
            ease: 'Linear',
            duration: 50,
            onComplete: function() {
                destroyPlatform.call(ourScene, platform);
            }
        });
    }
}

We store this in a variable called ourScene. That is storing a reference to the scene for us. Notice that we do this before tweening the platform. When we call a tween, we fire an onComplete function when it’s done. Inside this onComplete, the scope of this has actually changed! It no longer refers to the scene, but to the tween itself. So we store our reference to the scene before the tween, because this is about to change. We couldn’t just say:

destroyPlatform.call(this, platform);

Because this now refers to the tween, and when destroyPlatform tries to add a new tween by accessing the tweens property in our scene, the this.tweens property won’t exist.

So we end up having to store the value of this even before we start tweening and we pass that to our call function instead, along with the platform the player has just stood on. Once in destroyPlatform this now refers to our scene, just like it is in every other Phaser function in our code. destroyPlatform looks like this:

function destroyPlatform(platform) {
    var tween = this.tweens.add({
        targets: platform,
        alpha: 0,
        y: "+=25",
        ease: 'Linear',
        duration: 100,
        onComplete: function() {
            destroyGameObject(platform);
        }
    });
}

So yes, we went through all that just so we can do tweens outside of the original callback! Notice how this last tween to fade out the platform has a callack and fires destroyGameObject on completion BUT we don’t bother using call here or trying to pass the scope through to destroyGameObject. We don’t do that, because we don’t need anything from scene. The scope just isn’t important as destroyGameObject doesn’t ever refer to anything else in the scene! It just takes in one parameter of the game object, be that a shell, or platform (any sprite basically) and calls that game objects built-in destroy() function. It’s self contained – it doesn’t need to access the physics or tweens of the scene, it has everything it needs with that one parameter.

Wow, quite a journey! The code for a collapsing platform really wasn’t that dissimilar to the one-way platform of the previous tutorial but the concepts around it are getting more involved. We’re getting more into the guts of Phaser now, and if we’re ever going to expand this little demo into a more fully-fledged game, we need to take a look at scenes.

chromeless mobile version
view all the code on codepen
download the source on github.