Adding Coins and Lives to the Mario-Style HTML5 Platformer

NOTE: This tutorial is not updated to the latest Quintus version and doesn’t run with it.

This tutorial is the third part in the Mario HTML5 platformer series, so get ready to dig in deeper into the awesome Quintus game framework. In this tutorial we’ll learn how to add coins, lives to our player and how to show all of this in a “player stats” area.

Requirements

Setup a local webserver. We need to run the code in this tutorial in a web server and not by just double clicking on the files. WAMP for Windows, MAMP for Mac. On linux just type sudo apt-get install apache2.

Have your favorite IDE ready (Netbeans, Eclipse, Sublime2, Notepad++, VIM, or any tool you use for coding).

Have your knuckles cracked and ready for a new dive into HTML5 gamedev.

Tutorial Assets

Get the full tutorial source code and images here. All images used in this tutorial have a Public Domain license.

Heads up!
I’ve provided the Quintus files among the tutorial source code but keep in mind that the framework is under heavy development. It’s be best if you just grab them straight from the Github page. Also, keep an eye on the repository as you work on your games since new features are being added.

Player Stats

In these series we’ve been working all along with just one scene: the level that contains the actual game. Quintus allows us to keep on many scenes on the screen so that for instance we can be scrolling through the level in one, but the other ones stay on their same place and don’t get scrolled.

Being more clear on this point, imagine we just use one scene, the level one, and we add player stats to it such as the number of lives and the number of coins. It’s all gonna look good until you start working and the viewport camera moves away from your initial location. The player stats will stay behind as the rest of the level. This concept is explored in more depth in our HTML5 Educational Game tutorial.

Anyway, let’s create a second scene called “gameStats”:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

Q.scene("gameStats",function(stage){

varstatsContainer=stage.insert(newQ.UI.Container({

fill:"gray",

x:960/2,

y:620,

border:1,

shadow:3,

shadowColor:"rgba(0,0,0,0.5)",

w:960,

h:40

})

);

varlives=stage.insert(newQ.UI.Text({

label:"Lives x 3",

color:"white",

x:-300,

y:0

}),statsContainer);

varcoins=stage.insert(newQ.UI.Text({

label:"Coins x 0",

color:"white",

x:300,

y:0

}),statsContainer);

});

What we are doing here is create a new scene that has a gray container which goes on the bottom of the page. This container has two Q.UI.Text objects, one for the lives and one for the coins. They both start with default values. In order to learn more about the Q.UI.Text and the containers check the file quintus_ui.js from the library and the HTML5 Educational Game tutorial.

Staging the game stats scene would be the last step. The asset loading should look like this (see that we are also adding a coin.png asset file, to be used next):

The second parameter when staging the scene is the index (0 by default). Scenes with higher index show on top of scenes with lower index.

Coins

Righto. All platformers need coins or something like them (diamonds, gold, fruits?). Their implementation using Quintus and the stuff we’ve learned so far is quite straightforward:

1

2

3

4

5

Q.Sprite.extend("Coin",{

init:function(p){

this._super(p,{asset:"coin.png"});

}

});

We’ll load the coins in our level initialisation using a JSON object in a similar manner to how we did it with the enemies:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

//level assets. format must be as shown: [[ClassName, params], .. ]

varlevelAssets=[

["GroundEnemy",{x:18*70,y:6*70,asset:"slime.png"}],

["VerticalEnemy",{x:800,y:120,rangeY:70,asset:"fly.png"}],

["VerticalEnemy",{x:1080,y:120,rangeY:80,asset:"fly.png"}],

["GroundEnemy",{x:6*70,y:3*70,asset:"slime.png"}],

["GroundEnemy",{x:8*70,y:70,asset:"slime.png"}],

["GroundEnemy",{x:18*70,y:120,asset:"slime.png"}],

["GroundEnemy",{x:12*70,y:120,asset:"slime.png"}],

["Coin",{x:300,y:100}],

["Coin",{x:360,y:100}],

["Coin",{x:420,y:100}],

["Coin",{x:480,y:100}],

["Coin",{x:800,y:300}],

["Coin",{x:860,y:300}],

["Coin",{x:920,y:300}],

["Coin",{x:980,y:300}],

["Coin",{x:1040,y:300}],

["Coin",{x:1100,y:300}],

["Coin",{x:1160,y:300}],

["Coin",{x:1250,y:400}],

["Coin",{x:1310,y:400}],

["Coin",{x:1370,y:400}]

];

//load level assets

stage.loadAssets(levelAssets);

