Wednesday, February 24, 2016

Swimming in a Phaser Platformer

The theme for Level 7 of my 8-bit style Phaser platformer has been selected - Liquid Cooled! It's a water level with a name that also describes a high-performance computer needing more than just a normal CPU fan.

But a water level means the frog has to swim through it, instead of jump. I had to make a few changes to my code in order to support an optional "swim mode" behavior. I handled this by putting a water flag in my level objects. When true, it means the physics should be more...liquid.

The swimming level. All platformers should have at least one.

To start, the gravity needed to be adjusted. I have the normal gravity set to 1500 in my game, but I played around with different numbers and 250 seemed most appropriate for a swimming effect. In real life, gravity doesn't decrease when you're in water...but the effect of your body's buoyancy makes it seem as if it does. Same thing here.

function create() {

    // ... other stuff ...

    if (waterLevel) {
        this.physics.arcade.gravity.y = 250;
    } else {
        this.physics.arcade.gravity.y = 1500;
    }

    // ... other stuff ...

}

The other thing that needed to change was the jump behavior. On land, the frog can't start a new jump while still in mid-air. But in the water, doing so gives the impression of swimming. Also, the amount of upward thrust supplied by a jump should be lessened under water. The speed at which the frog moves left and right should also be reduced.

function update() {

    // ... other stuff ...

    var xVel = waterLevel ? 100 : 150;
    var yVel = waterLevel ? -250 : -400;
    var jumpFrames = waterLevel ? 26 : 31;
    
    if (spacebar.isDown) {

        if ((frog.body.onFloor() || frog.locked || waterLevel) && jumpTimer === 0) {
            // jump is allowed to start
            jumpTimer = 1;
            frog.body.velocity.y = yVel;
            frog.cancelLock();
            jumpSound.play();
        } else if (jumpTimer > 0 && jumpTimer < jumpFrames && !frog.body.blocked.up && !frog.body.touching.up) {
            // keep jumping higher
            jumpTimer++;
            frog.body.velocity.y = yVel + (jumpTimer * 7);
        } else if (frog.body.blocked.up || frog.body.touching.up) {
            // permanently end this jump
            jumpTimer = 999;
        }

    } else {
        // jump button not being pressed, reset jump timer
        jumpTimer = 0;
    }

    if (cursors.left.isDown) {
        frog.body.velocity.x = -xVel;
    } else if (cursors.right.isDown) {
        frog.body.velocity.x = xVel;
    }

    // ... other stuff ...

}

Lines 5-7 in the code snippet above set the values for x-velocity, y-velocity, and length of a jump (in frames). These used to be hard-coded to the non-water values, but now there's a water option. In the check to see if a jump can start, line 11, I've added the condition that if this is a water level, jumps can start even if the frog is not on the ground or locked to a floating platform. The jump sets the y-velocity to the appropriate value, and the cursor keys set the x-velocity.

With these relatively simple adjustments in place, it really feels like the frog is swimming through Level 7. You can play it yourself on Amphibian.com, and make sure you also check out today's comic while you're there! If you're more interested in the complete source code for the game, you can find that on GitHub. But seriously, read the comic. It's funny. Most of the time.

Amphibian.com comic for 24 February 2016