Winning or Losing

Foxnoid is now a playable game but you can’t win it (or lose it). In this chapter we’re going to solve that by adding both the game win and game over mechanics.

We’re going to use two new game states one for each type of ending. Once each situation happens we’re going to switch to the needed state. Game states are useful for many things but one special use that they excel at is when the nature of what is happening on your game changes. In the game over situation the player was playing the game but lost it and is no longer able to play, this fundamental difference from the previous situation is easily crafted with game state switches.

Winning and losing are important aspects of games. You can create games with just one of these items. A game with no winning scenario is a game where winning is basically seeing how long you can last before losing. Games without a losing scenario option are common these days, specially on mobile. Games where you can play forever until you win or even in some cases pay-to-win games where you can spend real money to advance a level or buy upgrades.

Flappy Bird is a game without a win scenario (or at least I don’t know about it). Basically these type of games work really well if they are hard and use leaderboards where players can compete with each other to have the best score.

Angry Birds is the kind of game where you can’t really lose, you just keep stuck on a level until you pass it. That behavior is so common with mobile arcade/puzzle games that is basically the standard thing now.

In Foxnoid we’re going to implement both a game win and a game over scenario.

Game Over Scenario

Game over will happen once the player runs out of lives. His initial lives number is set by the initWorld() function and updated if needed by the ballCollidesWithGround() function. What we’re going to do there is check if the player has zero lives and switch to a new game state called GameOver if needed. Lets alter that function:

game.js: the ballCollidesWithBlock() function with Game Over switch
 1 ballCollidesWithGround: function() {
 2     if (this.ball.y >= 470) {
 3         this.playerLives -= 1;
 4         this.resetBall();
 5     }
 6 
 7     /*
 8      Update player life display
 9      */
10 
11     this.livesDisplay.setText("Lives: " + this.playerLives);
12 
13     if (this.playerLives === 0) {
14         this.state.start("GameOver");
15     }
16 
17 }

The important part is this.state.start("GameOver") which switches to the new state. Our new state will look like this during the game:

Foxnoid game over screen
Foxnoid game over screen

We’re using the same background as the one in the game with two images for the text parts. We need to add these images to the Preload state, so edit the preload.js like this:

preload.js: the game state to load our assets
 1 GameStates.Preloader = {
 2     preload: function() {
 3         this.load.image('background', 'assets/background.jpg');
 4         this.load.image('player', 'assets/player.png');
 5         this.load.image('ball', 'assets/ball.png');
 6         this.load.image('block', 'assets/block.png');
 7         this.load.image('gameover', 'assets/gameover.png');
 8         this.load.image('taptoplay', 'assets/taptoplay.png');
 9     },
10     create: function(){
11         this.state.start('Game');
12     }
13 };

As you can see we simply added two images to the preload() function, this way they are available to our new game state once it loads. Talking about game states we need some steps when creating a new one. First thing we need to create a new file. Lets call it game_over.js and place it inside the js folder.

We need to include it in index.html as shown here

index.html: now includes our game over state
 1 <!DOCTYPE html>
 2 <html>
 3     <head>
 4         <meta charset="UTF-8" />
 5         <title>Foxnoid Game</title>
 6         <link rel="stylesheet" href="css/style.css" />
 7         <script defer src="js/phaser.min.js"></script>
 8         <script src="js/init.js"></script>
 9         <script defer src="js/preload.js"></script>
10         <script defer src="js/game.js"></script>
11         <script defer src="js/game_over.js"></script>
12     </head>
13     <body>
14 
15         <div id="game"></div>
16 
17     </body>
18 </html>

After including that file in the HTML we need to register the state in init.js as seen here:

init.js: with the new game over state
 1 var GameStates = {}; // <-- Object to hold all our game states.
 2 
 3 document.addEventListener("DOMContentLoaded", function()  {
 4 
 5     // Portrait game orientation. 
 6 
 7     var width = 320;
 8     var height = 480;
 9 
10     var game = new Phaser.Game(width, height, Phaser.CANVAS, "game");
11     
12     // Add the Game States the game has.
13     game.state.add('Preloader', GameStates.Preloader);
14     game.state.add('Game', GameStates.Game);
15     game.state.add('GameOver', GameStates.GameOver);
16     
17     // Now start the Preloader state.
18     game.state.start('Preloader');
19 
20 });

With those steps done we can code game_over.js

