💡 This page contain affiliate links. By making a purchase through them, we may earn a commission at no extra cost to you.
How to Remake Chrome's Dino Game in PhaserJS: Part III.

How to Remake Chrome's Dino Game in PhaserJS: Part III.

Ferenc AlmasiLast updated 2021 June 07 • Read time 9 min read
  • twitter
  • facebook
JavaScript

This is the third, and last part of the Remake Dino in PhaserJS. If you missed out on the first part, you can reach it here, for the second part, you can follow this link. If you're looking for the full project in one piece, you can get the code from the GitHub repository.

We've left off the previous part with managing to spawn cactuses into the way of our dinosaur. In this tutorial, we will finish off the game by covering the remaining topics:


Table of Contents

  1. Increasing the Game Speed
  2. Game Over
    1. Storing the high score
  3. Restarting the Game
  4. Conclusion

Increasing the Game Speed

First of all, the game currently runs at a constant speed. There's no real challenge, the game doesn't get harder over time in any way. To fix that, let's introduce speed, just like the original game speeds up over time. For this, we are going to need two other state variables, called speed, and speedLoop:

Copied to clipboard! Playground
this.state = {
    ...
    speed: 1,
    timer: {
        speedLoop: 0,
        cactusSpawnLoop: 0
    }
};
dino.js

While state.speed will hold the current speed of the game, state.timer.speedLoop will keep track of time to increase the speed by a fraction every 10 seconds. To increase it, we need to update the update method in the following way:

Copied to clipboard!
update(time, delta) {
+   this.state.timer.speedLoop += delta;
    this.state.timer.cactusSpawnLoop += delta;

    if (this.inputs.space.isDown && !this.state.started && !this.state.gameOver) {
        this.state.started = true;
    }

    if (this.state.started) {
        this.player.update(this.inputs, delta);
            
        if (!this.state.UIUpdated) {
            this.updateUI();
        }
            
        if (this.state.timer.cactusSpawnLoop > this.state.cactusDistance) {
            this.state.cactusDistance = Phaser.Math.Between(5000 / this.state.speed, 1000 / this.state.speed);
            this.state.cactuses.push(new Cactus(this));
            this.state.timer.cactusSpawnLoop = 0;
        }
 
+       if (this.state.timer.speedLoop > 10000) {
+           this.state.timer.speedLoop = 0;
+           this.state.speed += .25;
+       }
    }
}
dino.diff

Just like we did for the cactusSpawnLoop, we need to add delta time to the speedLoop as well, to keep track of the time passed. Then when the game started, we can check if 10000 milliseconds have passed, and increase the speed by 0.25. On its own, this won't do much, we only have a speed inside the state that is increasing over time. However, we can use this number to increase difficulty. One way to do that is to reduce the distance between each cactus. To do that, modify cactusDistance to the following:

Copied to clipboard!
if (this.state.timer.cactusSpawnLoop > this.state.cactusDistance) {
-   this.state.cactusDistance = Phaser.Math.Between(5000, 1000);
+   this.state.cactusDistance = Phaser.Math.Between(5000 / this.state.speed, 1000 / this.state.speed);
    this.state.cactuses.push(new Cactus(this));
    this.state.timer.cactusSpawnLoop = 0;
}
dino.diff

We divide the time it takes for each cactus to spawn to reduce the distance between them. To further increase difficulty, you could also divide state.cactusDistance by the speed inside the if statement. Of course, with higher difficulty comes a higher reward, so let's go inside the Player class and update the scoring logic:

