« Back to Blog

Phaser Platformer Series: 10 Ladders

posted on 14 May 2021

Ladders are a classic mechanic in platformers. If our player is standing at the foot of the ladder and they press up, then they should climb the ladder instead of jumping. In our version, if the player jumps across a ladder they will land on it at that point. This allows players to jump onto ladders and then climb. It’s not the most realistic way to do it (would really hurt the ol’ arms) but I think it makes the game more fun to play.
Here’s what we’ll be making:

See the Pen
Phaser Platformer: 10 Ladders
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.

Firstly we’re going to add a global var to store our ladder (or ladders as the case may be later).

ladders,

and a flag to store whether our hero is currently on a ladder for our game loop

onLadder = false,

Then we add the sprite to our preload function:

this.load.image('ladder', 'ladder.png');

In our create function we place our ladder and enable physics. We set this to immovable so it can’t move around when another bodu bumps into it. As with the rest of our game objects, we’re just setting it’s position by eye using absolute coordinates.

ladders = this.physics.add.staticGroup();
ladders.enableBody = true;
ladder = ladders.create(268, 296, 'ladder');
ladder.body.immovable = true;

That will place our ladder in the game, but now we need to handle the physics and how it interacts with our player. At the end of our create function we add a collision test using overlap for our hero and the ladder. When these two bodies are overlapping, the function isOnLadder will be called.

this.physics.add.overlap(player, ladders, isOnLadder, null, this);

All our isOnLadder function does is sets a flag and turns off the players gravity. We then use this flag in our update loop.

function isOnLadder() {
        //set ladder flag to true and remove gravity
        onLadder = true;
        player.body.setAllowGravity(false);
    }

This has set us up so that we now know in the game loop whether we are on a ladder or not. We now need to tweak both our mobile controls and keyboard controls to behave differently if we are on a ladder. First, let’s look at mobile. previously using your right thumb resulted in a jump. Let’s make it so that only happens if our hero is not on a ladder:

//check if we are currently overlapping a ladder. If we are then...
if (onLadder) {
    //code goes in here
}
//not on a ladder so go for a standard jump 
else {
    //  if we have moved our thump upwards then we set jump flag to true
    if (prevPos - yPos > touchJumpThreshold) {
        touchJump = true;
    }
}

If our hero is on a ladder, we want an upward movement of the users thumb to make the hero climb, and the downward movement to make them descend:

//check if we are currently overlapping a ladder. If we 
//check if we are currently overlapping a ladder. If we are then...
//kill any upwards / downwards velocity from our hero
player.setVelocityY(0);

//if moving up with thumb then climb up the ladder
if (Math.floor(prevPos) > Math.floor(yPos)) {
    if (!myMovePointer) {
        //when moving vertically we want the player pefectly lined up
        player.x = ladder.x;
        //also kill any x velocity to be sure
        player.setVelocityX(0);
        player.setVelocityY(-100);
    }
}
//if moving down with thumb then climb down the ladder
if (Math.floor(prevPos) < Math.floor(yPos)) {
    if (!myMovePointer) {
        //when moving vertically we want the player pefectly lined up
        player.x = ladder.x;
        //also kill any x velocity to be sure
        player.setVelocityX(0);
        player.setVelocityY(100);
    }
}

The first thing we do here, is to remove any velocity the hero might have from jumping / falling. We then check the prevPos variable against the current yPos. If the prevPos is greater then yPos that means the users thumb has moved upwards and we want the hero to ascend the ladder. We do this by applying some negative y velocity. We also check the other way for descending the ladder.

Next we need to check when the user is no longer touching the screen if they are still on the ladder. After our code that hides the touch movement indicator we check the onLadder flag. If set, we apply a slight upward velocity to stop out character from moving whilst they are still on the ladder.

else {
    touchSlider.alpha = 0;
    startX = 0;
    touchMoving = false;

    if (onLadder) {
        player.setVelocityY(0);
    }
}

When working on this I came across a bug. Setting the y velocity to 0 and removing the gravity wasn’t keeping the player perfectly still on the ladder instead they would very slowly slide down. It turns out that there are actually two loops going on, our game loop (the update function) and the physics loop (The physics loop happens behind the scenes). They are usually in step, but rarely the game loop can step ahead for one frame. This caused the gravity to get flicked on and off applying a downward force on our character. Both these loops are supposed to run at 60fps (if they can) so the solution is to specify a higher frame per second rate for our physics loop in the config like this:

physics: {
            default: 'arcade',
            arcade: {
                fps: 120,
                gravity: { y: 300 }
            }
        },

This keeps the two loops in sync, so we no longer get our hero slowly sliding down the ladder.

Now that mobile controls are sorted, we need to do the same again with keyboard controls. I don’t want the same code being executed twice, so upon the first touch I set a flag isTouch to be true.

//set flag as we are definitely moving
touchMoving = true;
//set a flag so we know we are on a mobile device
isTouch = true;

I added isTouch = false to my list of global variables, so by default we assume we are on a non touch device until we have a touch detected.

I can then use this flag as a conditional for my keyboard code – if we are on a touch device don’t use this code:

//if we are on a ladder and not on a touch device
if (onLadder && !isTouch) {
    //kill any upwards / downwards velocity from our hero
    player.setVelocityY(0);

    if (cursors.up.isDown) {
        if (!cursors.left.isDown && !cursors.right.isDown) {
            //when moving vertically we want the player pefectly lined up
            player.x = ladder.x;
            //also kill any x velocity to be sure
            player.setVelocityX(0);
            player.setVelocityY(-100);
        }
    }
    //if JUST down is being pressed then line up player perfectly with ladder. 
    //We do this to make it quicker - but like a firemans pole
    if (cursors.down.isDown && !cursors.left.isDown && !cursors.right.isDown) {
        //when moving vertically we want the player pefectly lined up
        player.x = ladder.x;
        //also kill any x velocity to be sure
        player.setVelocityX(0);
        player.setVelocityY(100);
    }
}

As you can see, it’s the same code as on mobile. We are repeating ourselves a bit here code-wise, but as the control systems are so different it’s becoming a fairly complicated process to untangle it all and make it properly neat. We will refactor it at some point.

We now have a working ladder, but it’s introduced a few issues. What about when we want to jump off the top of a ladder? Mobile also falls quite clunky, having to constantly move your thumb upwards to keep ascending. Next up, we’ll fix those issues in improved ladders.

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