Wednesday, February 17, 2016

Hitting the Wall - The Difference Between "blocked" and "touching" in Phaser

I've made another small improvement to my 8-bit style Phaser platformer. Now that I have some more complete levels in place, I noticed that if the frog was jumping and hit his head on the jump wasn't ending. He would just kind of hover there, in collision with the overhead tile, until the jump timer was up (I discussed my use of a timer to control the jump behavior previously).

I wanted to cancel the upward motion in the event of an overhead collision. Phaser has two ways of detecting this kind of thing in the Arcade physics engine: blocked and touching. The blocked and touching fields on the Sprite physics body are objects which each contain four boolean values - one for each direction. When true, these values indicate a collision on that side of the Sprite.

if (sprite.body.blocked.right) {
    // running into a wall on the right
} else if (sprite.body.blocked.left) {
    // running into a wall on the left
} else if (sprite.body.blocked.up) {
    // hitting something above
} else if (sprite.body.blocked.down) {
    // on the ground. same as sprite.body.onFloor() ??
} 

if (sprite.body.touching.right) {
    // hitting another sprite on the right
} else if (sprite.body.touching.left) {
    // hitting another sprite on the left
} else if (sprite.body.touching.up) {
    // hitting another sprite above
} else if (sprite.body.touching.down) {
    // hitting another sprite below
} 

So what's the difference? The blocked object refers to the Sprite colliding with a Tile. Since I am using a Tilemap for each level in my game, blocked will tell me if the frog is hitting some part of the environment. The touching object refers to the Sprite touching another sprite. I could use that field to determine if the frog hits an overhead enemy, for example.

For now, I've updated my jump control algorithm to take the Tile collision case into account.

function update() {

    // ... other stuff ...

    if (spacebar.isDown) {
    
        if ((frog.body.onFloor() || frog.locked) && jumpTimer === 0) {
            // jump is allowed to start
            jumpTimer = 1;
            frog.body.velocity.y = -400;
            frog.cancelLock();
            jumpSound.play();
        } else if (jumpTimer > 0 && jumpTimer < 31 && !frog.body.blocked.up) {
            // keep jumping higher
            jumpTimer++;
            frog.body.velocity.y = -400 + (jumpTimer * 7);
        } else if (frog.body.blocked.up) {
            // permanently end this jump
            jumpTimer = 999;
        }
    
    } else {
        // jump button not being pressed, reset jump timer
        jumpTimer = 0;
    }

    // ... other stuff ...

}

The changes are minor, but important. If the player continues to hold down the spacebar, the jump will only keep going higher if the frog is not being blocked from above. If he is being blocked, I set the jumpTimer to a high value, to ensure that the jump is over - treated basically the same way that it would be if the player let go of the spacebar. If I don't do this, on the next frame the frog will have dropped a little bit and be out of collision, which will make the second branch of the nested if-else block true again and he'll move up some more. This causes the overhead collision to re-occur, and it's a viscous cycle until we get past 30 frames!

That's all I've changed for today, but that's actually good news. It means I'm much closer to being finished with the game, since I'm working on such small details. Maybe by the end of the month it will be complete. For now, you can view the source code on GitHub and play the game in its unfinished state here.

Be sure to read today's comic! It's about load balancers. Or see-saws. Or both.

Amphibian.com comic for 17 February 2016