Creating an html5 game like concentration

In this tutorial, we are going to explore various features of the phaser library by creating an html5 game like concentration. One of the advantages of using the phaser library is its cross-platform compatibility. And it is very easy to prototype a game. You can learn so much things by going into the phaser website.

There are so many tutorials on internet about phaser or this games. But here, we are going to explore various aspects like loading texturepacker spritesheet, using phaser custom signals, phaser tweens etc. It is not what you create, but how you create the game.

Before going into this tutorial, let us check how it is going to look like.

First, you need to setup a local server on your computer. Then go to the phaser github project page and download the entire thing as zip. If you use git, then you can clone the repo. After downloading that, go to the resources/Project Templates/ and copy the ‘Full Screen Mobile’ folder. Paste the folder into your localhost directory. We are going to edit the files inside that for this game.

Preparing the assets

In this tutorial, I am going to use the animal pack assets by Kenney. You can download the assets for free here.

Our game is going to be 640×960 resolution. The above asset is of 256×256 size each, so we need to convert the assets to 128×128 size. You can use photoshop or GIMP for that task. I used imagemagick and it is very easy to do this in imagemagick by entering this command.

mogrify -resize 50% *.png

This will convert all the images to half of its size at once. After resizing the tiles, I created a 128×128 grey square image “blind.png” and put it in the assets folder. This is for showing the back side of the tiles when it is hidden.

blind.png tile

blind.png tile

After doing this, we need to convert all the images into a spritesheet using TexturePacker. You can select the Phaser – JSON format to convert all the animal tiles including the blind.png tile.

Also Read:   Drawing a circle using Gizmos in Unity3D

So it will look like this.

Animal tile spritesheet

Animal tile spritesheet

I also have an image for the play button which looks like this.

play

After creating a static color bg, our images folder will look like this.

The images folder inside our project

The images folder inside our project

That’s all the assets we need. Now we can go into the coding part.

Coding

Our game is going to be 640×960 resolution so we need to set it in the index.html page. Open up index.html and change the line where the new Phaser.Game is created to this.

var game = new Phaser.Game(640, 960, Phaser.AUTO, 'game');

We also need to add some css to get our game to fit properly to the browser. So add this style block just before closing the <head> tag.

<style type="text/css">
    #game{
        position: absolute;
        top: 0;
        bottom: 0;
        left: 0;
        right: 0;
    }
</style>

The above css is going to fill our game container to the browser window. So the scaling will work properly.

Next, edit our Boot.js and remove all the preloader related codes because we are not using any preloading art for now. So our Boot.js looks like this.

Concentration = {

    /* Here we've just got some global level vars that persist regardless of State swaps */
    score: 0,

    /* If the music in your game needs to play through-out a few State swaps, then you could reference it here */
    music: null,

    /* Your game can check BasicGame.orientated in internal loops to know if it should pause or not */
    orientated: false

};

Concentration.Boot = function (game) {
};

Concentration.Boot.prototype = {

    init: function () {

        this.input.maxPointers = 1;
        this.stage.disableVisibilityChange = true;

        if (this.game.device.desktop)
        {
            this.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL;
            this.scale.pageAlignHorizontally = true;
            this.scale.pageAlignVertically = true;
        }
        else
        {
            this.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL;
            this.scale.pageAlignHorizontally = true;
            this.scale.pageAlignVertically = true;
            this.scale.forceOrientation(false, true);
            this.scale.setResizeCallback(this.gameResized, this);
            this.scale.enterIncorrectOrientation.add(this.enterIncorrectOrientation, this);
            this.scale.leaveIncorrectOrientation.add(this.leaveIncorrectOrientation, this);
        }

    },

    preload: function () {
    },

    create: function () {
        this.state.start('Preloader');
    },

    gameResized: function (scale, parentBounds) {

    },

    enterIncorrectOrientation: function () {

        Concentration.orientated = false;

        document.getElementById('orientation').style.display = 'block';

    },

    leaveIncorrectOrientation: function () {

        Concentration.orientated = true;

        document.getElementById('orientation').style.display = 'none';

    }

};

You can see that we also changed the ‘BasicGame’ to ‘Concentration’ i.e., our game name.

