Headstart 2018 - Project

You might also like

Download as pdf or txt
Download as pdf or txt
You are on page 1of 34

Head Start 2018

Building Platform Games with Phaser


[Type the abstract of the document here. The abstract is typically a short summary of the
contents of the document. Type the abstract of the document here. The abstract is typically a
short summary of the contents of the document.]
Head Start 2018
Building Platform Games with Phaser

Contents
Project introduction .......................................................................... 2
Build a Basic Platform Game ............................................................. 3
Setting up our desktop .................................................................. 3
Files Structure ........................................................................... 4
HTML web page ........................................................................ 4
Aim of the
JavaScript file ............................................................................ 5
booklet
Run the game ............................................................................ 5
In this booklet, you
Development mode (Chrome Dev Tools) ................................. 6
will find a tutorial on

Head Start 2018


Build the world (stage 2) ............................................................... 6
how to build a simple
What is a State in Phaser? (source www.joshmorony.com) .... 6
platformer game. You
Create the main state ............................................................... 8
will find the basic
Setting the décor ...................................................................... 9
Adding a player (stage 3)............................................................. 10 blocks of a game,

Adding a goal: (stage 4) ............................................................... 15 decors, player,

Create goal .............................................................................. 15 enemies, target to

Have we reach the level’s goal? ............................................. 16 reach, and screens.


Add a new Winning State ....................................................... 17 From these blocks you
Add Enemies................................................................................ 19 will be able to create
Create an Enemy ..................................................................... 19
your own, more
Animate an Enemy .................................................................. 20
challenging game.
Final Touches: (stage 6)............................................................... 21
GRAVITY! ................................................................................. 21
Killing Enemies ........................................................................ 22
1
Project introduction
The aim of this week project is to build a Web based platform game using the Phaser
framework. The Phaser framework has several languages and the one we will be using
during the week is JavaScript.

You will be working in a group of three or more, and your job is to develop your own
platform game. You will have to define the look and feel of the game, what are the
Head Start 2018

objectives, and how the user interacts with it.

A lot of research on how things can be done will be done by you, with myself and the
ambassadors helping you when you are stuck. This first booklet is to get you started.

2
Build a Basic Platform Game

Setting up our desktop (stage 1)


1. We need a text editor, any would do and it depends on your preferences. For
our project, we will be using Notepad++ ()
2. We need a Webserver in order to run our web base game. For testing purpose,
we can create a local one, which is only
available on our machine, not through the
Internet. To do so we will be running a Python
command on the Command Prompt.
a. First open a Command shell via the start
button and typing cmd and select the
icon “Command Prompt”.

Head Start 2018


b. on the prompt, go to the folder containing the files for our website
using the cd command. It should be in folder Documents\headstart.
Then type the following command:
python -m http.server
A local Webserver is now up and running. Note below the message:
Serving HTTP on 0.0.0.0 port 8000. The value 0.0.0.0
means localhost, e.g. works only on your computer, not Internet. Port
8000 means the port used to access the file from your browser. We will
come back to it later on to see how we can access our webpage

3
3. We need a web browser to run the game on a web page. We will be using
Google Chrome and use the Dev Tool provided to check for errors in our code.
Now we are ready to start.
Files Structure
For a game, we need to organize our files into a directory tree. Below is a template
for such a directory tree. The folder assets contains all the element needed for the
game, such as images for the background, sound effects and music. These elements
are themselves sorted into sub-folders. The JavaScript files are stored in the js folder.

game
|---assets
| |---audio
| |---data
| |---image
| |---sprites
|
|---js

HTML web page


Head Start 2018

To launch our game we need to embed our JavaScript into an HTML file. At template
is shown below.

<!DOCTYPE html>
<html lang="en-UK">

<head>
<meta charset="utf-8">
<script src="../node_modules/phaser-ce/build/phaser.min.js"></script>
<title> Game </title>
</head>

<body>
<!-- include the main game file -->
<script src="js/index.js"></script>
</body>

</html>
The line <script src="../node_modules/phaser-ce/build/phaser.min.js"></script>
indicates where the Phaser code can be found (note it is also in JavaScript). The path
may vary depending on your setup.

4 Your game will be written in the JavaScript file index.js and it should be in the sub-
folder js. The line <script src="js/index.js"></script> indicates where the file is and
that is should be executed.
JavaScript file
Open a new file in the editor, copy the code below and save it as index.js into the
sub-folder js. Write the following code and save the file.

var game = new Phaser.Game(500,500);


The line of code creates a Phaser game and assigns it to the variable game. The canvas
of the game is 500x500 pixels.

Run the game


Open the Google Chrome browser. In the URL field type localhost:8000. This is
the address of the local webserver we created earlier using Python 3.
You should have something like the image below. It’s not much but it’s a start.

Head Start 2018

