« Back to Blog

Phaser Platformer Series: 13 Hearts / Lives

posted on 9 June 2021

In this tutorial we’ll look at lives. Instead of dying as soon as we hit a baddie, our hero will have 3 chances. Getting hit will results in a loss of a life and brief period of invulnerability. If the hero gets hit three times it’s game over.

Here’s what we will be making:

See the Pen
Phaser Platformer: 13 Hearts / Lives
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.

We’ll need to add some new variables to our globals at the top of the file. We’ll be using two sets of sprites for our hearts, the outline and the fill. When the hero has full health, they’ll have 3 filled hearts, and as they lose a live, a heart will lose it’s fill leaving the outline behind. We also need a variable to store the number of lives and the time in miliseconds that the player should be invulnerable for (1000 = 1 second):

heartOutlines = [],
hearts = [],
maxHearts = 3,
vulnerableTime = 1000,

and then load in the sprites in the preload function:

this.load.image('heart', 'heart.png');
this.load.image('heart-filled', 'heart-filled.png');

We need to add two new properties to our hero, the number of lives and an invulnerability flag:

//create and set an invulnerable flag for after the player has been hit
player.invulnerable = false;
//set no lives / hearts
player.hearts = maxHearts;

After our score variables in the create function we’ll add our heart visuals:

//create three heart outlines...
var heartOutline1 = this.add.sprite(760, 38, 'heart'),
    heartOutline2 = this.add.sprite(720, 38, 'heart'),
    heartOutline3 = this.add.sprite(680, 38, 'heart');

//and store in an array for easy access later
heartOutlines = [heartOutline1, heartOutline2, heartOutline3];

//create three heart fills...
heart1 = this.add.sprite(760, 38, 'heart-filled');
heart2 = this.add.sprite(720, 38, 'heart-filled');
heart3 = this.add.sprite(680, 38, 'heart-filled');
//and store in an array for easy access later
hearts = [heart1, heart2, heart3];

The sprites are added from left to right (so heart 1 is the one of the far right). Notice the heart sprites sit exactly on top of the outlines. Both sets of sprites are added to arrays for easy access later.

Then when our hero takes a hit in the hitBaddie function:

//get the heart sprites from our arrays we set up earlier
var currentHeartCount = player.hearts,
    currentHeart = hearts[currentHeartCount - 1],
    currentHeartOutline = heartOutlines[currentHeartCount - 1];

We get the number of lives left stored in the player object. So on first hit that will be 3. We use these to get the next heart sprite from our arrays we created earlier. As arrays start at an index of 0, we have to take away 1 to get the right index. i.e. we can’t say get me hearts[3] when there are three items in the array, the correct index is 2 (0,1,2). Grabbing the last heart from our array (heart3) is actually the left most heart, so we’ve laid them out on the screen in reverse so it’s really easy to get them. If player has lives lives left, we’re going to grab heart 3 BUT heart 3 is the first heart on the page from left to right, so the one we want first.

Now we have the two heart sprites we want, the outline and fill, we can animate them. We’re just going to fade out the fill, but for the outline we’ll use a timeline and make it shrink and then grow back to normal size:

//fade out the heart fill
var heartFade = this.tweens.add({
    targets: currentHeart,
    alpha: 0,
    scaleX: 0,
    scaleY: 0,
    ease: 'Linear',
    duration: 200
});

//create a timeline of tweens for the heart outline so it shrinks then grows back
var heartsTimeline = this.tweens.createTimeline();

//this is the heart outline scaling down
heartsTimeline.add({
    targets: currentHeartOutline,
    scaleX: 0.5,
    scaleY: 0.5,
    ease: 'Power1',
    duration: 200
});

//and then back
heartsTimeline.add({
    targets: currentHeartOutline,
    scaleX: 1,
    scaleY: 1,
    ease: 'Power1',
    duration: 200
});
//play the timeline straight away
heartsTimeline.play();

The timeline lets us chain tweens, so we can play them in squence. Once that is done we can update some variables:

//remove a heart from out count stored on the player object
player.hearts -= 1;

We then check if the hero has run out of lives completely:

//if hearts is 0 or less you're dead as you are out of lives
if (player.hearts <= 0) {

In that case we play the death animation and restart the game as before. If it’s not the players last life, then we want to play some sort of animation, and make them invulnerable for a short period of time.

//make the player stop in their tracks and jump up
player.body.velocity.x = 0;
player.body.velocity.y = -220;
//tween the players alpha to 30%
var tween = this.tweens.add({
    targets: player,
    alpha: 0.3,
    ease: 'Linear',
    duration: 200,
    onCompleteScope: this
});

//set a timer for 1 second. When this is up we tween player back to normal and make then vulnerable again
var timer = this.time.delayedCall(1000, playerVulnerable, [this]);

Here we made the player jump up in the away as well as stopping any sideways velocity. Then we change their opacity to 30%. We’re using Phasers built in timer functions to call the function playerVulnerable after 1 second. So we give our hero a 1 second grace period where they can’t be harmed again. After that 1 second the animation fades the hero back in and resets invulnerabiility flag.

function playerVulnerable(game) {
//tween player back to 100% opacity and reset invulnerability flag
var death = game.tweens.add({
    targets: player,
    alpha: 1,
    ease: 'Linear',
    duration: 200,
    onComplete: function() {
        player.invulnerable = false;
    },
    onCompleteScope: this
});
}

Looking good. Next up, spikes / lava.

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