<Emanuele Feronato/>
home

HTML5 prototype of iOS hit “Hero Slide” made with Phaser – adding animations and matching tiles

Did you enjoy the first step of Hero Slide prototype made with Phaser? Time to proceed to next step, you already know how to: * Initialize the game * Place random tiles on the map * Move tiles with WASD keys And now you’ll see these new features: * Add animation to tile creation and movement * Match tiles The game matches two tiles when the player tries to move two adjacent tiles of the same kind in a direction but they both can’t move, in this case similar tiles will slide one over the other, popping one slide, freeing the game field and allowing you to keep playing. I really suck when it’s time to explain game mechanics, so you’d better have a look at the original iOS game. Now, play the game with WASD key – focus the canvas first.
As you can see, animations make the game look smoother and you can match tiles. And this is the commented source code, with new lines highlighted:
// the game
var game;

// size of each tile, in pixels
var tileSize = 120;

// different kinds of tiles
var tileTypes = 5;

// the game array, the board will be stored here
var gameArray = [];

// field size, in tiles. This will represent a 4x4 tiles field
var fieldSize = 4;

// creation of the game
window.onload = function() {	
	game = new Phaser.Game(480, 480);
     game.state.add("PlayGame", playGame);
     game.state.start("PlayGame");
}


var playGame = function(game){}


