Your First PhaserJS Game

Score and Health System

Add scoring, health, and obstacles to your PhaserJS game, introducing apples to collect and spikes to dodge for a dynamic gameplay experience.

In the last chapter, we gave our character the power to move and jump around! Now, it’s time to make things a little more interesting by adding apples to collect (yep, your player’s hungry 🍎) and some deadly spikes to dodge! We’ll also track score and health, so players can feel the thrill of staying alive with every jump. Let’s dive in! 🎉


Step 1: Collecting Apples 🍏

We’ll detect overlaps between the player and apples, and update the score when an apple is collected.

In the addCollectables() function inside mainScene.js, add the following code where we handle overlaps. This callback function will destroy the apple when collected:

this.physics.add.overlap(this.player, this.collectables, (player, apple) => {
    apple.destroy();
});

What's Happening Here? 🧠

Whenever the player overlaps with an apple, Phaser detects the overlap event and runs the callback function. Here, apple.destroy() removes the collected apple from the scene, making it disappear once the player grabs it!

13


Step 2: Adding a Score Display 📊

Now, we’ll add a score counter that updates every time an apple is collected.

Inside addCollectables(), initialize the score and create a text display for it. Also, update the score in the overlap callback.

addCollectables() {
    this.score = 0;
    this.scoreText = this.add.text(16, 16, `Score: ${this.score}`, {
        fontSize: "32px",
        fontStyle: "bold",
        color: "#000000",
    });
 
    this.collectables = this.physics.add.staticGroup();
    const apple1 = this.add.sprite(270, 180, "apple").setScale(2).play("apple_anim");
    const apple2 = this.add.sprite(510, 180, "apple").setScale(2).play("apple_anim");
    const apple3 = this.add.sprite(210, 470, "apple").setScale(2).play("apple_anim");
 
    this.collectables.add(apple1);
    this.collectables.add(apple2);
    this.collectables.add(apple3);
 
    this.physics.add.overlap(this.player, this.collectables, (player, apple) => {
        apple.destroy();
        this.score++;
        this.scoreText.setText(`Score: ${this.score}`);
    });
}

How Does It Work? 💡

  • Score Tracking: this.score = 0; initializes the score at zero. Every time the player collects an apple, this.score++ increases the score by 1.
  • Display Update: this.scoreText.setText(...) updates the text display with the new score after each apple is collected.

14


Step 3: Adding Health and Health Display 💔

Next, we’ll add a health counter for the player. Whenever the player touches a spike, the health will decrease. We’ll also display the player’s current health on the screen.

Add the following lines at the end of the create() function in mainScene.js:

this.playerHealth = 100;
this.healthText = this.add.text(570, 16, `Health: ${this.playerHealth}`, {
    fontSize: "32px",
    fontStyle: "bold",
    color: "#000000",
});

Explanation 🧐

  • Player Health: this.playerHealth starts at 100. Every time the player collides with a spike, the health will reduce.
  • Health Display: this.healthText shows the player’s health at the top of the screen and updates as the health changes.

15


Step 4: Adding Dangerous Spikes 🌵

It’s time to add some danger to the game with deadly spikes! These spikes will reduce the player’s health if touched and even restart the game if health reaches zero.

Add the addSpikes() function in mainScene.js and call it in the create() function:

addSpikes() {
    this.spikes = this.physics.add.staticGroup();
 
    // Placing spikes at various locations
    for (let i = 0; i < 3; i++) {
        const spike = this.add.image(300 + i * 40, 735, "spike").setScale(2.5).setDepth(3);
        this.spikes.add(spike);
        spike.body.setSize(spike.body.width, spike.body.height - 20);
        spike.body.setOffset(spike.body.offset.x, spike.body.offset.y + 10);
    }
    for (let i = 0; i < 3; i++) {
        const spike = this.add.image(70 + i * 40, 480, "spike").setScale(2.5).setDepth(3);
        this.spikes.add(spike);
        spike.body.setSize(spike.body.width, spike.body.height - 20);
        spike.body.setOffset(spike.body.offset.x, spike.body.offset.y + 10);
    }
 
    this.physics.add.overlap(this.player, this.spikes, () => {
        this.player.setTint(0xff0000); // Temporary red tint on hit
        this.cameras.main.shake(300, 0.005); // Screen shake effect
        this.playerHealth -= 1;
 
        if (this.playerHealth <= 0) {
            this.scene.restart(); // Restart scene if health hits zero
        }
        this.healthText.setText(`Health: ${this.playerHealth}`);
        setTimeout(() => {
            this.player.clearTint();
        }, 500);
    });
}