Next, in the Preloader.js, we load our assets in the preload method.

preload: function () {
    this.load.image('bg','images/bg.png');
    this.load.image('playButton', 'images/play.png');
    this.load.atlas('animals', 'images/animals.png', 'images/animals.json');
}

Then in the create method we will start our MainMenu

create: function () {
    this.state.start("MainMenu");
},

We are not using the logic in the update, so clean out the update method.

Also Read:   Start Developing HTML5 games in Phaser using TypeScript

In our MainMenu.js we will add a bg and the play button like so.

create: function () {
    this.bg = this.add.sprite(0,0,'bg');
    this.playButton = this.add.button(this.game.width/2, this.game.height/2, 'playButton', this.startGame, this);
    this.playButton.anchor.setTo(0.5);	
},
startGame: function (pointer) {
    this.state.start('Game');
},

Now, we are going to add the game logic. It is straight forward logic here, I am not going to explain everything in detail. If you have any questions, I will be happy to answer in the comments.

Game.js

Concentration.Game = function (game) {

    //	When a State is added to Phaser it automatically has the following properties set on it, even if they already exist:

    this.game;		//	a reference to the currently running game
    this.add;		//	used to add sprites, text, groups, etc
    this.camera;	//	a reference to the game camera
    this.cache;		//	the game cache
    this.input;		//	the global input manager (you can access this.input.keyboard, this.input.mouse, as well from it)
    this.load;		//	for preloading assets
    this.math;		//	lots of useful common math operations
    this.sound;		//	the sound manager - add a sound, play one, set-up markers, etc
    this.stage;		//	the game stage
    this.time;		//	the clock
    this.tweens;	//	the tween manager
    this.world;		//	the game world
    this.particles;	//	the particle manager
    this.physics;	//	the physics manager
    this.rnd;		//	the repeatable random number generator

    //	You can use any of these from any function within this State.
    //	But do consider them as being 'reserved words', i.e. don't create a property for your own game called "world" or you'll over-write the world reference.

    this.bg;
    this.prevTile;
    this.tiles;
    this.busy;
};

Concentration.Game.prototype = {

    create: function () {
        this.prevTile = null;



        this.bg = this.game.add.sprite(0,0,'bg');
        this.tiles = this.game.add.group();
        var animals = ["elephant","giraffe","hippo","monkey","panda","parrot","pig","rabbit","snake","penguin"];
        animals = animals.concat(animals);
        var tileSize = 128;
        var cols = 5;
        for (var i = 0; i < 20; i++) {
            var xx = (i%cols) * tileSize;
            var yy = Math.floor(i/cols) * tileSize;
            var randomName = Phaser.ArrayUtils.removeRandomItem(animals);
            var tile = new Tile(this.game,xx,yy,"animals",randomName+".png");
            this.tiles.add(tile);
            tile.animal = randomName;
            tile.onTap.add(this.onTileTap,this);
        }
        this.tiles.x = this.game.width/2 - this.tiles.width/2 + (tileSize/2);
        this.tiles.y = this.game.height/2 - this.tiles.height/2 + (tileSize/2);
    },

    onTileTap:function (tile) {
        if(this.busy){
            return;
        }
        this.busy = true;
        tile.reveal();

        var t = this.game.time.create(true);
        if(this.prevTile === null){
            this.prevTile = tile;
            this.busy = false;
            return;
        }
        t.add(1000,function () {
            if(this.prevTile.animal !== tile.animal){
                console.log("No match: ",this.prevTile.animal,tile.animal);
                this.prevTile.hide();
                tile.hide();
                this.prevTile = null;
            }else if(this.prevTile.animal === tile.animal){
                console.log("Match: ",this.prevTile.animal,tile.animal);
                this.tiles.removeChild(this.prevTile);
                this.tiles.removeChild(tile);
                this.prevTile = null;
                if(this.tiles.children.length===0){
                    this.quitGame();
                }
            }
            this.busy = false;
        },this);
        t.start();

    },

    update: function () {

        //	Honestly, just about anything could go here. It's YOUR game after all. Eat your heart out!

    },

    quitGame: function (pointer) {

        //	Here you should destroy anything you no longer need.
        //	Stop music, delete sprites, purge caches, free resources, all that good stuff.

        //	Then let's go back to the main menu.
        this.state.start('MainMenu');

    }

};

