Quantcast
Channel: Envato Tuts+ Game Development
Viewing all articles
Browse latest Browse all 728

Getting Started With Phaser: Building "Monster Wants Candy"

$
0
0

In this extra-long tutorial, I'll break down the source for Monster Wants Candy, a multi-platform game my colleague and I built with Phaser, the HTML5 game engine. In this way, you'll gain a practical introduction to the engine, and will learn concepts you can use to build your own HTML5 mobile and browser games.

Introduction

If you want to create HTML5 games, it's good to choose a framework or engine. You could, of course, do it in plain JavaScript, but using a framework greatly speeds up development, and takes care of the things that are not so exciting but that have to be done. 

Phaser is a hot new HTML5 game development framework with a dedicated community, so if you haven't heard about it yet you should definitely give it a try!

Related Posts

What is Phaser?

Phaser is a framework for building both desktop and mobile HTML5 games. It was created by Photon Storm. The framework is written in pure JavaScript, but also contains TypeScript definition files, in case you're into that. 

Phaser code is heavily based on the Flash gamedev platform Flixel, so Flash developers can feel right at home. Under the hood, it uses the Pixi.js engine to take care of rendering everything on screen using Canvas, or WebGL if possible.

Phaser logo

It's quite new, but growing rapidly fast with the help of the active community at HTML5GameDevs forums. There are already many tutorials and articles available, and you can also check the official documentation, and a large collection of examples that can be very helpful during development. It is open sourced and freely available on GitHub, so you can dive directly into the source code and learn from it. 

The latest stable build of Phaser, at the time of writing, is version 2.0.7.

What is Monster Wants Candy?

When I start working on a game, I think about the core idea first and try to quickly set up a working prototype. In this case study, we start with a fairly simple demo of a game called Monster Wants Candy

Instead of working from a prototype, I will show you the structure of the project first, so you can understand the whole idea. We will then go through the chronological steps of our game: from loading the assets to creating the main menu and the actual game loop. You can check out the Monster Wants Candy demo right now to see what we'll be working on together. 