How It Works 🎮

  1. Spike Setup: This function places spikes at different positions on the screen. Each spike is added to a static group, this.spikes.
  2. Collision Effect: If the player overlaps with a spike, a few things happen:
    • The player flashes red (setTint(0xff0000)) for a visual hit effect.
    • The screen shakes for dramatic effect (this.cameras.main.shake(300, 0.005)).
    • Health decreases by 1 (this.playerHealth -= 1). If health drops to zero, the game restarts.
    • Health display updates (this.healthText.setText(...)) with the current health value.

16


Congratulations! 🎊

You’ve officially added a score and health system to your game! Now, every apple collected adds to the score, and every spike touched takes away health. You’re building up quite an exciting game world—next up, we’ll add even more fun features!


Complete Code 📜

If you missed anything or made some accidental changes, here’s the complete code for reference! 💻🔧


import Phaser from "phaser";
 
export default class LoadingScene extends Phaser.Scene {
    constructor() {
        super({
            key: "LoadingScene"
        });
    }
    preload() {
        // background
        this.load.image("bg", "assets/bg.png")
 
        // ground
        this.load.image("ground", "assets/ground.png")
 
        // spike
        this.load.image("spike", "assets/spike.png")
 
        // items
        this.load.spritesheet("apple", "assets/Items/Apple.png", {
            frameWidth: 32,
            frameHeight: 32
        })
        this.load.spritesheet("collected", "assets/Items/Collected.png", {
            frameWidth: 32,
            frameHeight: 32
        })
 
        // main character
        this.load.spritesheet("player_idle", "assets/character/idle.png", {
            frameWidth: 32,
            frameHeight: 32
        })
        this.load.spritesheet("player_hit", "assets/character/hit.png", {
            frameWidth: 32,
            frameHeight: 32
        })
        this.load.spritesheet("player_jump", "assets/character/jump.png", {
            frameWidth: 32,
            frameHeight: 32
        })
        this.load.spritesheet("player_run", "assets/character/run.png", {
            frameWidth: 32,
            frameHeight: 32
        })
 
        // platforms
        this.load.image("platform_brown", "assets/Platforms/Brown.png")
        this.load.image("platform_grey", "assets/Platforms/Grey.png")
 
        // falling platforms
        this.load.image("falling_platform_off", "assets/FallingPlatforms/Off.png")
        this.load.spritesheet("falling_platform_on", "assets/FallingPlatforms/On.png", {
            frameWidth: 32,
            frameHeight: 10
        })
 
        // Trampoline
        this.load.image("trampoline_idle", "assets/Trampoline/Idle.png")
        this.load.spritesheet("trampoline_jump", "assets/Trampoline/Jump.png", {
            frameWidth: 28,
            frameHeight: 28
        })
    }
    create() {
        // main character animations
        this.anims.create({
            key: "player_idle_anim",
            frames: this.anims.generateFrameNumbers("player_idle", {
                start: 0,
                end: 10
            }),
            frameRate: 20,
            repeat: -1
        })
        this.anims.create({
            key: "player_hit_anim",
            frames: this.anims.generateFrameNumbers("player_hit", {
                start: 0,
                end: 6
            }),
            frameRate: 12,
        })
        this.anims.create({
            key: "player_jump_anim",
            frames: this.anims.generateFrameNumbers("player_jump", {
                start: 0,
                end: 0
            })
        })
        this.anims.create({
            key: "player_run_anim",
            frames: this.anims.generateFrameNumbers("player_run", {
                start: 0,
                end: 11
            }),
            frameRate: 20,
            repeat: -1
        })
 
        // falling platform on
        this.anims.create({
            key: "falling_platform_on_anim",
            frames: this.anims.generateFrameNumbers("falling_platform_on", {
                start: 0,
                end: 3
            }),
            frameRate: 12,
            repeat: -1
        })
 
        // apple animation
        this.anims.create({
            key: "apple_anim",
            frames: this.anims.generateFrameNumbers("apple", {
                start: 0,
                end: 16
            }),
            frameRate: 20,
            repeat: -1
        })
 
        // Trampoline Jump Animation
        this.anims.create({
            key: "trampoline_jump_anim",
            frames: this.anims.generateFrameNumbers("trampoline_jump", {
                start: 0,
                end: 7
            }),
            frameRate: 20,
            repeat: 0
        })
 
        this.scene.start("MainScene");
    }
    update() {
 
    }
}

Now you have everything! 🚀 Enjoy creating your awesome PhaserJS game!

On this page