playGame.prototype = {
     preload: function(){
          
          // preloading the assets
          game.load.spritesheet("tiles", "tiles.png", tileSize, tileSize);          
     },
     create: function(){ 
          
          // initializing game board
          for(var i = 0; i < fieldSize; i++){
               gameArray[i] = [];
               for(var j = 0; j < fieldSize; j++){
                    
                    // each array item is an object with a tile value (0: empty) and a sprite (null: no sprite)
                    gameArray[i][j] = {
                         tileValue : 0,
                         tileSprite: null
                    };
               }
          }
          
          // function to add a new item, will be explained later
          this.addItem(); 
          
          // liteners to handle WASD keys. Each key calls handleKey function
          this.upKey = game.input.keyboard.addKey(Phaser.Keyboard.W);
          this.upKey.onDown.add(this.handleKey, this);
    		this.downKey = game.input.keyboard.addKey(Phaser.Keyboard.S);
    		this.downKey.onDown.add(this.handleKey, this);
    		this.leftKey = game.input.keyboard.addKey(Phaser.Keyboard.A);
    		this.leftKey.onDown.add(this.handleKey, this);
    		this.rightKey = game.input.keyboard.addKey(Phaser.Keyboard.D);
    		this.rightKey.onDown.add(this.handleKey, this);    
     },
     
     // this function will add a new item to the board
     addItem: function(){
     
          // emptySpots is an array which will contain all the available empty tiles where to place a new item
          var emptySpots = [];
          
          // now we loop through the game board to check for empty tiles
          for(var i = 0; i < fieldSize; i++){
               for(var j = 0; j < fieldSize; j++){
               
                    // remember we define an empty tile as a tile whose tileValue is zero
                    if(gameArray[i][j].tileValue == 0){
                    
                         // at this time we push a Point with tile coordinates into emptySpots array
                         emptySpots.push(new Phaser.Point(j, i));
                    }
               }
          }
          
          // newSpot is a randomly picked item in emptySpots array
          var newSpot = Phaser.ArrayUtils.getRandomItem(emptySpots);
          
          // if newSpot is not null this means we have a place where to put a new tile
          if(newSpot != null){
          
               // selecting a random value between 1 and tileTypes
               var tileType = game.rnd.between(1, tileTypes);
               
               // updating game array with the new tile value and sprite
               gameArray[newSpot.y][newSpot.x] = {
                    tileValue: tileType,
                    tileSprite: game.add.sprite(newSpot.x * tileSize, newSpot.y * tileSize, "tiles", tileType - 1)
               }
               
               // we start with the alpha at zero to create a "fade in" tween
               gameArray[newSpot.y][newSpot.x].tileSprite.alpha = 0;
               
               // here is the fade in effect
               var fadeTween = game.add.tween(gameArray[newSpot.y][newSpot.x].tileSprite).to({
                    alpha: 1
               }, 500, Phaser.Easing.Linear.None, true);
               
               // now the player can move
               fadeTween.onComplete.add(function(e){
                    
                    // the player can move again
                    this.canMove = true;    
               }, this);
          }
     },
     
     // this function handles player movements
     handleKey: function(e){
          
          // first of all, let's see if the player can move
          if(this.canMove){
          
               // if the player can move, let's set canMove to false to prevent the player to move twice
               this.canMove=false;
               
               // time to check for the keycode which generated the event
               switch(e.keyCode){
               
                    // "A" key (left)
                    case Phaser.Keyboard.A:
                         
                         // we scan for game field, from TOP to BOTTOM and from LEFT to RIGHT starting from the 2nd column
                         for(var i = 0; i < fieldSize; i++){
                              for(var j = 1; j < fieldSize; j++){
                              
                                   // we can move a tile if it's not empty and the tile on its left is empty
                                   if(gameArray[i][j].tileValue != 0 && gameArray[i][j - 1].tileValue == 0){
                                        
                                        // moving the sprite on the left
                                        var moveTween = game.add.tween(gameArray[i][j].tileSprite).to({
                                             x: gameArray[i][j].tileSprite.x - tileSize     
                                        }, 200,  Phaser.Easing.Linear.None, true);
                                        
                                        // copying the content of the current tile on the tile on its left
                                        gameArray[i][j - 1] = {
                                             tileValue: gameArray[i][j].tileValue,
                                             tileSprite: gameArray[i][j].tileSprite
                                        }
                                        
                                        //setting current tile to empty
                                        gameArray[i][j] = {
                                             tileValue: 0,
                                             tileSprite: null
                                        }
                                   }
                                   else{
                                        // we can match a tile if it's not empty and the tile on its left has the same value
                                        if(gameArray[i][j].tileValue != 0 && gameArray[i][j - 1].tileValue == gameArray[i][j].tileValue){
                                             
                                             // moving the sprite on the left
                                             var moveTween = game.add.tween(gameArray[i][j].tileSprite).to({
                                                  x: gameArray[i][j].tileSprite.x - tileSize     
                                             }, 200,  Phaser.Easing.Linear.None, true);
                                             
                                             // no need to copy the content of the current tile on the tile on its left, but we are going to remove
                                             // the sprite we won't use any longer once the tween has been completed
                                             moveTween.onComplete.add(function(e){
                                                  e.destroy();   
                                             }, this);
                                             
                                             //setting current tile to empty
                                             gameArray[i][j] = {
                                                  tileValue: 0,
                                                  tileSprite: null
                                             }
                                        }
                                   }
                              }
                         }
                    break;
                    
                    // "W" key (up)
                    case Phaser.Keyboard.W:
                    
                         // we scan for game field, from TOP to BOTTOM and from LEFT to RIGHT starting from the 2nd row
                         // applying the same concepts seen before
                         for(var i = 1; i < fieldSize; i++){
                              for(var j = 0; j < fieldSize; j++){
                                   if(gameArray[i][j].tileValue != 0 && gameArray[i - 1][j].tileValue == 0){
                                        var moveTween = game.add.tween(gameArray[i][j].tileSprite).to({
                                             y: gameArray[i][j].tileSprite.y - tileSize     
                                        }, 200,  Phaser.Easing.Linear.None, true);
                                        gameArray[i - 1][j ] = {
                                             tileValue: gameArray[i][j].tileValue,
                                             tileSprite: gameArray[i][j].tileSprite
                                        }
                                        gameArray[i][j] = {
                                             tileValue: 0,
                                             tileSprite: null
                                        }
                                   }
                                   else{
                                        if(gameArray[i][j].tileValue != 0 && gameArray[i - 1][j].tileValue == gameArray[i][j].tileValue){
                                             var moveTween = game.add.tween(gameArray[i][j].tileSprite).to({
                                                  y: gameArray[i][j].tileSprite.y - tileSize     
                                             }, 200,  Phaser.Easing.Linear.None, true);
                                             moveTween.onComplete.add(function(e){
                                                  e.destroy();   
                                             }, this);
                                             gameArray[i][j] = {
                                                  tileValue: 0,
                                                  tileSprite: null
                                             }
                                        }     
                                   }
                              }
                         }
                    break;
                    
                    // "D" key (right)
                    case Phaser.Keyboard.D:
                    
                         // we scan for game field, from TOP to BOTTOM and from RIGHT to LEFT starting from the next-to-last column
                         // applying the same concepts seen before
                         for(var i = 0; i < fieldSize; i++){
                              for(var j = fieldSize - 2; j >= 0; j--){
                                   if(gameArray[i][j].tileValue != 0 && gameArray[i][j + 1].tileValue == 0){
                                        var moveTween = game.add.tween(gameArray[i][j].tileSprite).to({
                                             x: gameArray[i][j].tileSprite.x + tileSize     
                                        }, 200,  Phaser.Easing.Linear.None, true);
                                        gameArray[i][j + 1] = {
                                             tileValue: gameArray[i][j].tileValue,
                                             tileSprite: gameArray[i][j].tileSprite
                                        }
                                        gameArray[i][j] = {
                                             tileValue: 0,
                                             tileSprite: null
                                        }
                                   }
                                   else{
                                        if(gameArray[i][j].tileValue != 0 && gameArray[i][j + 1].tileValue == gameArray[i][j].tileValue){
                                             var moveTween = game.add.tween(gameArray[i][j].tileSprite).to({
                                                  x: gameArray[i][j].tileSprite.x + tileSize     
                                             }, 200,  Phaser.Easing.Linear.None, true);
                                             moveTween.onComplete.add(function(e){
                                                  e.destroy();   
                                             }, this);
                                             gameArray[i][j] = {
                                                  tileValue: 0,
                                                  tileSprite: null
                                             }
                                        }
                                   }
                              }
                         }
                    break;
                    
                    // "S" key (down)
                    case Phaser.Keyboard.S:
                         
                         // we scan for game field, from BOTTOM to TOP and from LEFT to RIGHT starting from the next-to-last row
                         // applying the same concepts seen before
                         for(var i = fieldSize - 2; i >= 0; i--){
                              for(var j = 0; j < fieldSize; j++){
                                   if(gameArray[i][j].tileValue != 0 && gameArray[i + 1][j].tileValue == 0){
                                        var moveTween = game.add.tween(gameArray[i][j].tileSprite).to({
                                             y: gameArray[i][j].tileSprite.y + tileSize     
                                        }, 200,  Phaser.Easing.Linear.None, true);
                                        gameArray[i + 1][j ] = {
                                             tileValue: gameArray[i][j].tileValue,
                                             tileSprite: gameArray[i][j].tileSprite
                                        }
                                        gameArray[i][j] = {
                                             tileValue: 0,
                                             tileSprite: null
                                        }
                                   }
                                   else{
                                        if(gameArray[i][j].tileValue != 0 && gameArray[i + 1][j].tileValue == gameArray[i][j].tileValue){
                                             var moveTween = game.add.tween(gameArray[i][j].tileSprite).to({
                                                  y: gameArray[i][j].tileSprite.y + tileSize     
                                             }, 200,  Phaser.Easing.Linear.None, true);
                                             moveTween.onComplete.add(function(e){
                                                  e.destroy();   
                                             }, this);
                                             gameArray[i][j] = {
                                                  tileValue: 0,
                                                  tileSprite: null
                                             }
                                        }
                                   }
                              }
                         }
                    break;
                    
               } 
               
               // at the end of player input, we add a new item
               this.addItem();               
          }
     }
}
That’s all at the moment, during next step I will show you how to make bombs explode and how to control game with swipe, meanwhile download the source code.
230 games covered