The coding was taken care of by Andrzej Mazur from Enclave Games (that's me!), and all the graphical assets were created by Robert Podgórski from Blackmoon Design.

Monster Wants Candy demo

The story of Monster Wants Candy is simple: an evil king has kidnapped your love and you have to collect enough candy to get her back. The gameplay is also simple: the sweets are falling down and you can tap them to eat them. The more points you gain from eating the candy, the better. If you miss any and they fall off the screen, you'll lose a life and the game will be over.

As you can see, it is a very simple game, but the structure is complete. You'll find that the most important use of the framework is for tasks like loading images, rendering sprites, and detecting user activity. It also makes for a good starting point from which you can copy the code, start fiddling with it, and build your own game.

Project Setup and Structure

You can read this handy article from the framework author himself about how to get started with Phaser, or you can copy the phaser.min.js file from the GitHub repo into your project directory and start working from scratch. You don't need an IDE—you can simply launch the index.html file in your browser and instantly see the changes you made in the source code.

Our project folder contains the index.html file which includes the HTML5 structure and all the necessary JavaScript files. There are two subfolders: img, which stores all of our graphic assets, and src, which stores the source code of the game. 

Here's how the folder structure looks:

Folder structure for Monster Wants Candy Phaser project

In the src folder, you'll find the JavaScript files—this is where the magic happens. In this tutorial, I will describe the purpose and the contents of every file in that folder.

You can see the source for each file in the GitHub repo for this tutorial.

Index.html

Let's start with the index.html file. It looks like a basic HTML5 website, but instead of adding the text and lots of HTML elements, we initialize the Phaser framework, which will render everything to a Canvas element.

<!DOCTYPE html><html><head><meta charset="utf-8" /><title>Monster Wants Candy demo</title><style> body { margin: 0; background: #B4D9E7; } </style><script src="src/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/Game.js"></script></head><body><script>
(function() {
	var game = new Phaser.Game(640, 960, Phaser.AUTO, 'game');
	game.state.add('Boot', Candy.Boot);
	game.state.add('Preloader', Candy.Preloader);
	game.state.add('MainMenu', Candy.MainMenu);
	game.state.add('Game', Candy.Game);
	game.state.start('Boot');
})();</script></body></html>

We define the usual structure of the HTML document with the doctype and some information in the <head>: charset encoding, title of the page, and CSS style. Usually, we would reference the external CSS file where we put all the styling, but we don't need it here—as I mentioned earlier, everything will be rendered on a Canvas element, so we won't have any HTML elements to style. 

The last thing to do is to include all of our JavaScript files: from the phaser.min.js file with the source code of the Phaser framework, to all of our own files containing the game's code. It is good to minify the number of requests in the browser by combining all the JavaScript files into one, so that your game loads faster, but for the purpose of this tutorial we will simply load them separately.

Let's move to the contents of the <body> tag, where we initialize the framework and start our game. The code is inside a self-invoking function; the first line looks like this:

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

This code will initialize Phaser with some defaults: 

640 is the game's Canvas width in pixels and 960 is the game's height.

Phaser.AUTO informs the framework how we want our game to be rendered to the Canvas. There are three options: CANVASWEBGL and AUTO. The first runs our game in the 2D context of the Canvas; the second uses WebGL to render it where possible (mostly desktop right now, but the mobile support is getting better); and the third leaves this decision to the framework, which will check whether WebGL is supported and decide whether the game can be rendered in this context—if it can't, then the 2D Canvas rendering will be used. 

The framework initialization will be assigned to the single object called game, which we will use when referencing the Phaser instance.

The next lines are all about adding states to our game:

game.state.add('Boot', Candy.Boot);

'Boot' is a state name and Candy.Boot is an object (defined in the next steps) that will be executed when we start that state. We're adding states for Boot (configuration), Preloader (loading assets), MainMenu (you guessed it; the main menu of our game) and Game (the main loop of the game). The last line, game.state.start('Boot'), starts the Boot state, so that the proper function from the Candy.Boot object will be executed.

As you can see, there's one main JavaScript game object created, with many others assigned within for special purposes. In our game we have BootPreloaderMainMenu, and Game objects which will be our game states, and we define them by using their prototypes. There are a few special function names inside those objects reserved for the framework itself (preload()create()update(), and render()), but we can also define our own (startGame()spawnCandy()managePause()). If you're not sure you understand all of this, then don't worry—I'll explain everything using the code examples later.

The Game

Let's forget about the BootPreloader and MainMenu states for now. They will be described in detail later; all you have to know at the moment is that the Boot state will take care of the basic config of the game, Preloader will load all the graphic assets, and MainMenu will show you the screen where you'll be able to start the game. 

Let's focus on the game itself and see how the code of the Gamestate looks. Before we go through the whole Game.js code, though, let's talk about the concept of the game itself and the most important parts of the logic from a developer's point of view.

Portrait mode

The game is played in portrait mode, meaning that the player holds their mobile vertically to play it.

Rotate device prompt

In this mode, the screen's height is greater than its width—as opposed to the landscape mode, where the screen's width is greater than its height. There are types of games that work better in portrait mode (like Monster Wants Candy), types that work better in landscape mode (including platformer games like Craigen), and even some types that work in both modes, although it's usually a lot harder to code such games.

Game.js

Before we go through the source code of the game.js file, let's talk about its structure. There is a world created for us, and there is a player character inside whose job it is to grab the candy.

Game world: The world behind the monster is static. There's an image of the Candyland in the background, we can see the monster in the foreground, and there's also a user interface.

Player character: This demo is intentionally very simple and basic, so the little monster is doing nothing besides waiting for the candy. The main task for the player is to collect the candy.

Candy: The core mechanic of the game is to catch as much candy as possible. The candies are spawned at the top edge of the screen, and the player must tap (or click) them as they're falling down. If any candy falls off the bottom of the screen, it's removed, and the player character receives damage. We don't have a lives system implemented, so after that the game instantly ends and the appropriate message is displayed.

Okay, let's look at the code structure of our Game.js file now:

Candy.Game = function(game) {
	// ...
};
Candy.Game.prototype = {
	create: function() {
		// ...
	},
	managePause: function() {
		// ...
	},
	update: function() {
		// ...
	}
};
Candy.item = {
	spawnCandy: function(game) {
		// ...
	},
	clickCandy: function(candy) {
		// ...
	},
	removeCandy: function(candy) {
		// ...
	}
};

There are three functions defined in the Candy.Game prototype: 

  • create() takes care of the initialization 
  • managePause() pauses and unpauses the game 
  • update() manages the main game loop with every tick 

We will create a handy object called item to represent a single candy. It will have some useful methods: 

  • spawnCandy() adds new candy to the game world 
  • clickCandy() is fired when a user clicks or taps on the candy
  • removeCandy() removes it

Let's go through them:

Candy.Game = function(game) {
	this._player = null;
	this._candyGroup = null;
	this._spawnCandyTimer = 0;
	this._fontStyle = null;
	Candy._scoreText = null;
	Candy._score = 0;
	Candy._health = 0;
};

Here, we're setting up all the variables that we will be using later in the code. 

By defining this._name, we're restricting the use of the variables to the Candy.Game scope, which means they can't be used in other states—we don't need them there, so why expose them? 

By defining Candy._name, we're allowing the use of those variables in other states and objects, so, for example, Candy._score can be increased from the Candy.item.clickCandy() function. 

The objects are initialized to null, and the variables we need for calculations are initialized with zeros. 

We can move on to the contents of Candy.Game.prototype:

create: function() {
	this.physics.startSystem(Phaser.Physics.ARCADE);
	this.physics.arcade.gravity.y = 200;

	this.add.sprite(0, 0, 'background');
	this.add.sprite(-30, Candy.GAME_HEIGHT-160, 'floor');
	this.add.sprite(10, 5, 'score-bg');
	this.add.button(Candy.GAME_WIDTH-96-10, 5, 'button-pause', this.managePause, this);

	this._player = this.add.sprite(5, 760, 'monster-idle');
	this._player.animations.add('idle', [0,1,2,3,4,5,6,7,8,9,10,11,12], 10, true);
	this._player.animations.play('idle');

	this._spawnCandyTimer = 0;
	Candy._health = 10;

	this._fontStyle = { font: "40px Arial", fill: "#FFCC00", stroke: "#333", strokeThickness: 5, align: "center" };
	Candy._scoreText = this.add.text(120, 20, "0", this._fontStyle);

	this._candyGroup = this.add.group();
	Candy.item.spawnCandy(this);
},

At the beginning of the create() function, we set up the ARCADE physics system—there are a few available in Phaser, but this is the simplest one. After that, we add vertical gravity to the game. Then we add three images: the background, the floor on which the monster is standing, and the score UI's background. The fourth item we add is the Pause button, Note that we're using the Candy.GAME_WIDTHand Candy.GAME_HEIGHT variables, which are defined in Candy.Preloader() but are available throughout the whole game code.

Then we create a monster, the player's avatar. It's a sprite with frames—a spritesheet. To have it look like he's standing and breathing calmly, we can animate him. 

The animations.add() function creates an animation from the available frames, and the function takes four parameters: 

  • the name of the animation (so we can reference it later) 
  • the table with all the frames we want to use (we can use only some of them if we want) 
  • a framerate
  • a flag to specify whether to loop the animation and play it indefinitely.

If we want to start our animation, we have to use the animations.play() function with the name specified.

We then set the spawnCandyTimer to 0 (getting ready to count up) and the health of the monster to 10.

Styling the Text

The next two lines let us show some text on the screen. The this.add.text() function takes four parameters: left and top absolute positions on the screen, the actual text string and the config object. We can format the text accordingly using the CSS-like syntax in that config object. 

The config for our font looks like this:

this._fontStyle = { 
    font: "40px Arial", 
    fill: "#FFCC00", 
    stroke: "#333", 
    strokeThickness: 5, 
    align: "center" 
};

In this case, the font is Arial, it's 40 pixels tall, the color is yellow, there's a stroke defined (with color and thickness), and the text is center-aligned.

After that, we define candyGroup and spawn the first candy.

Pausing the Game

The pause function looks like this:

managePause: function() {
	this.game.paused = true;
	var pausedText = this.add.text(100, 250, "Game paused.\nTap anywhere to continue.", this._fontStyle);
	this.input.onDown.add(function(){
		pausedText.destroy();
		this.game.paused = false;
	}, this);
},

We change the state of this.game.paused to true every time the pause button is clicked, show the appropriate prompt to the player, and set up an event listener for the player's click or tap on the screen. When that click or tap is detected, we remove the text and set this.game.paused to false

The paused variable in the game object is special in Phaser, because it stops any animations or calculations in the game, so everything is frozen until we unpause the game by setting it to false.

The Update Loop

The update() function name is one of the reserved words in Phaser. When you write a function with that name, it will be executed on every frame of the game. You can manage calculations inside it based on various conditions.

update: function() {
	this._spawnCandyTimer += this.time.elapsed;
	if(this._spawnCandyTimer > 1000) {
		this._spawnCandyTimer = 0;
		Candy.item.spawnCandy(this);
	}
	this._candyGroup.forEach(function(candy){
		candy.angle += candy.rotateMe;
	});
	if(!Candy._health) {
		this.add.sprite((Candy.GAME_WIDTH-594)/2, (Candy.GAME_HEIGHT-271)/2, 'game-over');
		this.game.paused = true;
	}
}

Every tick in the game world, we add the time elapsed since the previous tick to the spawnCandyTimer variable to keep track of it. The if statement checks whether or not it's time to reset the timer and spawn new candy onto the game world—we do this every second (that is, every time we notice that the spawnCandyTimer has passed 1000 milliseconds). Then, we iterate through the candy group with all the candy object inside (we could have more than one on screen) using a forEach, and add a fixed amount (stored in the candy object's rotateMe value) to the candy's angle variable, so that they each rotate at this fixed speed while falling. The last thing we do is check if the health has dropped to 0—if so, then we show the game over screen and pause the game.

Managing the Candy Events

To separate the candy logic from the main Game, we use an object called item that contains the functions we will use: spawnCandy()clickCandy() and removeCandy(). We keep some of the variables related to candy in the Game object for easier use, while others are defined only in the item functions for better maintainability.

spawnCandy: function() {
	var dropPos = Math.floor(Math.random()*Candy.GAME_WIDTH);
	var dropOffset = [-27,-36,-36,-38,-48];
	var candyType = Math.floor(Math.random()*5);
	var candy = game.add.sprite(dropPos, dropOffset[candyType], 'candy');
	candy.animations.add('anim', [candyType], 10, true);

	game.physics.enable(candy, Phaser.Physics.ARCADE);
	candy.inputEnabled = true;
	candy.events.onInputDown.add(this.clickCandy, this);

	candy.checkWorldBounds = true;
	candy.events.onOutOfBounds.add(this.removeCandy, this);
	candy.anchor.setTo(0.5, 0.5);
	candy.rotateMe = (Math.random()*4)-2;
	game._candyGroup.add(candy);
},

The function begins by defining three values: 

  • a randomized x-coordinate to drop the candy from (between zero and the width of the Canvas)
  • the y-coordinate to drop the candy from, based on its height (which we determine later on based on the type of candy) 
  • a randomized candy type (we have five different images to use)

We then add a single candy as a sprite, with its starting position and image as defined above. The last thing we do in this block is set a new animation frame that will be used when the candy spawns.

Next, we enable the body of the candy for the physics engine, so that it can fall naturally from the top of the screen when the gravity is set. Then, we enable the input on the candy to be clicked or tapped, and set the event listener for that action.

To be sure that the candy will fire an event when it leaves the screen boundaries we set checkWorldBounds to true. events.onOutOfBounds() is a function that will be called when our candy exits the screen; we make it call removeCandy() in turn. Setting the anchor to our candy in the exact middle lets us rotate it around its axis, so that it will spin naturally. We set the rotateMe variable here so we can use it in the update() loop to rotate the candy; we choose a value between -2 and +2. The last line adds our newly created candy to the candy group, so that we can loop through them all. 

Let's move on to the next function, clickCandy():

clickCandy: function(candy) {
	candy.kill();
	Candy._score += 1;
	Candy._scoreText.setText(Candy._score);
},

This one takes one candy as a parameter and uses the Phaser method kill() to remove it. We also increase the score by 1 and update the score text. 

Resetting the candy is also short and easy:

removeCandy: function(candy) {
	candy.kill();
	Candy._health -= 10;
},

The removeCandy() function is fired if the candy disappears below the screen without being clicked. The candy object is removed, and the player loses 10 points of health. (He had 10 at the beginning, so missing even one piece of falling candy ends the game.)

Prototypes and Game States

We've learned about the game mechanics, the core idea, and how the gameplay looks. Now it's time to see the other parts of the code: scaling the screen, loading the assets, managing button presses, and so on. 

We already know about the game states, so let's see exactly how they look, one after the other:

Boot.js

Boot.js is the JavaScript file where we will define our main game object—let's call it Candy (but you can name it whatever you want). Here's the source code of the Boot.js file:

var Candy = {};
Candy.Boot = function(game) {};
Candy.Boot.prototype = {
	preload: function() {
		this.load.image('preloaderBar', 'img/loading-bar.png');
	},
	create: function() {
		this.input.maxPointers = 1;
		this.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL;
		this.scale.pageAlignHorizontally = true;
		this.scale.pageAlignVertically = true;
		this.scale.setScreenSize(true);
		this.state.start('Preloader');
	}
};

As you can see, we're starting with var Candy = {} which creates a global object for our game. Everything will be stored inside, so we won't bloat the global namespace. 

The code Candy.Boot = function(game){} creates a new function called Boot() (used in index.html) which receives the game object as a parameter (also created by the framework in index.html). 

The code Candy.Boot.prototype = {} is a way to define the contents of Candy.Boot using prototypes:

Candy.Boot.prototype = {
	preload: function() {
		// code
	},
	create: function() {
		// code
	}
};

There are a few reserved names for functions in Phaser, as I mentioned before; preload() and create() are two of them. preload() is used to load any assets and create() is called exactly once (after preload()), so you can put the code that will be used as a setup for the object there, such as for defining variables or adding sprites.

Our Boot object contains these two functions, so they can be referenced by using Candy.Boot.preload() and Candy.Boot.create(), respectively. As you can see in the full source code of the Boot.js file, the preload() function loads a preloader image into the framework:

preload: function() {
	this.load.image('preloaderBar', 'img/loading-bar.png');
},

The first parameter in this.load.image() is the name we give to the loading bar image, and the second is the path to the image file in our project structure. 

But why are we loading an image in the Boot.js file, when Preload.js is supposed to do it for us anyway? Well, we need an image of a loading bar to show the status of all the other images being loaded in the Preload.js file, so it has to be loaded earlier, before everything else.

Scaling Options

The create() function contains a few Phaser-specific settings for input and scaling:

create: function() {
	this.input.maxPointers = 1;
	this.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL;
	this.scale.pageAlignHorizontally = true;
	this.scale.pageAlignVertically = true;
	this.scale.setScreenSize(true);
	this.state.start('Preloader');
}

The first line, which sets input.maxPointers to 1, defines that we won't use multi-touch, as we don't need it in our game. 

The scale.scaleMode setting controls the scaling of our game. The available options are: EXACT_FITNO_SCALE and SHOW_ALL; you can enumerate through them and use the values of 0, 1, or 2, respectively. The first option will scale the game to all the available space (100% width and height, no ratio preserved); the second will disable scaling completely; and the third will make sure that the game fits in the given dimensions, but everything will be shown on the screen without hiding any fragments (and the ratio will be preserved). 

Setting scale.pageAlignHorizontally and scale.pageAlignVertically to true will align our game both horizontally and vertically, so there will be the same amount of free space on the left and right side of the Canvas element; the same goes for top and bottom. 

Calling scale.setScreenSize(true) "activates" our scaling. 

The last line, state.start('Preloader'), executes the next state—in this case, the Preloader state.

Preloader.js

The Boot.js file we just went through has a simple, one-line preload() function and lots of code in the create() function, but Preloader.js looks totally different: we have lots of images to load, and the create() function will just be used to move to another state when all the assets are loaded. 

Here's the code of the Preloader.js file:

Candy.Preloader = function(game){
	Candy.GAME_WIDTH = 640;
	Candy.GAME_HEIGHT = 960;
};
Candy.Preloader.prototype = {
	preload: function() {
		this.stage.backgroundColor = '#B4D9E7';
		this.preloadBar = this.add.sprite((Candy.GAME_WIDTH-311)/2,
			(Candy.GAME_HEIGHT-27)/2, 'preloaderBar');
		this.load.setPreloadSprite(this.preloadBar);

		this.load.image('background', 'img/background.png');
		this.load.image('floor', 'img/floor.png');
		this.load.image('monster-cover', 'img/monster-cover.png');
		this.load.image('title', 'img/title.png');
		this.load.image('game-over', 'img/gameover.png');
		this.load.image('score-bg', 'img/score-bg.png');
		this.load.image('button-pause', 'img/button-pause.png');

		this.load.spritesheet('candy', 'img/candy.png', 82, 98);
		this.load.spritesheet('monster-idle',
			'img/monster-idle.png', 103, 131);
		this.load.spritesheet('button-start',
			'img/button-start.png', 401, 143);
	},
	create: function() {
		this.state.start('MainMenu');
	}
};

It starts similarly to the previous Boot.js file; we define the Preloader object and add definitions for two functions (preload() and create()) to its prototype. Inside the Prototype object we define two variables: Candy.GAME_WIDTH and Candy.GAME_HEIGHT; these set the default width and height of the game screen, which will be used elsewhere in the code.

The first three lines in the preload() function are responsible for setting the background color of the stage (to #B4D9E7, light blue), showing the sprite in the game, and defining it as a default one for the special function called setPreloadSprite() that will indicate the progress of the loading assets. 

Let's look at the add.sprite() function:

this.preloadBar = this.add.sprite((640-311)/2, (960-27)/2, 'preloaderBar');

As you can see, we pass three values: the absolute left position of the image (the center on screen is achieved by subtracting the width of the image from the width of the stage and halving the result), the absolute top position of the image (calculated similarly), and the name of the image (which we loaded in the Boot.js file already).

Loading spritesheets

The next few lines are all about using load.image() (which you've seen already) to load all of the graphic assets into the game. 

The last three are a little different:

this.load.spritesheet('candy', 'img/candy.png', 82, 98);

This function, load.spritesheet(), rather than loading a single image, takes care of a full collection of images inside one file—a spritesheet. Two extra parameters are needed for telling the function the size of a single image in the sprite.

Candy spritesheet

In this case, we have five different types of candy inside one candy.png file. The whole image is 410x98px, but the single item is set to 82x98px, which is entered in the load.spritesheet() function. The player spritesheet is loaded in a similar manner.

The second function, create(), starts the next state of our game, which is MainMenu. This means that the main menu of the game will be shown just after all the images from the preload() function have been loaded.

MainMenu.js

This file is where we will render some game-related images, and where the user will click on the Start button to launch the game loop and play the game.

Candy.MainMenu = function(game) {};
Candy.MainMenu.prototype = {
	create: function() {
		this.add.sprite(0, 0, 'background');
		this.add.sprite(-130, Candy.GAME_HEIGHT-514, 'monster-cover');
		this.add.sprite((Candy.GAME_WIDTH-395)/2, 60, 'title');
		this.add.button(Candy.GAME_WIDTH-401-10, Candy.GAME_HEIGHT-143-10,
			'button-start', this.startGame, this, 1, 0, 2);
	},
	startGame: function() {
		this.state.start('Game');
	}
};

The structure looks similar to the previous JavaScript files. The prototype of the MainMenu object doesn't have a preload() function, because we don't need it—all the images have been loaded in the Preload.js file already. 

There are two functions defined in the prototype: create() (again) and startGame(). As I mentioned before, the name of the first one is specific to Phaser, while the second one is our own. 

Let's look at startGame() first:

startGame: function() {
	this.state.start('Game');
}

This function takes care of one thing only—launching the game loop—but it's not launched automatically or after the assets are loaded. We will assign it to a button and wait for a user input.

create: function() {
	this.add.sprite(0, 0, 'background');
	this.add.sprite(-130, Candy.GAME_HEIGHT-514, 'monster-cover');
	this.add.sprite((Candy.GAME_WIDTH-395)/2, 60, 'title');
	this.add.button(Candy.GAME_WIDTH-401-10, Candy.GAME_HEIGHT-143-10,
		'button-start', this.startGame, this, 1, 0, 2);
},

The create() method has three add.sprite() Phaser functions that we are familiar with already: they add images to the visible stage by positioning them absolutely. Our main menu will contain the background, the little monster in the corner, and the title of the game.

Buttons

There's also an object we've already used in Game state, a button:

this.startButton = this.add.button(Candy.GAME_WIDTH-401-10, Candy.GAME_HEIGHT-143-10,
	'button-start', this.startGame, this, 1, 0, 2);

This button looks like it is more complicated than over methods we've seen so far. We pass eight different arguments to create it: left position, top position, name of the image (or sprite), the function to execute after the button is clicked, the context in which this function is executed, and indices of the images in the button's spritesheet. 

This is how the button spritesheet looks, with the states labelled:

Start button spritesheet

It's very similar to the candy.png spritesheet we used before, except arranged vertically. 

It's important to remember that the last three digits passed to the function—1, 0, 2—are the different states of the button: over (hover), out (normal), and down (touch/click) respectively. We have normal, hover and click states in the button.png spritesheet, respectively, so we change the order in the add.button() function from 0, 1, 2 to 1, 0, 2 to reflect that.

That's it! You now know the basics of Phaser game framework; congratulations!

The Finished Game

The demo game used in the article has evolved into a full, finished game that you can play here. As you can see, there are lives, achievements, high scores, and other interesting features implemented, but most of them are based on the knowledge you've already learned by following this tutorial.

Monster Wants Candy full game

You can also read the short "making of" blog post to learn about the origins of the game itself, the story behind it, and some fun facts.

Resources

Building HTML5 games for mobile devices has exploded in the last few months. The technology is getting better and better, and there are tools and services popping out almost every single day—it's the best time to dive in to the market. 

Frameworks like Phaser gives you the ability to create games that run flawlessly on a variety of different devices. Thanks to HTML5, you can target not just mobile and desktop browsers, but also different operating systems and native platforms.

There are lots of resources right now that could help you get into HTML5 game development, for example this HTML5 Gamedev Starter list or this Getting Started With HTML5 Game Development article. If you need any help you can find fellow developers on the HTML5GameDevs forums or directly at #BBG channel on Freenode IRC. You can also check the status of the upcoming book about Firefox OS and HTML5 games, but it's still in the early stages of writing. There's even a Gamedev.js Weekly newsletter that you can subscribe to, to keep up to date with the latest news.

Summary

This was a long journey through every line of code of the Monster Wants Candy demo, but I hope it will help you learn Phaser, and that in the near future you will create awesome games using the framework. 

The source code used in the article is also freely available on GitHub, so you can fork it or just download it and do whatever you want. Feel free to modify it and create your own games on top of it, and be sure to visit the Phaser forums if you need anything during the development.


Viewing all articles
Browse latest Browse all 728

Trending Articles