💡 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 Mario in PhaserJS: Part III.

How to Remake Mario in PhaserJS: Part III.

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

The is the third and last part of the Remake Mario in PhaserJS tutorial. If you missed out on the previous part, you can reach it here, and for part I., click here.

How to Remake Mario in PhaserJS

We've left off the tutorial, by generating collectible coins and goombas that can be destroyed. We've also added some motivation for the player by making them able to get scores for collecting coins and killing goombas.

In this tutorial, we're going to finish off the level by first implementing the game over functionality, and then making the player actually win the game if they reach the flag at the end of the map. If you would like to get the full source code, you can get it from the GitHub repository. And with that being said, let's jump into coding!


Table of Contents

  1. Game Over
    1. Making Mario die
  2. Winning the Game
    1. Creating Particle Effects
  3. Conclusion

Game Over

Right now, there's no way to lose the game, which greatly reduces the challenge. Let's look into how can we make Mario die when he hits a goomba from the side. Inside Goomba.js we had the following method:

Copied to clipboard! Playground
gameOver() {
    // PHEW
    if (this.scene.player.sprite.body.touching.down) {
        this.die();

        return;
    }
}
Goomba.js

This did a check if the goomba was hit from the top. Essentially this is checking if Mario stepped on one. But what happens if it's not the case? Let's see what we want to happen:

Game over for Mario

We want Mario to jump up, play the die animation and then fly off the scene. Then we want to show the "Game Over" text on a blank canvas. To do that, extend the method with the following lines of code after the if statement:

Copied to clipboard! Playground
this.scene.player.die();
this.scene.input.keyboard.shutdown();

this.scene.physics.world.removeCollider(this.scene.player.collider);
this.scene.physics.world.removeCollider(this.collider);

setTimeout(() => {
    this.scene.scene.start('GameOver');
}, 1500);
Goomba.js

First, we want to call the die method on our Player class. We haven't defined this yet, so that will be our next step after examining this method. We also want to disable the keyboard input with keyboard.shutdown. This way, the player won't be able to control Mario anymore after he dies. To make Mario completely leave the scene, you also need to remove the colliders from the world with the removeCollider method. This is why we needed to assign the collider to a property in our Player class. After 1500 milliseconds, we then show the GameOver scene. Now let's see what Player.die does.

Making Mario die

In your Player class, under your constructor, first introduce a new flag to determine if the player is already dead:

Copied to clipboard! Playground
constructor(scene, x, y) {
    this.scene = scene;
    this.sprite = scene.physics.add.sprite(x, y, 'atlas')
        .setScale(2);

    this.sprite.setCollideWorldBounds(true);
    this.sprite.isDed = false;

    ...
}
Player.js

By default, this will take the value of false. Then in your update method, make sure you check the value of this flag, before playing any animation:

Copied to clipboard!
this.sprite.body.onFloor() && !this.sprite.isDed && this.sprite.play('run', true);
this.sprite.body.onFloor() && !this.sprite.isDed && this.sprite.play('idle', true);
Player.js

This prevents playing the running or idle animations while Mario is dead. Underneath your update method, create a new method called die. This is what we will call from the Goomba class, once Mario hits one.

Copied to clipboard! Playground
die() {
    this.sprite.isDed = true;
    this.sprite.setVelocity(0, -350);
    this.sprite.play('die', true);
    this.sprite.setCollideWorldBounds(false);
}
Player.js

It will simply make Mario jump into the air, and then make the sprite play the die animation. Make sure you remove the world bounds collider, otherwise, Mario won't be able to leave the screen. Going back to our Goomba class, we had a reference to a GameOver scene that we haven't defined yet, so create a new file called GameOver under your scene folder, and add the following to it:

Copied to clipboard! Playground
class GameOver extends Phaser.Scene {

    constructor () {
        super('GameOver');
    }

    create() {
        this.cameras.main.setBackgroundColor('#000');

        document.getElementsByClassName('game-over')[0].classList.add('visible');
    }
}

export default GameOver;
GameOver.js

All this class does is:

  • It sets the background color to black
  • It adds a visible class to the game-over div that we've created in the index.html file at the very first tutorial. Remember, this is hidden initially, and is displayed with a visible class.

Don't forget to import this scene into your index file and reference it in your scene array, otherwise, Phaser will not have access to it.

Copied to clipboard! Playground
const config = {
    ...
    scene: [
        Game,
        GameOver
    ]
};
index.js
Looking to improve your skills? Check out our interactive course to master JavaScript from start to finish.
Master JavaScriptinfo Remove ads

Winning the Game

And with that being said, all that's left to do is giving a way for the player to actually win the game. We will end the game by adding some animations to the flag at the end of the map:

Winning the game

We will make the flag descend and fire particles. This time, however, we don't have any animation for the flag in our atlas. Instead, we will use a tileSprite for it using our tileset. This way you can see how to use sprites directly from your tileset for inanimate objects, instead of having to use an atlas like we did for every other game object so far. First, create a new Flag class in your gameObjects folder, and import it into your Game scene:

Copied to clipboard! Playground
class Flag {
    constructor(scene) {
        this.scene = scene;
    }
}

// In your Game.js create method:
this.flag = new Flag(this);
Flag.js

In your constructor, you want to define a couple of variables:

Copied to clipboard!
const flagObject = scene.map.getObjectLayer('flag').objects[0];
const flagCoordinates = scene.tileset.texCoordinates[962]; 
const flagRoot = scene.platform.getTileAt(75, 23);
Flag.js

Just like we did for every other game object, we want to retrieve it from Tiled using the getObjectLayer method of our map. To get the sprite for the flag, we can use the texCoordinates array on our tileset. It is at the 962 index position. You can get this information from your tileset in Tiled.

Getting the ID from a tileset

To make it easier to keep track of things, you can store these index values in an object, or even create a custom function for retrieving the coordinates.

Copied to clipboard! Playground
const objects = {
    flag: 962
};

scene.tileset.texCoordinates[objects.flag];

// Or you can define a custom function for improving readability
const getObjectCoordinates = object => scene.tileset.texCoordinates[objects[object]];

getObjectCoordinates('flag');
Flag.js
Example on how to improve readability

The third variable is used for getting the tile at a given tile position on the world with getTileAt. We will use this variable to add a collision for a specific tile (The root of the flag). But before doing that, let's create the flag sprite using a tileSprite:

Copied to clipboard!
this.sprite = scene.add.tileSprite(flagObject.x, flagObject.y, 16, 16, 'tiles')
    .setOrigin(0, 1)
    .setTilePosition(flagCoordinates.x, flagCoordinates.y);
Flag.js

Using the setTilePosition method, we can make the tileSprite use the sprite of the flag. Also note that you want to pass tiles to the tileSprite function rather than the atlas this time. After creating the sprite, you want to add a collision between Mario and the root of the flag. To add a collision to a specific tile, you can use the setCollisionCallback function on it:

Copied to clipboard!
flagRoot.setCollisionCallback(() => {
    flagRoot.collisionCallback = null;
});
Flag.js
If you want the callback to be fired only once, make sure you set the callback to null immediately.

For now, if you try things out, you may notice you are unable to jump on top of the box.

Wrong collision box for the flag.

This is because the collision boxes are preventing us from doing so. If you remember from the first tutorial, we've defined for the platform which tiles should have collisions with setCollisionByExclusion. This function also accepts an array of tile indexes, so we can remove the left side of the flag. In Game.js, change the platform collision to the following:

Copied to clipboard!
- this.platform.setCollisionByExclusion(-1, true);
+ this.platform.setCollisionByExclusion([-1, 450], true);
Game.js
450 being the left side tile index of the flag

Again, to make things easier to remember, you can create a custom object for these indexes with more meaningful names, and reference those instead:

Copied to clipboard! Playground
const tiles = {
    EMPTY: -1,
    FLAG_LEFT: 450,
    ...
};

const noCollisionTiles = [
    tiles.EMPTY,
    tiles.FLAG_LEFT
];

this.platform.setCollisionByExclusion(noCollisionTiles, true);
Game.js

Now if you go back to the game and enable collision boxes, you can see you will be able to jump on top of the big box because the collision has been removed.

Fixing collision boxes
The collision box has been removed from the left side of the flag

Creating Particle Effects

Lastly, let's add an animation and some particle effects to the flag. Inside your setCollisionCallback function, add the following lines:

Copied to clipboard! Playground
const particles = scene.add.particles('atlas', 'mario-atlas_13');
const emitter = particles.createEmitter({
    x: flagObject.x,
    y: flagObject.y - flagObject.height,
    scale:  { start: 1, end: 0 },
    speed:  { min: 50, max: 100 },
    angle:  { min: 0, max: -180 },
    rotate: { min: 0, max: 360 },
    alpha: .5
});
Flag.js

You can create new particles by calling add.particles on your scene, with a key and a frame. We're using the last frame from our atlas. It also needs an emitter, that you can create with the createEmitter function. You can find all available options for the configuration on their official documentation. As you can see from the code example above, you have a ton of options you can tweak, all capable of taking even intervals. Below your emitter, let's also add a tween that will make the flag descend and stop the particle effect after the animation has completed:

Copied to clipboard! Playground
scene.tweens.add({
    targets: this.sprite,
    ease: 'Linear',
    y: '+=60',
    duration: 800,
    onComplete: () => emitter.stop()
});

this.scene.input.keyboard.shutdown();
Flag.js

Just like the built-in particle system, this has just as many configuration options. Make sure you set the flag's sprite as the target and call emitter.stop once the tween has completed. One cool thing you can do with tweens is specifying a string with += operations that will start the tween from the current value and adds 60 to it in our case. Again, you can disabled movements by removing keyboard inputs, and you can also call a new scene that congratulates the player for winning the game. 🎉


Conclusion

And with that, you've just finished your very first game in Phaser! You've successfully created the base gameplay mechanics of Mario! If you've reached this far, congratulations! You've learned how to create tilemaps in Tiled with various game objects, how to load them into Phaser, and make these objects interactive.

How to add a player that you can control with keyboard input, and how you can animate these sprites to bring them to life. And lastly, you now also know about tweens and particles. If you would like to continue your journey in game development, check out how you can recreate the famous Atari Breakout using Phaser:

Building The Game Breakout Using JavaScript

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
Mentoring

Rocket Launch Your Career

Speed up your learning progress with our mentorship program. Join as a mentee to unlock the full potential of Webtips and get a personalized learning experience by experts to master the following frontend technologies:

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.