5
Development mode (Chrome Dev Tools)
Use Ctrl + Shift + I to open the DevTools. The browser should look like the image below.
We have access to the source code, a debugger and a console. This will help us to
debug our code when we encounter an issue.
Head Start 2018

Build the world (stage 2)


One of the core concepts in Phaser is States. States split your game code up into logical
chunks, and you progress through these states one at a time. Typically, you would
have individual states for a level in your game, a title screen, a game over screen, a
bonus round game, and so on. You can switch between states and go back to states
that have already been used, but there is only ever one active state at a time.
What is a State in Phaser? (source www.joshmorony.com)
When building HTML5 games with Phaser, we use the concept of States. A state is
implemented using a JavaScript object, and allows us to modularise the different
concerns of our game into separate chunks of code.
6
You might have the following states in a Phaser game, for example:

• Boot

• Preload

• GameTitle

• Main

• GameOver
The Boot state handles any set up that is
required (like setting the dimensions for the
game) and calls the next state. Preload
handles loading in any assets that are required
and then launches the GameTitle state.
GameTitle displays the title screen for the
application, where a player can typically start
the game, see instructions, or access the

Head Start 2018


settings. Main is the main state for the
application which will handle the logic for the
game itself. GameOver displays the game over
screen and will handle displaying the player’s
score, allow them to reset the game, and so
on. Four states are shown on the right.

Using states has two main benefits:

• It allows us to organise our code, making it easier to build and maintain

• It allows us to manage each state individually, meaning we don’t have to keep


everything for the entire game in memory all at once.

7
Create the main state
To start with, we will be focusing on the main state. The first line of code declare a
variable mainState that will contain all the properties of a state. Finally, the last
line of code adds the state to the game.

var mainState = {

preload: function(){
},

create: function(){
},

update: function(){
}
};

game.state.add('main', mainState);

You may have noticed that three functions have been declared:
Head Start 2018

• The preload method is triggered first and is typically used to load any assets
that are required (like sprites and sound effects).

• The create method is called after preload has finished, it runs once and is
used to do the initial set up for the game.

• The update method starts after create has finished, and then it is
continuously called as part of the “game loop”. This method is where most of
the fun happens, as it can be used to make decisions based on the current
state of the game – is the player touching an enemy, is there less than 10
seconds remaining, are there multiple enemies on screen?

For the time being we will skip the preload function.

8
Setting the décor
We need to write a function to be able to draw rectangle that will represents
platforms and walls. Later, we will be using images to create the décor. Copy the code
below at the top of the file. Don’t worry if you don’t understand the code it is not
important for the rest of the session.

var box = function(options){


var bmd = game.add.bitmapData(options.width, options.height);
bmd.ctx.beginPath();
bmd.ctx.rect(0,0,options.width, options.height);
bmd.ctx.fillStyle = options.color;
bmd.ctx.fill();
return bmd;
};

To build the décor, we need to modify the create function. Walls and platforms are
built using a rectangle (box). We will be using six walls, four for the border of the world
and two for the platforms in the middle. For convenience, we are adding all the walls
into a group called walls.

Head Start 2018


create: function(){

this.walls = this.game.add.group();

var topWall = this.walls.create(0, 0,


box({width: this.game.world.width, height: 16, color: '#374A69'}));

var bottomWall = this.walls.create(0, this.game.world.height-16,


box({width: this.game.world.width, height: 16, color: '#374A69'}));

var rightWall = this.walls.create(this.game.world.width-16, 16,


box({width: 16, height: this.game.world.height-32, color: '#374A69'}));

var leftWall = this.walls.create(0, 16,


box({width: 16, height: this.game.world.height-32, color: '#374A69'}));

var midWallTop = this.walls.create(16, 175,


box({width: this.game.world.width - 150, height: 16, color: '#374A69'}));

var midWallBottom = this.walls.create(150, 325 ,


box({width: this.game.world.width - 150-16, height: 16, color: '#374A69'}));
},

9
We could also change the colour background by adding the following statement at the
top of the create function.

this.game.stage.backgroundColor = '#C2C2C2';

The game should look like this now:

Adding a player (stage 3)

create: function(){
this.game.stage.backgroundColor = '#C2C2C2';
//…
Head Start 2018

this.player = this.game.add.sprite(32, 32, box({width: 32, height: 32, color: '#4F616E'}));


},

Reload the web page and you should see the game
world on the right. Now that we have a player, we want
to be able to interact with it using the keyboard. Add
the following statement at the end of the create
function.

this.cursor = this.game.input.keyboard.createCursorKeys();

this.cursor will collect the keyboard input from the user, however if we reload
the page, nothing happens when we press a key. This is because we haven’t told the
program what it should do when a key is pressed. We want the player to go right when
we press the right arrow key, and left when we press the left arrow key.

10
We need to use a new JavaScript construct: if-else statement.