Copied to clipboard!
update(input, delta) {
    this.timer += delta;
     
-   if (this.timer > 100) {
+   if (this.timer > 100 / this.scene.state.speed) {
        this.timer = 0;
        updateScore(this.scene.state);
    }
}
Player.diff

As time passes, the score will be updated at an increasing pace. The longer we survive, the more cactuses we will face, and the faster we will collect the score.

Increasing the speed of the game over time
Speed increases every 2,5 seconds, as well as the score updates at an increasing speed.
Looking to improve your skills? Check out our interactive course to master JavaScript from start to finish.
Master JavaScriptinfo Remove ads

Game Over

Still, there's no price we pay, as we cannot die. So as a next step, let's add the necessary collision. We only need one between the dino and the cactuses. To do that, let's open up our Cactus class, and add a new collider into the constructor:

Copied to clipboard! Playground
class Cactus {
    constructor(scene) {
        ...
        this.collider = scene.physics.add.collider(scene.player.sprite, this.sprite, this.gameOver, null, this);
    }

    gameOver() {
        this.scene.player.die();
    }
}
Cactus.js

We can add colliders in Phaser by calling physics.add.collider with a couple of arguments:

  • Two objects to test the collision between, this time player.sprite and the cactus (this.sprite).
  • A collide callback, which executes when the two objects collide. For this, we can create a gameOver method to end the game.
  • A process callback, which is executed when the two objects intersect. Very similar to the collide callback, but this function must return a boolean to indicate whether the two objects are intersecting or not.
  • The context of the callback function, which should be this.

As for the gameOver method, we have one single line: calling player.die. Since we haven't added this function yet, let's go into our Player class and do that now:

Copied to clipboard! Playground
// Make sure you add `showHighScore` to your imports
import { updateScore, showHighScore } from '../ui/score'

die() {
    this.isDead = true;
    this.sprite.play('idle', true);

     this.scene.state.started = false;
     this.scene.state.gameOver = true;

     showHighScore();
     showGameOver();
}
Player.js

This will stop the dinosaur from playing the running animation and sets all necessary states to stop the game. On its own, this will not stop the incoming fleet of cactuses, however. To do that, let's add a new method to our Cactus class:

Copied to clipboard!
stop() {
    this.sprite.setVelocityX(0);
}
Cactus.js

This method is straightforward, it will set the velocity of the cactus back to zero. So where do we call this? Inside the update method of our scene. Remember that we've set state.gameOver to true in the die method of our player? We can use this flag to loop through each cactus we store in the state to stop them:

Copied to clipboard!
if (this.state.gameOver) {
    this.state.cactuses.forEach(cactus => cactus.stop());
}
dino.js

Storing the high score

If we run the game now, as soon as we hit a cactus, we should be greeted with a "Game Over". There's only one problem still. The high score remains at zero.

The highscore is not updated at the moment
Notice that the high score is still sitting at 0.

To fix this, let's add highScore to our state, so we can keep track of it:

Copied to clipboard!
this.state = {
    ...
    highScore: 0
};
dino.js

We will also need a new method inside score.js, in order to update it. This function will set it to the current score:

Copied to clipboard!
export const setHighScore = state => {
    state.highScore = state.score;
    score.best.innerText = `HI ${state.score.toString().padStart(6, '0')}`;
};
ui/score.js

It needs to take the state as an argument in order to update that as well. Just like we did for the score, we need to use padStart here again. And to actually call it, we can add the following statement into the die method of our player:

Copied to clipboard!
die() {
    this.isDead = true;
    this.sprite.play('idle', true);

    this.scene.state.started = false;
    this.scene.state.gameOver = true;

    showHighScore();
    showGameOver();

+   if (this.scene.state.score > this.scene.state.highScore) {
+       setHighScore(this.scene.state);
+   }
}
Player.diff
The high score will only be set if it's greater than the current score. Don't forget to import `setHighScore`

Restarting the Game

There's only one thing missing from the game, and that is resetting it, so we can go another round. For this, let's add a last check to the update method of our Dino scene:

Copied to clipboard!
if (this.inputs.space.isDown && this.state.gameOver) {
    this.restartGame();
}
dino.js

We will be able to restart the game once the gameOver state is set to true, and we are pressing the space bar. Let's take a look at what we need to do in the restartGame method:

Copied to clipboard! Playground
restartGame() {
    hideGameOver();
    resetScore(this.state);

    this.state.started = true;
    this.state.gameOver = false;
    this.state.speed = 1;
    this.state.cactuses.forEach(cactus => cactus.sprite.destroy());
    this.state.cactuses = [];

    this.player.isDead = false;
}
dino.js

We need to hide the "Game Over" text and reset the score — which we yet to define — as well as reset most of our state variables. Now we can finally make use of the cactuses array. To free up memory, we can call sprite.destroy to remove them, and also set the array to a new, empty one.

To further improve performance, we can destroy sprites as soon as they leave the scene. They will never come back, so there's no reason to keep them.

Also, make sure to reset the isDead state of the player. So to reset the score, we need one last function for score.js:

Copied to clipboard!
export const resetScore = state => {
    state.score = 0;
    score.current.innerText = '000000';
}
ui/score.js

It simply resets the state and sets the UI back to all zeroes. Now we have a working dinosaur game with cactuses, animations, scoring, and collisions.

Playing the finished dinosaur game
If we get a game over, we can restart by pressing space. The high score is also updated correctly.

Conclusion

And with that, you've just finished your very first game in Phaser! 🎉 You've successfully created the base gameplay mechanics of Chrome's Dinosaur! If you've reached this far, congratulations! You've learned how to set up a new Phaser project with Parcel, how to set up animations, and add sprites to the game that are responding to user inputs. We've looked into how we can spawn enemies, and update the UI based on the game state.

If you would like to continue your journey in game development, make sure you check out how you can recreate the base gameplay mechanics of the famous Super Mario Bros. using Phaser:

How to Remake Mario in PhaserJS

Do you have experience building platform games in PhaserJS? Let us know your thoughts and opinions about it in the comments below! Thank you for reading through, happy coding! 🎮

  • twitter
  • facebook
JavaScript
Did you find this page helpful?
📚 More Webtips
Frontend Course Dashboard
Master the Art of Frontend
  • check Access 100+ interactive lessons
  • check Unlimited access to hundreds of tutorials
  • check Prepare for technical interviews
Become a Pro

Courses

Recommended

This site uses cookies We use cookies to understand visitors and create a better experience for you. By clicking on "Accept", you accept its use. To find out more, please see our privacy policy.