You can see that I used a Tile class to create the animal tiles. We are going to create it by extending Phaser.Group.

var Tile = (function () {
    var Tile = function (game,x,y,image,frame) {
        Phaser.Group.call(this,game);
        this.animal = "";
        this.hidden = true;
        this.onTap = new Phaser.Signal();
        this.x = x;
        this.y = y;
        this.front = this.create(0,0,image,frame);
        this.front.anchor.setTo(0.5);
        this.front.scale.setTo(0,1);
        this.back = this.create(0,0,image,'blind.png');
        this.back.anchor.setTo(0.5);

        this.back.inputEnabled = true;
        this.back.events.onInputDown.add(this.dispatchStateChange,this);
    };

    Tile.prototype = Object.create(Phaser.Group.prototype);
    Tile.prototype.constructor = Tile;

    Tile.prototype.hide = function () {
        this.hidden = true;
        var t1 = this.game.add.tween(this.back.scale).to({
            x:1
        },100,Phaser.Easing.Linear.None);
        var t2 = this.game.add.tween(this.front.scale).to({
            x:0
        },100,Phaser.Easing.Linear.None);
        t2.chain(t1);
        t2.start();
    };

    Tile.prototype.dispatchStateChange = function () {
        this.onTap.dispatch(this);
    };

    Tile.prototype.reveal = function () {
        this.hidden = false;
        var t1 = this.game.add.tween(this.back.scale).to({
            x:0
        },100,Phaser.Easing.Linear.None);
        var t2 = this.game.add.tween(this.front.scale).to({
            x:1
        },100,Phaser.Easing.Linear.None);
        t1.chain(t2);
        t1.start();
    };


    return Tile;
}());

There is a variable called onTap which is a Phaser.Signal. A signal is like an event listener but it is more flexible and easier than events. We can pass in custom data to the listeners easily.

Also Read:   7 Lerping tricks you need to know as a Game Developer

Using Phaser Signals

We can create a signal like this,

var heroHurt = new Phaser.Signal();

Then you can listen for this signal on any other external classes.

heroHurt.add(function(){
 //Some logic
},this);

And inside our hero when he is hurt, call

heroHurt.dispatch()

And all the listeners are notified of this event.

Similarly, we added an onTap listener where the Game class can listen to and when we click on the ‘back’ Sprite, it is dispatched. In other words, phaser groups don’t have an on click listener, we just created one using Signals.

Now the game is complete and you can test run it. Before that we need to include the phaser.min.js and our Tile class in the index.html file.

<script src="js/phaser.min.js"></script>
<script src="src/Boot.js"></script>
<script src="src/Preloader.js"></script>
<script src="src/MainMenu.js"></script>
<script src="src/Tile.js"></script>
<script src="src/Game.js"></script>

If you like this tutorial you can share your experiences. If you want to change my pacing or style let me know in the comments. You can download the entire game project here.

[Total: 0    Average: 0/5]
  • Pingback: Phaser - javascript | Pearltrees()

  • Pingback: Phaser | Pearltrees()

  • Pingback: Animated particles in Phaser - Codetuto()

  • Chris Clower

    If you double-click fast enough on a tile, sometimes the game will think you found a match and remove that tile.

  • shohanur rahaman

    please explain your code in Tile.js

  • shohanur rahaman

    in Tile.js I can not understand onTap signal is it a touch event for mobile device.

    • Vinod

      The onTap is a custom signal which we create. Phaser groups don’t have input events. So we just created one. And this signal is activated when we tap on the ‘back’ sprite.
      So whoever is listening for this onTap signal are all notified when we tap on it.

      Signals are not bound to inputs. It can be used for any events that are happening in a game. Like shown in the hero hurt example shown in the post.

      Instead of this, we can call the onTileTap method directly. But using signals is a way to decouple things from each other.

  • FYI, you refer to a retry image which isn’t included in the project file, nor does it appear to be used.

    this.load.image(‘retryButton’, ‘images/retry.png’);

    • Vinod

      Glad you looked into it.
      It was for a game win scene. It was removed to not to make this tutorial too lengthy.