If(condition){
doTheseCommands;
} else {
doTheOtherCommands;
}

What does the new construct do? condition is a Boolean (i.e. can have only the
values true or false).

• If condition is true the construct executes doTheseCommands,

• if condition is false, the construct executes doTheOtherCommands.


What about the following construct?

If(conditionA){
doCommandsA;
} else If(conditionB){
doCommandsB;

Head Start 2018


} else {
doTheOtherCommands;
}

• If conditionA is true the construct executes doCommandsA,

• If conditionA is false and conditionB is true the construct executes


doCommandsB,

• Otherwise (i.e. conditionA and conditionB are false), the construct


executes doTheOtherCommands.
Now we are ready to handle the user input from the keyboard. As the keyboard input
happens while we are playing, dealing with this input must be done in the function
update. Modify the function update so it looks like:

update: function(){
if(this.cursor.left.isDown){
console.log("go left");
} else if (this.cursor.right.isDown){
console.log("go right");
}
}
11
Reload the game and press the left and/or right arrows. The player does not move,
however when we look at the console, we can see the following:
You can see that the messages “go right” and “go left” have been printed on the
console multiple times. So, what happened exactly?
this.cursor.left.isDown is true when the left arrow key is down, therefore
the code executes the code console.log("go left"). When the left arrow key
is not down, this.cursor.left.isDown is false, therefore the code go to the
next else if statement and tests if this.cursor.right.isDown is true. If
the right arrow is pressed down, the code executes console.log("go right").
If none of the two arrow keys are pressed down, nothing happens.
We are now able to read the input from the user and do something, but so far our
Head Start 2018

player does not move. The player sprite needs a way to tell it that it must move one
way or the other, for that we need to give it a velocity. The velocity is described as a
2D vector, with a x and y value. A positive x value means moving to the right, whereas
a negative value means going left. Similarly, a positive y value means going up, a
negative value means going down. So how can we attach a velocity to a sprite? We
can do that by changing the value of a property of its body, in the case of our player
by doing:

this.player.body.velocity.x = 250;
Let’s modify the update function as follow and reload the game.

update: function(){
var speed = 250;
this.player.body.velocity.y = 0;
this.player.body.velocity.x = 0;
if(this.cursor.left.isDown){
this.player.body.velocity.x -= speed;
} else if (this.cursor.right.isDown){
this.player.body.velocity.x += speed;
}
}
12

Reloading the game results in the following page:


Unfortunately, we have our first error, the game is black and if we look at the console
we can see an error message.
index.js:36 Uncaught TypeError: Cannot read property 'velocity' of null

The error comes from line 36 in the file index.js (note it may be a different line number
for your program). From this it seems that he cannot find the property velocity,

Head Start 2018


something to do with null. In fact, it seems that the sprite does not have a body, and
therefore does not have a velocity. This is because we forgot to tell the game that
objects in this world should have a body. We can resolve this issue by adding the
following statement at the top of the create function:

create: function(){
this.game.stage.backgroundColor = '#C2C2C2';
this.game.world.enableBody = true;

}

Let’s reload the game and try to move the player. As you can see we can move the
player from left to right. Could you add more code so the player can also go up and
down?

13
Did you notice something?

Our player moves out of this world! You see that testing thoroughly your code is
essential for a successful project. So what is the issue? We forgot to tell the player
sprite it should interact with the world boundary. We can do so by adding some code
to the create function.

create: function(){
this.game.stage.backgroundColor = '#C2C2C2';
//…
this.player = this.game.add.sprite(32, 32, box({width: 32, height: 32, color: '#4F616E'}));
this.player.body.collideWorldBounds = true;

this.cursor = this.game.input.keyboard.createCursorKeys();
},

We are almost there, we can move the player, it does not go out of bounds but
Head Start 2018

annoyingly it goes through the walls. We need to add physical behaviors, such as
dealing with collision between objects. Let’s tell our world it should have physics
constraints and that walls have a body, so we can interact with them. Add to the
create function the statement below:

create: function(){
this.game.stage.backgroundColor = '#C2C2C2';
this.game.physics.startSystem(Phaser.Physics.ARCADE);
this.game.world.enableBody = true;
this.walls = this.game.add.group();
this.walls.enableBody = true;

}

We must also tell the program that it should check collision between the player and
the walls by adding a statement to the update function.

update: function(){
this.game.physics.arcade.collide(this.player, this.walls);
var speed = 250;

}
14
Interestingly, the behavior of our game is not exactly the one we expected. Our player
does not go through wall (that’s good) but the walls are moving when our player hits
them. Is this normal? Yes, it is from the point of view of the physical world. The walls
behave like a ball on the floor when you bump into it, the ball moves. You must state
explicitly that the walls cannot move (at least for the game we are creating today). For
every single wall declared in the create function add the following statement:

nameOfTheWall.body.immovable = true;
For example:

topWall.body.immovable = true;

Congratulation, we now have a player that can interact with its environment.

Adding a goal: (stage 4)


We need to define a way of winning the game, or at least a level.

Head Start 2018


Create goal
For our project, we will add a spot on the map that is the goal to reach. We will define
the spot as a sprite like a player. At the end of the create function add the
statement:

this.goal = this.game.add.sprite( this.game.world.width - 80,


this.game.world.height - 50,
box({width: 32, height: 32, color: '#50FF50'}));

Our world now looks like this, with the player at the top left
corner and the goal to reach at the bottom right in green.

15
Have we reach the level’s goal?
Now we need to check constantly if the player overlaps with the goal, so we need to
add a statement in the update function. At the beginning of the function update add
the statement:

this.game.physics.arcade.overlap(this.player, this.goal, this.handlePlayerWin, null, this);

What is this.handlePlayerWin? It is a callback function which is called when


the player overlaps the goal. Where can I find this callback function? Nowhere
because we did not write it yet. Let’s add a new function to our state mainState
after the update function (we could add it before if we wanted to, the order does
not matter as long as it is defined in the mainState object).

var mainState = {
create: function(){

},
update: function(){

Head Start 2018

},
handlePlayerWin: function(sprite1, sprite2){
sprite1.kill();
this.game.state.start('main');
}
};

Let reload the game and play. As you can see, as soon as our player passes over the
goal, a new game restart.
So, what does the code says? Because handlePlayerWin is the callback function
from overlap, sprite1 will received the object that is the first parameter in the
overlap function (in our case player), and sprite2 receives the second
parameter (in our case goal).

• sprite1.kill(); says to delete the object passed via the parameter


sprite1 (in our case player).

• this.game.state.start('main'); says to restart the game using


the ‘main’ state, i.e. restart the game from the beginning. This is not ideal,
16 it would probably be better to have a screen saying congratulation, and
offering the player to restart a new game if he/she wishes.
A note on coding style, rather than using sprite1 and sprite2 as names for the parameters, it is better to
use names that help understanding what the parameter represents. I would rewrite the function like this:
handlePlayerWin: function(player, goal){
player.kill();
this.game.state.start('main');
}

Add a new Winning State


So far, we have created only one state object for our game, the playing state called
mainState. Now we need to create a new state showing that a player has won and
add it to the game. Let’s call it winningState. Add the following code just after the
end of the mainState declaration.

var mainState = {

};
var winningState = {
create: function(data){
},
update: function(){

Head Start 2018


}
};
game.state.add('main', mainState);
game.state.add('winning', winnerState);
game.state.start('main');

Now let’s modify the handlePlayerWin function to call the new state when the
player overlaps the goal.

handlePlayerWin: function(player, goal){


player.kill();
this.game.state.start('winning');
}
Reload the game and move the player over the goal. You notice that the game become
a grey screen. This should be expected, as our new state does not do anything. What
are the next steps?
1. Show a message to congratulate the winner and ask if he/she want to restart
the game by pressing the space bar.
2. Listen to the input from the keyboard to check if the space bar is pressed
17
down.
Step 1
To show a message we need to add some text via the function
game.add.text(x, y, message, propertiesOfText)
x and y represent the position on the screen of the reference point of the text,
message is a string containing the message to be displayed (note \n means new line,
i.e the characters following the \n are displayed on the line below the characters
preceding \n). propertiesOfText is an list of pairs name/value representing font
type, font colour, alignment of the text and so on (search the documentation to find
more about it).

create: function(data){
label = this.game.add.text(this.game.world.width/2, this.game.world.height/2,
'Congratulation \n YOU HAVE WON! \n Press SPACE to restart.',
{
font: '22px Arial',
fill: '#b00',
align: 'center'
});
}
Head Start 2018

When we execute the code, and the player win the game we get to the new screen.

It’s better but not what we expected. It would be better if


the text was centred. The problem here is not the
coordinates, it is the reference point of the text object. By
default, the reference point is the top left corner. To make
the reference point the centre of the object, add the
statement at the end of the create function.

label.anchor.setTo(0.5, 0.5);
Next, we need to say that we want to listen to the keyboard input, especially the space
bar. At the end of the create function add the statement:

this.spacebar = this.game.input.keyboard.addKey(Phaser.Keyboard.SPACEBAR);

18
Step 2
Finally, we want to restart the game if and when the space bar is pressed. Like for the
arrow keys, we need to deal with the key pressed in the update function of the state
winningState.

update: function(){
if(this.spacebar.isDown){
this.game.state.start('main');
}
}

Congratulation, we have a completed game. It will not win any award, but that is a
start.

Add Enemies
The least we can say is that our game is far from being interesting or captivating. One
thing that is missing are the villains of the game, i.e. something that could kill us.