Heads up!
It’s recommended you try to load all the entities in the game such as enemies, items, etc, using JSON objects. JSON is a data exchange protocol which is commonly used to receive and send information to and from the web. If your plan is to eventually load level data from the web or from the local storage it’s good that you keep JSON in mind and get used to working with this format.

Our level can be filled up with coins now but we still need to be able to grab them and keep a total of coins. Let’s update a bit our Player definition:

This is firstly, setting an initial value of 0 coins. We are checking the collision with coin objects. If we find a coin then destroy the coin and increase the number of coins in 1. We still need to update the GUI, which we’ll be doing next.

Searching for the GUI

We grabbed a coin, destroyed it and increased our coin count. The GUI needs to be updated, but how do we find it? we need to search for it. Quintus has it’s own way of searching for objects:

1

2

3

4

5

6

7

8

9

this.on("hit.sprite",function(collision){

if(collision.obj.isA("Coin")){

collision.obj.destroy();

this.p.coins++;

varcoinsLabel=Q("UI.Text",1).items[1];

coinsLabel.p.label='Coins x '+this.p.coins;

}

});

By doing

1

Q("UI.Text",1).items[1];

we are searching for all Q.UI.Text objects in the scene located at index 1. This returns an object, where the property “items” contains an Array with all the results. In this case the index 1 (second item in the Array) contains the text we are looking for. Index 0 of the array contains the lives text.

Once we find the text object we are looking for, we just update the label property (object.p.label, don’t forget the p which we always use to access an entity’s parameters in Quintus).

Player Lives

Our game so far is extremely tough. One strike and you are out! we’ll implement now player lives or energy. If the lives run out then we are getting the game over kick.

There are many ways in which lives / energy can be implemented. The way it’ll be in this example is:

The player is hit by an enemy (same collision rules we have in place)

The lives go minus one

The player is “invincible” for 1 second, so that if you are hit again right away it won’t take a second life

After the 1 second you can be hit again if you are not careful

If the lives run out.. game over!

When the player is hit, ideally, the player sprite should be blinking for that second or have some sort of visual cue. Since this is just a tut not a real game we won’t show any visual cue.

Modify the enemies so that now they don’t kill you, but damage() you instead.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

//component for common enemy behaviors

Q.component("commonEnemy",{

added:function(){

varentity=this.entity;

entity.on("bump.left,bump.right,bump.bottom",function(collision){

if(collision.obj.isA("Player")){

collision.obj.damage();

}

});

entity.on("bump.top",function(collision){

if(collision.obj.isA("Player")){

//make the player jump

collision.obj.p.vy=-100;

//kill enemy

this.destroy();

}

});

},

});

The damage() method in the player will follow the steps mentioned before. It will make you invincible for 1 second and then you’ll be damageable again. We also need to update step() to keep track of this 1 second. The following goes inside Player:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

step:function(dt){

if(Q.inputs["left"]&&this.p.direction=="right"){

this.p.flip="x";

}

if(Q.inputs["right"]&&this.p.direction=="left"){

this.p.flip=false;

}

if(this.p.timeInvincible>0){

this.p.timeInvincible=Math.max(this.p.timeInvincible-dt,0);

}

},

damage:function(){

//only damage if not in "invincible" mode, otherwise beign next to an enemy takes all the lives inmediatly

if(!this.p.timeInvincible){

this.p.lives--;

//will be invincible for 1 second

this.p.timeInvincible=1;

if(this.p.lives<0){

this.destroy();

Q.stageScene("endGame",1,{label:"Game Over"});

}

else{

varlivesLabel=Q("UI.Text",1).first();

livesLabel.p.label="Lives x "+this.p.lives;

}

}

}

This part:

1

varlivesLabel=Q("UI.Text",1).first();

is updating the GUI in a similar way to the coin counter, but using a first() method that the returned object comes with (remember that index 0 in the Array was the lives text).

This gives us a playable demo where you can collect coins, jump around, kill bad guys and if you are not careful they can take your lives away and kill you!

Congratulations

Congrats if you’ve got all the way here! you have now the basics of what can become a great game! Some ideas on what to build next:

Visual cue when the player is damaged

Multiple levels

Level boss

Grab extra lives

Enemies that take coins away instead of lives

What Else to Check Out

If you like my teaching style, at Zenva we have two high quality video courses on HTML5 game development where we build several examples of different types of games, such as a top-view shooting bad guys Zelda-style game, spaceship games, farming games, virtual pet games and many other examples!

Published by

Pablo Farias Navarro

Pablo is an educator, developer and entrepreneur. Founder of Zenva, Pablo has published over 30 online courses that teach game, app and web development. Pablo has also created educational content for companies such as Amazon and Intel.
View all posts by Pablo Farias Navarro