game_over.js: player lost, how sad
 1 GameStates.GameOver = {
 2     create: function() {
 3 
 4         // Add the background
 5         this.add.sprite(0, 0, 'background');
 6 
 7         // Add the message
 8         this.add.sprite(20, 30, 'gameover');
 9 
10         // Add the tap to play button
11         this.add.sprite(20, 300, 'taptoplay');
12     },
13 
14     update: function() {
15 
16         /**
17          * We just want to detect a tap. 
18          * If there is one, we switch back to the game
19          * state and start the game again
20          */
21 
22         if (this.input.pointer1.isDown) {
23             this.state.start('Game');
24         }
25 
26     }
27 
28 };

This new game state is pretty simple. On create() we build the screen with our messages and on update() we keep checking for a touch on the screen. If the player touches the screen we switch to the game state which restarts the game.

Game Win Scenario

The game win scenario will be the same as the game over scenario but with different images. As seen here

Player won!
Player won!

First lets add the new image to the preload state:

preload.js: the game state to load our assets
 1 GameStates.Preloader = {
 2     preload: function() {
 3         this.load.image('background', 'assets/background.jpg');
 4         this.load.image('player', 'assets/player.png');
 5         this.load.image('ball', 'assets/ball.png');
 6         this.load.image('block', 'assets/block.png');
 7         this.load.image('gameover', 'assets/gameover.png');
 8         this.load.image('taptoplay', 'assets/taptoplay.png');
 9         this.load.image('gamewin', 'assets/gamewin.png');
10     },
11     create: function(){
12         this.state.start('Game');
13     }
14 };

And create a file game_win.js in the js folder. We need to register this new game state in init.js and add it to the index.html.

index.html: now includes our game win state
 1 <!DOCTYPE html>
 2 <html>
 3     <head>
 4         <meta charset="UTF-8" />
 5         <title>Foxnoid Game</title>
 6         <link rel="stylesheet" href="css/style.css" />
 7         <script defer src="js/phaser.min.js"></script>
 8         <script src="js/init.js"></script>
 9         <script defer src="js/preload.js"></script>
10         <script defer src="js/game.js"></script>
11         <script defer src="js/game_over.js"></script>
12         <script defer src="js/game_win.js"></script>
13     </head>
14     <body>
15 
16         <div id="game"></div>
17 
18     </body>
19 </html>

After including that file in the HTML we need to register the state in init.js as seen here:

init.js: with the new game win state
 1 var GameStates = {}; // <-- Object to hold all our game states.
 2 
 3 document.addEventListener("DOMContentLoaded", function()  {
 4 
 5     // Portrait game orientation. 
 6 
 7     var width = 320;
 8     var height = 480;
 9 
10     var game = new Phaser.Game(width, height, Phaser.CANVAS, "game");
11     
12     // Add the Game States the game has.
13     game.state.add('Preloader', GameStates.Preloader);
14     game.state.add('Game', GameStates.Game);
15     game.state.add('GameOver', GameStates.GameOver);
16     game.state.add('GameWin', GameStates.GameWin);
17     
18     // Now start the Preloader state.
19     game.state.start('Preloader');
20 
21 });

With that done we can code the game_win.js file.

game_win.js: player won!
 1 GameStates.GameWin = {
 2     create: function() {
 3 
 4         // Add the background
 5         this.add.sprite(0, 0, 'background');
 6 
 7         // Add the message
 8         this.add.sprite(20, 30, 'gamewin');
 9 
10         // Add the tap to play button
11         this.add.sprite(20, 300, 'taptoplay');
12     },
13 
14     update: function() {
15 
16         /**
17          * We just want to detect a tap. 
18          * If there is one, we switch back to the game
19          * state and start the game again
20          */
21 
22         if (this.input.pointer1.isDown) {
23             this.state.start('Game');
24         }
25 
26     }
27 
28 };

We’ve created all files necessary for the game to be winnable but we still need to add that condition to our game.js state. The player will win once all the blocks are removed from the game. The best place to check that is during update() after the collision check that destroys the blocks. Lets add a function to that file:

game.js: new function to check for game win condition
1 checkGameWin: function () {
2     if (this.blocks.countLiving() === 0) {
3         this.state.start("GameWin");
4     }
5 }

The this.blocks.countLiving() call is from Phaser.Group and returns the number of sprites alive in a given group. Since each time there is a collision with a block we kill() it then that function will return zero once all blocks are cleared. When that happens we switch to the game win scenario that works just like the game over one.

We need to add it to the update() function:

game.js: the new update() function
1 update: function() {
2    this.handleTouchInput();
3    this.handleKeyboardInput();
4    this.checkHitWithBlocks();
5    this.checkHitWithPlayer();
6    this.ballCollidesWithGround();
7    this.checkGameWin();
8 }

Now you can play and win or lose the game!

Summary

In this chapter we added mechanics for winning and losing the game. In the next chapter we’re going to keep players engaged by adding levels to the game.