Head Start 2018


Create an Enemy
An enemy will be represented by another sprite, so we can create it the same way as
a goal. However, its behavior will be different. Modify your code in the mainState
to add an enemy.

var mainState = {
create: function(){
this.game.stage.backgroundColor = '#C2C2C2';

this.goal = this.game.add.sprite(this.game.world.width - 80, this.game.world.height - 50,
box({width: 32, height: 32, color: '#50FF50'}));
this.enemy = this.game.add.sprite(this.game.world.width - 200, 130,
box({width: 32, height: 32, color: '#FF5050'}));
},
update: function(){
this.game.physics.arcade.collide(this.player, this.walls);
this.game.physics.arcade.overlap(this.player, this.enemy, this.handlePlayerDeath, null, this);
this.game.physics.arcade.overlap(this.player, this.goal, this.handlePlayerWin, null, this);

},
handlePlayerDeath: function(player, enemy){
player.kill();
this.game.state.start('main');
}, 19

Rather than restarting the game by calling this.game.state.start('main'),
create a new game state to show a game over screen (in the same way as the
winningState).
Animate an Enemy
A sprite has predefine properties such as its x and y coordinates. They can be accessed
via the name of the sprite object and the name of the property, e.g.
spriteName.propertyName. For example, to get the x coordinate of our enemy we can
use this.enemy.x. In addition to existing properties, we can add our own. For
example we want our enemy to patrol a certain area of the game, e.g the enemy walks
some distance in one direction and then turn around and walks the same distance,
and then turn around and so on. We define a property maxDistance representing
the length of the patrolled area. We must also define a property storing the x position
where the enemy turned around, let’s call it previousX.

create: function(){

this.enemy = this.game.add.sprite( this.game.world.width - 200, 130,
box({width: 32, height: 32, color: '#FF5050'}));
Head Start 2018

this.enemy.previousX = this.enemy.x;
this.enemy.maxDistance = 250;
this.enemy.body.velocity.x = -100;
}
The next step is to check the current position of the enemy to see if it should turn
around or not. This is done in the update function and his shown below.

update: function(){
this.game.physics.arcade.overlap(this.player, this.enemy, this.handlePlayerDeath, null, this);

// change the direction if walked the maximum distance


if (Math.abs(this.enemy.x - this.enemy.previousX) > this.enemy.maxDistance) {
this.enemy.body.velocity.x *= -1;
this.enemy.previousX = this.enemy.x;
}

},

Reload the game and see what happens. Add more enemies to the game, for example
one on each level to have a total of three enemies. Use different values for each
enemy.
20
Final Touches: (stage 6)
GRAVITY!
The idea of a platform game is to have a player being have to jump to and fall from
walls and platform. At this stage our game is not able to do that, we are lacking an
important physics property: GRAVITY!
Gravity is simply implemented by the physics engine using the statement:

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

}
Reload the game.

OH DEAR! Everything fall apart!

The problem here is that every object with a body falls due to gravity (which would be

Head Start 2018


the case in a real word anyway). We must explicitly indicate which elements of our
game should not be affected by gravity. In our case, every single wall and the goal
should not be affected by gravity. Regarding enemies, it depends on what you want
them to do. If they are flying enemies, they should not be affected by the gravity. If
they are simple crawlers, they should fall down if going over a platform. For the time
being we are only considering crawlers.
To ensure our top wall does not fall we add to the create function the statement:
topWall.body.allowGravity = false;
Do the same for all walls and goal.

21
Next step we want the player to jump, we need to modify the code we wrote for the
user input in the update function.

update: function(){
this.game.physics.arcade.collide(this.player, this.walls);
this.game.physics.arcade.collide(this.enemies, this.walls);
this.game.physics.arcade.overlap(this.player, this.enemies, this.handlePlayerDeath, null, this);
this.game.physics.arcade.overlap(this.player, this.goal, this.handlePlayerWin, null, this);

var speed = 200;


if(this.cursor.up.isDown){
if(this.player.body.touching.down){
this.player.body.velocity.y = -600;
}
}else if(this.cursor.left.isDown){
this.player.body.velocity.x =- speed;
} else if (this.cursor.right.isDown){
this.player.body.velocity.x = speed;
} else {
this.player.body.velocity.x = 0;
}
// change the direction if walked the maximum distance

Head Start 2018

},

Killing Enemies
The final touch to our game is to be able to kill enemies. We have decided that the
player must jump on the enemy to kill it, in other word the player y velocity should
be greater than 0 when it touches the enemy, therefore falling onto them. To do that
we need to modify the callback function handlePlayerDeath.

handlePlayerDeath: function(player, enemy){


if(player.body.velocity.y > 0){
enemy.kill()
} else {
player.kill();
this.game.state.start('gameOver');
}
},

One important thing when writing code is to make it easier for someone else to read
and understand your code. For a code readability purpose, we may want to rename
22 the function handlePlayerDeath to something like:
handlePlayerEnemiesCollision
A more advanced Platform Game

Setup
For this session, you will need the following files which can be found in the
assets/images and assets/audio folders.

• background.png,

• grass_8x1.png,

• grass_6x1.png,

• grass_4x1.png,

• grass_2x1.png,

• grass_1x1.png,

• hero_stopped.png,

Head Start 2018


• hero.png,

• jump.wav.
In headstart, create a new folder (for example GameSession2) to contain the code for
this session. In the same way as yesterday, create the needed folders, a new
index.html file and a new index.js.
Let start to write our basic template for the JavaScript file:

var game = new Phaser.Game(960, 600);

var mainState = {

preload : function(){

},

create: function(){

},

update: function(){

} 23
};
game.state.add('main', mainState);
game.state.start('main');
In order to make it easier to modify the parameter of our game, let’s create an new
variable containing all the option such as the value for gravity, player velocity, and so
on.

var gameOptions = {
gameWidth: 960,
gameHeight: 600,
bgColor: 0x444444,
playerGravity: 900,
playerSpeed: 200,
playerJump: 600,
}
var game = new Phaser.Game(gameOptions.gameWidth, gameOptions.gameHeight);
var mainState = {

};
game.state.add('main', mainState);
game.state.start('main');

It will be much easier to modify the gravity or speed in our game as it will be the place
to change it. When you want to use a value from these option, use
Head Start 2018

gameOptions.valueName, like I have done to create the size of the game.

Add Graphics
First things we need to do is load the graphics file into memory. This is done at the
preload stage of the game. Change the preload function so it looks like:

preload : function(){
this.game.load.image('background', 'assets/images/background.png');
this.game.load.image('grass:8x1', 'assets/images/grass_8x1.png');
this.game.load.image('grass:6x1', 'assets/images/grass_6x1.png');
this.game.load.image('grass:4x1', 'assets/images/grass_4x1.png');
this.game.load.image('grass:2x1', 'assets/images/grass_2x1.png');
this.game.load.image('grass:1x1', 'assets/images/grass_1x1.png');
this.game.load.image('player', 'assets/images/hero_stopped.png');
},
By using this.game.load.image(valueName, sourcePath), we tell the
program where the file can be found, and when we need to use the image, what name
we should use (valueName)

24 At this stage the image are in memory but not on the game canvas, so we cannot see
them if we run the game.
Now let’s create some platforms and a background. We need to modify the create
function.

create: function(){
this.game.physics.startSystem(Phaser.Physics.ARCADE);
this.cursor = this.game.input.keyboard.createCursorKeys();
this.game.physics.arcade.gravity.y = 900;
this.game.world.enableBody = true;

this.platforms = this.game.add.group();
let sprite = this.platforms.create(50, 200, 'grass:6x1');
game.physics.enable(sprite);
sprite.body.allowGravity = false;
sprite.body.immovable = true;

this.game.add.image(0,0,'background');
}
The statement this.platforms.create(50, 200, 'grass:6x1') says to build a sprite using
an image stored in memory with the valueName ‘grass:6x1’ that we have loaded in
the preload function.
The result should give us a platform with an image in the background. Unfortunately,

Head Start 2018


we cannot see the platform we implemented, why?
The order you draw object matters. We create the platform first, and then we add the
background image. So the platform is drawn first, and on top of it we draw the
background, so it is not visible, it is hidden behind the background.
Change the order of the statements.

create: function(){
this.game.physics.startSystem(Phaser.Physics.ARCADE);
this.cursor = this.game.input.keyboard.createCursorKeys();
this.game.physics.arcade.gravity.y = 900;
this.game.world.enableBody = true;

this.game.add.image(0,0,'background');

this.platforms = this.game.add.group();
let sprite = this.platforms.create(50, 200, 'grass:6x1');
game.physics.enable(sprite);
sprite.body.allowGravity = false;
sprite.body.immovable = true;
}

25
You should now have a game looking like that.

Let’s make something a little bit more fancy, let’s create a lift, i.e. a platform that goes
up and down. It is the same principle as our enemies from yesterday session.
For a lift, we may want to set three properties of our own. The maximum height it can
go to (minY) and the minimum height it should go down to. In addition, we may want
to setup the speed of the lift. It is important to emphasize that these three properties
do not exist in Phaser for a sprite, but we have the ability to add additional properties
ourselves.
Head Start 2018

At the bottom of the create function add the following:

create: function(){

this.lifts = this.game.add.group();
sprite = this. lifts.create(350, 350, 'grass:2x1');
game.physics.enable(sprite);
sprite.body.allowGravity = false;
sprite.body.immovable = true;
sprite.minY = 100;
sprite.maxY = 400;
sprite.speed = 100;
sprite.body.velocity.y = sprite.speed;
},

26
We have created the platform, but if we run the game, the platform moves out of the
frame. This is because we have not told the platform to go the other way when it
reaches minY or maxY. We need to do that in the update function.

update: function(){
this.lifts.forEach(function(platform){
if(platform.y > platform.maxY){
platform.body.velocity.y = -platform.speed;
} else if(platform.y < platform.minY){
platform.body.velocity.y = platform.speed;
}
});
}

What does the forEach means? Ask one of us to explain it to you.


Now we have a lift!
We have defined platforms using graphics rather than using the box function. You
should be able to add a player, enemies, and a goal using graphics too. Complete the

Head Start 2018


implementation to have a working game with graphics, including two enemies, one
goal, one player, two lifts and two static platforms.

Add Sound FX
First step is to load the audio file. We do that in the preload function by using the
statement:

this.game.load.audio('sfx:jump', 'assets/audio/jump.wav');
Next we must add the sound to the game. This is done in the create function. Add the
statement:

this.sfx = {
jump: game.add.audio('sfx:jump')
};
Note that we add the sound to a set of pairs name:values. This will be useful to add
all our sound effects to this set called this.sfx. Every time we want to use the sound
jump we can do it by referencing this.sfx.jump.
For example to play the sound jump, use the command this.sfx.jump.play(). Try to find
where you can add that line of code in order to have this sound played when the 27
player jump.
Using a Variable to setup the platforms
At the top of index.js file, add the variable setupData:

var setupData = {
"platforms": [
{"image": "ground", "x": 0, "y": 546},
{"image": "grass:8x1", "x": 0, "y": 420},
{"image": "grass:2x1", "x": 420, "y": 336},
{"image": "grass:1x1", "x": 588, "y": 504},
{"image": "grass:8x1", "x": 672, "y": 378},
{"image": "grass:4x1", "x": 126, "y": 252},
{"image": "grass:6x1", "x": 462, "y": 168},
],
"lifts" : [
{"image": "grass:2x1", "x": 798, "y": 84, "min:y":50, "max:y":450, "speed":50}
]
}

As you can see, setup data is a set of pairs name:value where the values are list of set
of pairs name:value. This is a nested data structure.
Head Start 2018

• Platforms is a list containing the platforms, and the platforms are described
using the name “image” with the image label to use, “x” with the x coordinate
of the platform in the game, and “y” with its y coordinate.

• Lift is a list (in the example above containing only one element) of lifts, and the
lifts have the same properties “image”, “x”, “y” as platforms, but in addition
have “min:y”, “max:y”, and “speed”.
The next step is to create the platforms based on the information stored in setupData.
First remove all our code regarding platforms that we have done previously. At the
bottom of the function create, add the following lines of code.

this.platforms = this.game.add.group();
this.lifts = this.game.add.group();

// spawn all platforms


setupData.platforms.forEach(this._spawnPlatform, this);
setupData['lifts'].forEach(this._spawnLift, this);

28
Now we need to write the two functions _ spawnPlatform and _ spawnLift. This could
be done between the create and update functions.

_spawnPlatform : function (platform) {


let sprite = this.platforms.create(platform.x, platform.y, platform.image);
game.physics.enable(sprite);
sprite.body.allowGravity = false;
sprite.body.immovable = true;
},

_spawnLift : function (platform) {


let sprite = this.lifts.create(platform.x, platform.y, platform.image);
sprite.minY = platform["min:y"];
sprite.maxY = platform["max:y"];
sprite.speed = platform["speed"];
game.physics.enable(sprite);
sprite.body.velocity.y = sprite.speed;
sprite.body.allowGravity = false;
sprite.body.immovable = true;
},
As you can see, it is a much cleaner way to create many platforms than the way we
tried previously. This works for platforms, but you could add more to the variable

Head Start 2018


setupData to include the player (position and graphics), the enemies, the goal and
more.

Using JSON File to setup the platforms


Create a new file using notepad++ and copy the following inside the document:

{
"platforms": [
{"image": "ground", "x": 0, "y": 546},
{"image": "grass:8x1", "x": 0, "y": 420},
{"image": "grass:2x1", "x": 420, "y": 336},
{"image": "grass:1x1", "x": 588, "y": 504},
{"image": "grass:8x1", "x": 672, "y": 378},
{"image": "grass:4x1", "x": 126, "y": 252},
{"image": "grass:6x1", "x": 462, "y": 168}
],
"lifts" : [
{"image": "grass:2x1", "x": 798, "y": 84, "min:y":50, "max:y":450, "speed":50}
]
}

29
Save the in the directory assets/data (if you don’t have one, create one) under the
name level00.json. As you may have notice, it looks the same as the variable
setupData. Phaser provides tools to read such a file format and create a variable like
our setupData. Let see how it is done.
First, remove the variable setupData from index.js. Secondly, we need to add the file
into the memory (in the preload function) in the same way we preloaded the images
and sound fx. For such a file (text file) we must use:

this.game.load.text("level:0", 'assets/data/level00.json');
When we want to access the file we use the associated name “level:0”. Now we need
to read the content of the file:

level_text = this.game.cache.getText("level:0");
Finally parse the content to create our setup data

setupData = JSON.parse(level_text);
The rest of the code should not changed and it should work. You will see that we can
Head Start 2018

add/remove platforms from the game without changing the index.js file, but by
changing the level00.json. In fact we could have a collection of such files, each
containing a different level of the game.

Better Code via Modularisation


The aim of this section is to modularise our code, that is create several JavaScripts
for our game and store them into separate files. To begin with, we will be separating
each game state into its own file. We will be storing information about assets into
JSON files. One of these JSON file contains the location of all our asset files, i.e.
location and name of all the images needed, as well as audio, and data files. We
have two other JSON files, each one containing the information to build one level of
our game (our game currently has only two different levels). The format of these
two JSON file is the same as the one we have seen previously. You can test the game
at: https://www-users.cs.york.ac.uk/~lblot/outreach/headstart/GameSession3/

30
Setup
Download the zip file from

https://www-users.cs.york.ac.uk/~lblot/outreach/headstart/GameSession3.zip
then unzip the file into your headstart folder.

Add multiple JavaScripts


index.html

<!DOCTYPE html>
<html lang="en-UK">

<head>
<meta charset="utf-8">
<script src="../node_modules/phaser-ce/build/phaser.min.js"></script>
<title> Game </title>
</head>

Head Start 2018


<body>
<!-- include the main game file -->
<script src="js/boot.js"></script>
<script src="js/load.js"></script>
<script src="js/game.js"></script>
<script src="js/gameOver.js"></script>
<script src="js/main.js"></script>
</body>

</html>

Note: You may have to change the path to phaser.min.js depending on where it has
been install. <script src="../build/phaser.min.js"></script> should be the right path
for your setup. If it is not working ask us.

In order to make the game works, we need to indicate in the HTML file where all the
scripts can be found. Note that main.js is our main program that will start the game.
It must be declared last to ensure all script have been loaded before it runs.

31
Add all states to the game
main.js

var gameOptions = {

}

var game = new Phaser.Game(gameOptions.gameWidth,gameOptions.gameHeight);

game.state.add('boot', bootState);
game.state.add('load', loadState);
game.state.add('play', mainState);
game.state.add('gameOver', gameOverState);

game.state.start('boot', true, false,'assets/data/level01_assets.json');


All the states for the game are stored in their own file. In total, we have four states:

1. bootState: to boot the program, instantiated general parameter for the


game, and loading the file containing the parameter of the game
Head Start 2018

2. loadState: Read the parameter of the game from a JSON file and load the
assets (images, audio, data)

3. mainState: this is actually the game, where we can play several level

4. gameOverState: this is the state shown when a player win/lose. It also ask if
the player want to play again and then restart the game.

Finally, once all states are added to the game, we start the game indicating the path
to the JSON file containing the game parameters. In our case level01_assets.json.

level01_assets.json

{
"player": { "type": "image", "source": "assets/images/hero_stopped.png"},
"coin": { "type": "image", "source": "assets/images/coin_icon.png"},
"background": {"type": "image","source": "assets/images/background.png"},

"sfx:jump": { "type": "sfx", "source": "assets/audio/jump.wav" },
"sfx:door": { "type": "sfx", "source": "assets/audio/door.wav" },

"level:0": { "type": "level", "source": "assets/data/level00.json" },
32 "level:1": { "type": "level", "source": "assets/data/level01.json" }
}
JSON files
We have seen during session 2 that it would be useful to get the information to build
a level from a JSON file. This is what we are doing with our game. The first level is
defined by the file level00.json.

level00.json

{
"platforms": [
{"image": "ground", "x": 0, "y": 546},
{"image": "grass:1x1", "x": 588, "y": 504},
{"image": "grass:8x1", "x": 672, "y": 378}
],
"player": {"x": 21, "y": 500},
"coin": {"x": 800, "y": 500}
}

Load another state


In the boot.js, we can see the method create calling the load state, passing

Head Start 2018


assets_data as a parameter to the init function of the loadSate.

create : function () {
var assets_text, assets_data;
assets_text = this.game.cache.getText("assets");
assets_data = JSON.parse(assets_text);
this.game.state.start("load", true, false, assets_data);
}
Whatever objects and values store in assets_data, it will be passed to the
loadState object. This variable can be retrieved in loadState via the init function, and
the parameter level_data. Now all objects in asset_data are in level_data in the file
load.js (show below).

var loadState = {

init : function (level_data) {


this.level_data = level_data;
},

33

You might also like