In this second practice, you're going to create a top-down game that shows an overhead view of the player. It will be a space shooter game, similar to Asteroids. This game will feature a nice balance of player skill, random chance, and increasing challenge. It will reinforce the basic concepts of using Phaser to create a game, plus show you some advanced features in Phaser.
Our game will be an update to the classic Asteroids that we'll call Asteroids 2084. Here's its premise:
The year is 2084. The Mars colony is running out of water and needs to reach the icy moons orbiting Jupiter. You're a spaceship pilot sent on a scouting mission to clear a path to Jupiter through the asteroid belt. Asteroids threaten to collide with your ship from all directions. Your only option is to evade and destroy the asteroids before they destroy you.
In this practice, all the game assets (i.e., images and sounds) and some of the game code will be provided. Focus on two things: (1) becoming familiar with the new Phaser commands that will be introduced, and (2) applying the Phaser commands you've already seen.
PREVIEW VIDEO: Demo of Asteroids 2084
Prepare a new Phaser Game Template. Be sure to place a copy of phaser.min.js into the same folder that has your HTML, CSS, and JS files.
Download this assets.zip file, and extract the file contents, which will be a folder named assets that has two subfolders containing 12 images and 7 sounds. (To extract the file on a Windows computer, right-click on the downloaded zip file, and select Extract All. On a Mac, double-click on the downloaded zip file.)
Place the assets folder into your game template folder.
Test your Phaser game template by previewing the HTML file online. If everything's ready to go, you should see a solid black box (your blank game canvas) on your webpage.
This second practice game will be coded in 15 steps, with the last step being an extra credit challenge. Some steps will involve coding that's similar to things you did previously. However, most of the steps will also introduce and explain new Phaser features.
As a reminder, all your game coding is done in your JS file (code.js).
The steps are outlined below. The instructions for Steps 1-5 start on the next page.
Be sure that you completed the Prep Steps on the previous page.
IMPORTANT: Take the time to read each step, and try to understand how the code works.
Add a sprite for the player. As a reminder, adding a sprite to the game involves 3 steps:
Declare a global variable for the sprite.
Load its spritesheet into memory in the preload()
function.
Add the sprite to the game in the create()
function.
Use player
as the name of the global variable for the sprite.
Use assets/images/spaceship.png for the spritesheet image. It contains 3 animation frames that are each 64 pixels in width and 64 pixels in height. Assign an asset key name to the spritesheet, such as ship
.
Add the player
sprite to the center of the game world, and set the sprite's anchor to its center.
For help with the code for sprites, you can refer back to Step 4 of Practice 1 (or look at your Practice 1 game code).
Refresh your HTML preview to verify that the player sprite appears in the center of the game.
This game will played using keyboard inputs. Most keyboard-based games use either the WASD keys or the arrow keys to move the player's character.
Your game will use the following keys to control the player's spaceship:
left arrow key will rotate the ship counter-clockwise
right arrow key will rotate the ship clockwise
up arrow key will move the ship forward
spacebar key will fire the ship's laser
Since it is common for games to use the arrow keys for input, Phaser has a command to add all four arrow keys as inputs (rather than having to add them individually). Phaser refers to the arrow keys as cursor keys (since many computer programs use the arrow keys to move the cursor).
Create a global variable named arrowKey
.
Add this Phaser command in the create()
function:
This command will make all 4 arrow keys (left, right, up, down) into inputs assigned to the arrowKey
variable. The individual keys are identified using the properties left
, right
, up
, and down
. So in your game, these inputs will be:
arrowKey.left
(used to rotate ship counter-clockwise)
arrowKey.right
(used to rotate ship clockwise)
arrowKey.up
(used to move ship forward)
arrowKey.down
(not used in game)
Create a global variable named fireKey
.
Add a Phaser command in the create()
function to add the spacebar key as an input assigned to the fireKey
variable.
For help with the code to add an individual key as input, you can refer back to Step 5 of Practice 1 (or look at your Practice 1 game code).
Let's make the player's spaceship move in response to the player's inputs. For that, we're going to need to use physics.
Many games features simulated physics. Objects in the games can move and accelerate. The objects can be affected by gravity and friction. Objects can collide and bounce off each other — and will respond differently based on their masses.
The cool thing about simulated physics is that you can make the game world behave however you want: You can change gravity so that objects will "fall up" instead of falling down. You can selectively apply gravity only to certain objects — or eliminate gravity entirely. You get to decide which physics properties to use and how to use them.
Phaser has several physics systems to choose from. We're going to use the Arcade Physics system, which is a good choice for most games. Arcade Physics has lots of useful physics properties and methods, plus support for weapons (i.e., the ability to shoot or throw objects such as bullets, lasers, arrows, fireballs, etc.). It's more simplistic than the other physics systems, but it runs much faster.
First, you have to "start" the physics system by including a Phaser command at the beginning of your create()
function. Then you have to "enable" physics for specific objects in your game. Not every object in the game actually needs to use physics, so you only enable it on the ones that do.
Start the Arcade Physics system by adding this Phaser command as the first line of code in the create()
function:
Enable physics on the player
sprite by adding this Phaser command in the create()
function (after the command that set the anchor for the sprite):
This will create an Arcade Physics body
object associated with the player
sprite. So let's add a couple of physics properties to the player.body
.
Add this Phaser code in the create()
function (after the "enable" physics command for the sprite):
The first line simply sets a limit on the velocity (speed) of the sprite. In Phaser, velocity is measured in pixels per second.
The second line adds a small amount of drag (friction) as the sprite moves. In reality, a spaceship in outer space would experience almost zero drag (because there is no air to cause drag), but since our game screen is relatively small, we'll add a small amount of drag so the spaceship doesn't drift off-screen with every forward movement.
Another thing to notice is that we did not set a gravity value for the player.body
because our game is set in space far from any planets or stars that would create a strong gravity field. Instead, we just want the spaceship to float along, affected only by its engines (and the small amount of drag that we added). In your next practice game, we'll start using gravity.
Let's add code to rotate and move the player
spaceship based on the arrow key inputs.
In Phaser, there are two properties for a sprite's rotation:
angle
can be used to get or set the angle of rotation of the sprite, measured in degrees
rotation
can be used to get or set the angle of rotation of the sprite, measured in radians (2π radians = 360°)
Both angle
and rotation
do the same thing — they just use different units of measurement. Either can be used to get the current value of the sprite's angle of rotation. Either can be used to set the sprite to a specific angle of rotation.
Add this Phaser command in the create()
function (as part of the commands after adding the sprite):
Refresh your HTML preview to verify that the player sprite is now pointed up, instead of to the right.
Setting the angle
to a negative value (up to -180) will rotate the sprite counter-clockwise from its original orientation. Setting the angle
to a positive value (up to 180) will rotate it clockwise from its original orientation. However, it instantly switches the rotation to a specific angle.
Instead, what we want is to make the player
sprite turn in a continuous motion, when the player presses the left or right arrow keys.
There are a couple of Arcade Physics properties that will allow you to turn (rotate) a sprite's body
in a continuous motion:
body.angularVelocity
can be used to get or set the speed of sprite's rotation (measured in pixels per second) — negative values = counter-clockwise rotational speed, positive values = clockwise rotational speed
body.angularAcceleration
can be used to get or set the acceleration of the sprite's rotation (measured in pixels per second squared) — negative values = decrease the rotational speed, positive values = increase the rotational speed
We're going to use body.angularVelocity
, so the spaceship will rotate at a constant speed:
If the left arrow key is pressed, we'll turn the sprite counter-clockwise at a speed of 200 pixels per second.
Otherwise, if the right arrow key is pressed, we'll turn the sprite clockwise at a speed of 200 pixels per second.
Otherwise, if neither the left nor right arrow key is pressed, we'll stop turning the sprite by setting the speed to 0.
To move the player
sprite forward, there is an Arcade Physics method (i.e., function) called accelerationFromRotation()
that will allow us to accelerate a sprite's body in the direction of its current angle of rotation:
If the up arrow key is pressed, we'll move the player
sprite forward by accelerating the sprite at 200 pixels per second squared in the direction of its current rotation.
Otherwise, if the up arrow key is not pressed, we'll stop accelerating the player
sprite by setting the acceleration to 0. The sprite will stop speeding up, but it will continue to drift forward at its current speed — only being slowed down by the drag that we applied to the player
sprite's body (or if the player turns the ship around and starts accelerating in the opposite direction).
Add this code in the update()
function to check the inputs and move the spaceship:
Refresh your HTML preview to verify that you can rotate and fly the spaceship using the arrow keys.
You probably discovered that you can actually fly the spaceship out of the game display. If you fly off-screen, you can turn around and fly back into the game display (but it can be a little tricky to do when you can't see where the ship is pointed).
We want to keep the spaceship within the game world boundaries.
Add this Phaser command in the create()
function (to go along with your other player.body
properties):
Refresh your HTML preview to verify that the spaceship will stay within the game world boundaries as you fly around.
Next let's add an animation and sound effect to the spaceship to make the flying feel more realistic.
Remember that the player
spritesheet has 3 animation frames:
Add this Phaser command in the create()
function (somewhere after adding the player
sprite):
As you can see, this command will add an animation for the player
sprite. Let's examine the code inside the parentheses to understand how it works:
'moving'
assigns a key name to the animation. It is similar to an asset key name or variable name. You decide what name to use, as long as it is a unique name.
[0, 1, 2]
is an array listing the spritesheet frame numbers to use (in order) for the animation. In our case, we're using all 3 frames (0-2). In other cases, a spritesheet might contain frames for different animations, so you only list the specific frames needed for each animation.
10
represents the frame rate that will be used for the animation, in frames per second. Depending on the animation, you might change this number to make it play faster or slower.
true
indicates that this animation should play repeatedly in a loop. If you didn't want the animation to loop, then you can list false
.
Similar to how sounds work in Phaser, the animation won't actually play until you use a command telling it to start playing.
Add this Phaser command in the update()
function, so that the animation will play when the up arrow key is pressed:
Add this Phaser command in the update()
function, so that the animation will stop when the up arrow key is not pressed:
This command stops the animation, and then sets the player
sprite to display frame number 1 (the second frame), which has the smallest engine exhaust flames (to make it seem like the spaceship engine is "idling").
Refresh your HTML preview to verify that the animation plays as you move the spaceship forward and stops when you release the up arrow key.
Now let's add a sound effect for the spaceship's engines.
As a reminder, adding a sound to the game involves 3 steps:
Declare a global variable for the sound.
Load the sound into memory in the preload()
function.
Add the sound to the game in the create()
function.
Use engineSound
as the name of the global variable for the sound.
Use assets/sounds/engine.mp3 for the sound file. Assign a unique asset key name to the sound, such as engine
.
Add engineSound
to the game with a volume of 0.3
. Also, set this sound to loop repeatedly when it plays.
Let's have engineSound
play from the very beginning of the game. To do this, add the command to play the sound in the create()
function (after setting the sound to loop).
For help with the code for sounds, you can refer back to Step 6 of Practice 1 (or look at your Practice 1 game code).
Add this Phaser command in the update()
function to make the sound louder when the up arrow key is pressed:
Then add a similar command in the update()
function to turn this sound back to normal (0.3
) when the up arrow key is not pressed.
Refresh your HTML preview to verify that the engine sound plays continuously but gets louder when you move the spaceship forward.
Now that you've got a spaceship that can fly around realistically, let's add a realistic background.
Preview the image file called space-stars.jpg in your assets/images folder. This image is 800 pixels in width and 600 pixels in height, which is exactly the same size as your game display.
You can add an image as a static background for your game. In fact, you can add multiple images to build a more complex background for a game.
In this game, we're going to add this image as a special background called a tilesprite. A tilesprite is an image with a repeating texture or pattern. Phaser can move and wrap the tilesprite in the game display to simulate a scrolling background.
Adding a tilesprite to the game involves 3 steps:
Declare a global variable for the tilesprite.
Load the tilesprite's image into memory in the preload()
function.
Add the tilesprite to the game in the create()
function.
Add a global variable named space
for the tilesprite.
Add this Phaser command in your preload()
function:
As you can see, this is similar to commands for loading a spritesheet or sound:
'space'
represents an asset key — again, you get to decide the name of the key, as long as each key has a unique name.
'assets/images/space-stars.jpg'
represents the folder path and filename of the image to load.
Add this Phaser command in your create()
function (before the command that adds the player
sprite):
Let's explain what's inside the parentheses:
0, 0
represent (in order) the x and y pixel coordinates for positioning the tilesprite image. This represents the upper-left of the game. This is normally where you want to position background images.
800, 600
represent (in order) the width and height to use for the tilesprite (which is also the same width and height that we set for the game display — we want to fill up the entire background).
'space'
is the asset key name of the image to use for the tilesprite.
Refresh your HTML preview to verify that the space background appears (but doesn't move yet).
ORDER MATTERS: As you create your game world, all the visual objects — such as images, sprites, text, etc. — are added to the game display in layers (meaning they can overlap other objects behind them). The order in which they are added in the create()
function determines the stacking of these layers in the game world.
So if you have an image that is supposed to be a background for your game, then that image should be added first in the create()
function, so it will be the farthest back layer. Each new visual object (image, sprite, etc.) that is added in the code will appear in front of the previous layers.
For example, if you add the player's sprite first and then add the tilesprite background, the player's sprite will be hidden behind the tilesprite. Try testing this out in your code.
Now let's make the tilesprite scroll as the spaceship moves. In order to make the scrolling look realistic, we need to do two things: 1. Scroll the tilesprite in the opposite direction of the spaceship's movement. So if the spaceship is moving to the right, the space background should scroll to the left. 2. Scroll the tilesprite at a slower speed than the speed of the spaceship. Because the space background represents stars that are in the far distance, they should appear to move slower than the spaceship does.
Add this Phaser code in your update()
function (after the if-else statements for the player movement):
This code changes the position of the tilesprite. As you can see, the x-position and y-position are set independently. You can also see that Phaser breaks down the velocity (speed) of the player.body
into x and y components.
By subtracting the player's velocity (after dividing it by 40), we are moving the tilesprite's position in the opposite direction.
By dividing the player's velocity by 40, we are making the change in the tilesprite position smaller (so the tilesprite moves slower than the spaceship). There's nothing magical about 40 — that number happens to produce results that feel right for this particular game when you fly the spaceship. If you don't divide by 40 (or change 40 to a small number), the background will scroll too fast. If you change 40 to a large number, the background will scroll too slowly.
Refresh your HTML preview to verify that the space background appears to scroll in the opposite direction of the spaceship's movement (which makes the flying seem more realistic).
However, you will notice that the tilesprite scrolling will stop when the spaceship collides with the game world boundaries. This is because the "collision" causes the spaceship's velocity to become zero (which then causes the tilesprite position to stop changing).
We want our game to feel like the spaceship can keep flying forever, so we want the tilesprite to keep scrolling even if the spaceship is at or near the edge.
So to fix this, we're going to add some code to keep the spaceship within the game world without using a collision with the game world boundaries. We'll check the position of the spaceship to make sure it never gets any closer than 50 pixels to any boundary (left, right, top, or bottom). This will keep the spaceship on-screen but not actually change its velocity in the game's memory. (It's a bit of a trick because the spaceship will visually stop moving because we keep resetting its position, but the game will think the spaceship is still moving.)
This extra 50 pixels will also leave us some "blank" space along the edge of the game to add the game's user interface elements (such as the score, etc.).
Add this Phaser code in your update()
function (right before the code that scrolls the tilesprite):
As you can see, Phaser has properties to represent the x-position of a sprite's left
and right
edges, as well as the y-position of a sprite's top
and bottom
edges. We can get or set these properties to check and/or change the sprite's position.
Refresh your HTML preview to verify that the spaceship will stay within the game world (inset a bit from the edge) and the space background will keep scrolling if the ship still has forward velocity.
As the spaceship's speed and direction change (due to your flying maneuvers and the ship's drag), the scrolling of the space background should appear to speed up or slow down accordingly.
Now it's time to add some asteroids to your game.
Phaser allows you to create a Group of game objects, such as sprites, images, text, etc. The objects that you add as members of a group are usually related in some way, such as a group of enemy sprites, a group of platforms or walls, etc.
Typically, the members of a group have many similarities — but they can also have some differences. For example, a group of enemy sprites might use the same spritesheet and move at the same speed, but each enemy sprite is probably in a different location and might be moving in a different direction.
One advantage of using a group is that is makes it easier to perform checks or actions on each member of the group. For example, one line of code could be used to set a property for every member in a group, such as setting the gravity value for each group member. Your code could also quickly loop through each member in a group to check a condition and decide what action to take for each individual member, such as deciding whether each enemy sprite in a group is close enough to the player sprite to fire at it.
Another advantage of using a group is that your code can "recycle" objects in the group (which helps save memory and make your game run faster). For example, if you have a group of enemy sprites and one of the enemy sprites is "killed", your game can reuse that sprite later to create another enemy elsewhere in the game.
You're going to create a group of 10 asteroid sprites for your game.
Adding a group to the game usually involves 4 steps:
Declare a global variable for the group.
If the group members will use an image or spritesheet, load the image or spritesheet into memory in the preload()
function.
Add the group to the game in the create()
function.
Add individual members to the group — usually done in the create()
function (but can also be done in the update()
function).
You can even use different images or spritesheets for the members in a group. For example, you could load and use different spritesheets for different types of enemies in a group. (You could also create separate groups for each type of enemy — it just depends on your game's design and what makes more sense for coding it.)
Add a global variable named asteroidGroup
for the asteroid sprites.
Each asteroid in the group will use the same spritesheet. Preload assets/images/asteroid.png for the spritesheet image. It contains 16 animation frames that are each 40 pixels in width and 40 pixels in height. When these frames are played, it will make the asteroid look like it is spinning. Assign an asset key name to the spritesheet, such as asteroid
.
Add this Phaser command in your create()
function (after the code related to the player):
Similar to what we did for the player
sprite, we will need to enable physics on every asteroid sprite that we add to the group. Rather than needing to do this for each individual asteroid, we can use a single line of code to enable physics for the entire group (even before we add any members to the group).
Add this Phaser command in the create()
function (after the command that added the group):
Now we can start adding members to the group. There are several ways to do this:
Add each member using separate lines of code for each member.
Use a for
loop to quickly add multiple members using the same lines of code.
Do both: use a for
loop to add a set of members, and then use separate code to individually add certain members that have very unique properties.
We're going to use a for
loop to quickly add each of the 10 asteroids to the group. In our game, we want each asteroid to start at a random location at the start of the game. Then we'll also make each asteroid move at a random speed in a random direction.
Add this code in the create()
function (after the command that added the group):
A for
loop contains a set of code within curly braces { }
that will be performed for a certain number of times (that's how it gets its name).
The number of times that the for
loop repeats is determined by the code in the parentheses ( )
after the word for
:
var i = 0;
sets an initial value for a local variable that will be used as a loop counter. Traditionally, you will see i
used as the variable name for the loop counter because it stands for the word "iteration" (which means repetition).
i < 10
sets a condition that is checked at the beginning of each loop — if the condition is true, the loop will occur — meaning the code inside the curly braces { }
will be performed. In this particular case, if the value of i
is less than 10, then another loop will occur.
i++
increases the loop counter by 1 at the end of each loop. (i++
is shorthand for i = i + 1
).
Here's how it would work when the game code runs:
At the start of the for
loop, i
equals 0, and since 0 is less than 10, it will perform a loop — performing the code inside the curly braces.
At the end of the first loop, i
will be increased from 0 to 1. Since 1 is still less than 10, it will perform another loop — performing the code inside the curly braces another time.
At the end of the second loop, i
will increase to 2. Since 2 is still less than 10, it will perform another loop of the code.
This process will keep repeating until i
has increased to 10. At that point, 10 is not less than 10 (duh, they're equal) — so the condition becomes false, which causes the for
loop to stop repeating. The for
loop will have repeated itself exactly 10 times.
Check your understanding: If you wanted to create 15 asteroids in the group, what would you change in the code?
Now let's look at the Phaser commands inside the for
loop curly braces { }
:
The first command creates a new member in the asteroidGroup
and assigns that member to a local variable named asteroid
(local variables only exist inside a particular loop, statement, or function). Inside the parentheses, you indicate (in order) the x and y coordinates of the object's location in the game and then the asset key name to use for the object. In this case, each asteroid will get a random x and random y position and will use the asteroid
spritesheet.
The second command sets the anchor point for the asteroid's position to be the center of the sprite. This command should be familiar to you by now.
Refresh your HTML preview to verify that 10 asteroids appear in random locations in the game. Refresh it a few more times to verify that the asteroids' locations are different every time.
If you fly the spaceship around, you will see that the asteroids remain fixed in location (because you haven't added any code to make them move yet). You'll also discover that the spaceship will fly behind the asteroids. This is because we added the asteroid group after adding the player sprite. If you were to switch the order of their code in your create()
function, the spaceship would fly in front of the asteroids. Let's leave it so that player sprite is added before the asteroid group is added.
Let's add two possible animations for each asteroid: either spinning clockwise or spinning counter-clockwise.
Add a Phaser command inside the for
loop (after the command that sets the asteroid anchor) to add an asteroid
animation named spin-clock
that will play all 16 frames in order (list an array from 0-15) at a frame rate of 16 (meaning it will play all 16 frames in one second) and set it to loop.
For help, look at your code from Step 3 that added an animation for the player
sprite.
Add another asteroid
animation named spin-counter
that lists the 16 frames in reverse order (from 15 down to 0). Use the same frame rate (16) and set it to loop.
Now that you've added the two possible animations for each asteroid, let's randomly pick one to use for each asteroid. We'll make it like flipping a coin — a 50/50 chance.
Add this code inside the for
loop (after the commands that added the asteroid animations):
Remember that Math.random()
generates a random decimal value between 0 and 1. So if the value is less than 0.5, the asteroid will play the clockwise animation. Otherwise, that asteroid will play the counter-clockwise animation. Each animation has a 50% chance of being selected. Once an animation is randomly selected for an individual asteroid, it will keep playing that same animation over and over in a loop.
Refresh your HTML preview to verify that each asteroid spins either clockwise or counter-clockwise.
Let's make each asteroid move at a random speed in a random direction. We'll use the body.velocity
property to do this. If you remember from earlier, Phaser breaks down velocity into x and y components.
Velocity is a combination of speed and direction. Velocity can be either a positive value or a negative value. The speed is determined by the magnitude (size) of the value: lower numbers are slower, higher numbers are faster. The direction of the motion is determined by whether the value is positive or negative:
Positive values for body.velocity.x
move the sprite to the right. Negative values move the sprite to the left.
Positive values for body.velocity.y
move the sprite down. Negative values move the sprite up.
Our game is going to set a maximum possible speed for each asteroid, and then increase this maximum as the game progresses (we'll do this later in Step 9).
Add a global variable called maxSpeed
and assign it an initial value of 100:
Add this code inside the for
loop (after the code that randomly selects the asteroid animation):
As you can see, the x and y components of the velocity are set individually. Each velocity component will be a random number between 0 and 100 (the current value of maxSpeed
). This will be the speed in pixels per second.
Then each velocity component has a 50% random chance of being changed to a negative number:
*= -1
is shorthand for multiply the current value of the variable by -1
, and then assign the result to be the new value of the variable
Refresh your HTML preview to verify that each asteroid moves at a random speed and direction.
You will notice that eventually all the asteroids move off-screen and disappear. That's going to make for a very short and unexciting game.
In the original Asteroids game, if an asteroid crosses a game world boundary, the asteroid reappears on the opposite side of the screen. For example, if an asteroid moves down past the bottom boundary, it will reappear at the top of the screen. Of course, this is not realistic — but for the purposes of the game, it makes it more interesting and challenging because the player has to mentally track where each asteroid will reappear when it wraps around the screen.
Luckily, Phaser has a function named wrap()
built into the game world object to do just this.
Add this Phaser code in your update()
function (after the code that scrolls the tilesprite):
This command will loop through each member of the asteroidGroup
and perform the code listed inside the function's curly braces { }
. As you can see, the code in the function will make each asteroid wrap around the game world. The number 20
represents how many pixels the sprite can move past the game boundary before it will reappear on the opposite side.
In this case, 20
is a good number to use because that's half the size of the asteroid sprite. Because the sprite anchor is set to its center (i.e., half-way), the asteroid sprite will have to move off-screen until it disappears completely before it will start to reappear on the opposite side.
Refresh your HTML preview to verify that each asteroid wraps around to the opposite side of the game if it moves off-screen.
RESOURCE:
RESOURCE:
Be sure that you completed Coding Steps 6-10 on the previous page.
IMPORTANT: Take the time to read each step, and try to understand how the code works.
Right now, our game is very unforgiving — if the spaceship collides with just one asteroid, the game is over.
Let's give the spaceship some "shields" that will be damaged every time the ship collides with an asteroid. Once all the shields are gone, then the ship will explode.
Many games have some kind of health bar for the player's character — when the health reaches zero, the player dies in the game. The shields that we're going to add to our game are just a health bar for the spaceship.
Phaser has built-in properties and methods to keep track of the health of a sprite. When a sprite's health reaches zero, Phaser will automatically kill()
the sprite.
health
is the property that represents a sprite's health value. You can set it to any value (but many game designers use 100
as the starting value for a sprite's health).
maxHealth
is a property that sets a maximum value for the sprite's health. You can set this to any value (but many game designers also use 100
for the maximum value). If your game has ways for the sprite's health to increase (e.g., by collecting health packs, resting, etc.), this will prevent the health from exceeding this limit.
damage()
is a method to decrease a sprite's health value. Inside the parentheses, you include a value representing the amount of damage.
heal()
is a method to increase a sprite's health value. Inside the parentheses, you include a value representing the amount of healing.
Add this Phaser code in your create()
function (after the command that set the angle of the player):
This will set the starting value of the player's health to 100 (and also set 100 as the maximum value).
Instead of killing the player
sprite when it collides with an asteroid, let's damage the player's health by 25. After 4 collisions, the player's health will be zero, and Phaser will automatically kill()
the player
sprite.
In your collideAsteriod()
function, replace the command that kills the player with this command instead:
Refresh your HTML preview to verify that it takes 4 collisions with asteroids before the spaceship explodes.
Now we just need to add a health bar, so the player has some visual feedback to track the amount of "shields" remaining for their spaceship.
Typically, a health bar is a rectangle that decreases (or increases) in width (or height) to represent the amount of health remaining. An easy way to do this in Phaser is to scale an image of a rectangle in one direction (either its width or its height).
We're going to use images of a red rectangle and a green rectangle to create our health bar:
Notice that these two bars are the exact same size. We're going to position them on top of each other as layers, so the red bar is behind the green bar.
The width of the green bar will represent the amount of health ("shields") remaining. When the shields are at maximum, the green bar will completely cover and hide the red bar.
When the player's health is damaged, we will scale down the width of the green bar, which will also reveal some of the red bar behind it. This will make it easier for the player to quickly see how much or how little health is remaining.
Let's first add a text label for the health bar (so it will be clear to the player what this bar represents):
Add a global variable for healthText
In your create()
function (after the code that added scoreText
), add healthText
as a text object at position 210, 20
. Set the text to display: 'Shields'
. Use the same font styling that you used for scoreText
(Arial, 20px, bold, #ffffff)
You do not need to set the text's anchor or add a text shadow.
Refresh your HTML preview to verify that the text label (Shields) appears at the top of the game to the right of the score.
We're going to add the red and green bars to the game as images instead of as sprites.
Add a global variable for healthBar
, which will represent the image of the green bar (we don't need to create a variable for the red bar)
Preload assets/images/health-red.png as the image for the red bar. Assign it a unique asset key name, such as red-bar
. (Hint: If you need help, look back at Step 4 to see how to preload an image.)
Preload assets/images/health-green.png as the image for the green bar. Assign it a unique asset key name, such as green-bar
.
Add this Phaser command in your create()
function (after the code that added healthText
):
This command does just what you think — it adds an image at position 300, 20
using the red-bar
asset. Because we're not going to scale or change the red bar in any way, we don't have to assign it to a variable — we can just add it to the game.
Refresh your HTML preview to verify that the red bar appears at the top of the game to the right of the "Shields" text.
Now add this Phaser command (immediately after the command that added the red bar):
This command adds the green bar at the exact same position and assigns it to the healthBar
variable (so that we can scale the green bar when the health value changes).
Because you added the red bar first and then added the green bar, the green bar will be layered on top of the red bar.
Refresh your HTML preview to verify that the green bar appears at the top of the game (and hides the red bar, which is actually behind it).
Now we just need to scale the width of the green bar, so it will shrink to represent the decrease in the player's health.
Phaser has a scale
property for game objects (such as: text, images, sprites, etc.) that allows you to scale the object's width and height to make the object larger or smaller.
To shrink the width or height, use a scale value that is between 0 and 1.
To enlarge the width or height, use a scale value that is greater than 1.
To keep the width or height the same size, use a scale value of exactly 1.
For our health bar, we only want to change the width of the green bar. We'll keep the height of the green bar the same size.
We can calculate the scale needed for the width of the healthBar
by using a simple fraction — the scale should be the player's current health value divided by its maximum possible health value.
For example, if the player's health is currently 25 and its maximum health value is 100, the scale for the width of the health bar should be 0.25 (25/100), which would look like this:
Add this Phaser command inside your collideAsteroid()
function (immediately after the command that damages the player):
This command sets the scale values for the width and height (in order) of the healthbar
image. The scale for the width will be calculated by dividing the player's health by its maximum health. The scale for the height will be kept at 1, so the height of the image doesn't change.
Refresh your HTML preview to verify that the width of the green health bar shrinks in proportion to the damage from each collision.
In our case, each collision should shrink the width of the health bar by 25% of its original width until it reaches zero (100 → 75 → 50 → 25 → 0). When the shields reach zero, the spaceship will explode.
Some games only give the player one life during the game — when the player's character is killed, the game is over. Other games give the player unlimited lives, allowing the player to restart the game again and again (often by sending the player back to a previous checkpoint in the game). Some games give the player a limited number of lives.
The original Asteroids game (like many video games from the 1970s and '80s) gave the player 3 lives at the start of the game. The player could also earn extra lives by reaching certain scores in the game.
We're going to add both of these features to our game. In this step, we'll give the player 3 lives (spaceships) at the start of the game. When the spaceship explodes, the player loses one life — but a new spaceship will appear as long as the player has at least one life left. When the player is out of lives, the game will be over.
Our game will also give the player a chance to earn an extra life as a bonus after every 10,000 score points (10,000 → 20,000 → 30,000 → 40,000, etc.). However, our game will also limit the total number of lives (spaceships) to a maximum of 5 lives.
Our game could just list the number of lives as text on the screen. Instead, we'll show spaceship icons to represent the number of lives. Visually, this will make it easier for the player to see at a glance how many lives remain. It will sort of be like a health bar for the number of lives left.
Here's the image for the number of lives remaining. It shows 5 spaceship icons because that's the maximum number of lives we decided to allow the player to have:
If we were to scale this image, we could make it look larger or smaller — but it would still show 5 spaceships.
So instead of scaling this image, we are going to hide part of it when the number of lives is less than 5.
Phaser has a method called crop()
that allows you to only show part of an image (and hide the rest). You can also change the "crop" later to show less (or more) of the original image. The "crop" can be changed or reversed whenever you need because it doesn't actually alter the original image.
Let's first add a text label for the spaceship icons (so it will be absolutely clear to the player what these icons represent):
Add a global variable for livesText
In your create()
function (after the code that added healthBar
), add livesText
as a text object at position 590, 20
. Set the text to display: 'Ships'
. Use the same font styling that you used for scoreText
and healthText
(Arial, 20px, bold, #ffffff)
You do not need to set the text's anchor or add a text shadow.
Refresh your HTML preview to verify that the text label (Ships) appears at the top of the game to the right of the Shields health bar.
Now you're going to add the spaceship icons to the game as an image:
Add a global variable for livesBar
Preload assets/images/ship-lives.png as the image for the spaceship icons. Assign it a unique asset key name, such as lives
.
Add the image to the game at position 655, 20
and assign it to the livesBar
variable.
Refresh your HTML preview to verify that the 5 spaceship icons appear at the top of the game to the right of the "Ships" text.
Next we're going to start the player with 3 lives and crop the livesBar
to only show 3 spaceships.
In order to crop an image, you create a rectangle of a specified width and height to represent the crop that you want to apply to an image. Then you can crop()
the image using this rectangle. Any part of the original image that is outside of the crop rectangle will be hidden.
The spaceship icons image is similar to a spritesheet containing frames. The image has 5 identical spaceship icons, which are each 25 pixels in width and 25 pixels in height (including the transparent area around each icon). The entire image is 125 pixels in width and 25 pixels in height.
So we can set the width of the crop rectangle based on how many lives the player has left. For example, if the player has 3 lives left, then the crop rectangle should be set to a width of 75 pixels (3×25).
We want the height of the crop rectangle to be the same as the height of the spaceship icons image (which is 25 pixels in height).
Add these variables to your code:
Add a global variable named shipLives
and assign it an initial value of 3
Add a global variable called livesCrop
to represent the crop rectangle
Add this Phaser code to your create()
function (after the command that added livesBar
):
Let's examine this code:
The first line of code creates a rectangle that you'll use for the crop. 0, 0
represent the position of the top-left corner of the crop rectangle (relative to the top-left corner of the image that it will crop). The next two numbers represent the width and height (in order) of the rectangle: shipLives * 25
is the width, and 25
is the height.
The second line applies the crop for the livesBar
image using the livesCrop
rectangle.
Refresh your HTML preview to verify that the only 3 spaceship icons appear at the top of the game.
Now we need to actually change the number of shipLives
every time the player's spaceship is destroyed. Then we will want to re-crop the livesBar
using a new width (based on how many lives remain).
As a reminder, in your create()
function, you added an event that will run a function whenever the player
is killed. This is where your game resets the explosion
sprite and plays its animation. Now you're going to add some more code inside this event function to change the number of lives remaining and adjust the crop for the livesBar
.
Add Phaser commands in your player.events.onKilled
function to do the following (in order):
Decrease the value of shipLives
by 1
Change the width of livesCrop
by calculating its new value: livesBar.width = shipLives * 25;
Apply the crop for livesBar
using the livesCrop
rectangle
Refresh your HTML preview to verify that only 2 spaceship icons will be shown after the spaceship is destroyed.
Now we need to respawn the player's spaceship if the player still has lives yet. We can use an if-else statement to determine if the number of lives left is greater than zero (otherwise the game is over).
Add this Phaser code in your player.events.onKilled
function (after the code that updated the crop for livesBar
):
If the player still has at least one life left, this code will:
position the player
sprite back at the center of the game with the spaceship pointing up (-90° angle)
sets the player
sprite's velocity and acceleration to zero
brings the player
sprite back to life with maximum health using the revive()
method (alternatively, we could have used the reset()
method)
fades the player
sprite into view using a to
tween on the sprite's alpha
property (2000
means the tween will take 2 seconds to complete)
Otherwise, the player has zero lives left, which means the game is over. In Step 14, we'll add some code to run when the game's over.
Refresh your HTML preview to verify that a new spaceship will appear after the spaceship is destroyed. After 3 spaceships have been destroyed, the game should be over.
Let's add some audio feedback to make it clear to the player that a new spaceship has appeared.
Add a sound effect that will play one time whenever a new spaceship appears:
Use teleportSound
as the name of the global variable for the sound.
Use assets/sounds/teleport.mp3 for the sound file. Assign a unique asset key name to the sound, such as teleport
.
Add teleportSound
to the game with a volume of 0.5
.
Add a Phaser command to play teleportSound
inside your player.events.onKilled
function (after the code for the tween that fades in the new spaceship).
Refresh your HTML preview to verify that the teleport sound plays whenever a new spaceship appears.
Let's reward the player with an extra life every 10,000 score points (10,000 → 20,000 → 30,000 → 40,000, etc.). However, we will set a limit of 5 lives that the player can have at any point.
If the player already has 5 lives, we'll reward the player by setting the shields (health) back to maximum.
Let's play a sound effect as feedback to the player when either reward is received. We'll also do a visual effect when a new life is awarded.
If the player already has 5 lives and maximum health, then no reward will be given, but the player will get another chance to earn a reward after another 10,000 points.
Add code to your game to do the following:
Add a global variable named maxLives
and assign it a value of 5
Add a global variable named newLife
and assign it an initial value of 10000
Add a global variable named lifeSound
for the sound effect
Preload assets/sounds/extra-life.wav and assign it a unique asset key name, such as life
Add lifeSound
to the game with a volume of 0.5
Now let's plan out the code that we'll need to check to see if the player has earned an extra life reward.
If the player's score
is greater than or equal to newLife
, then the player has earned a reward:
Next we need to check if the number of shipLives
is less than maxLives
. If that's true, we'll give the player an extra life.
Otherwise the player already has the maximum number of lives, so we'll check if the player's health
is less than its maxHealth
. If that's true, we'll set the health back to its maximum.
In either case, we need to increase newLife
by another 10000
points, so the player can earn another reward later.
Let's add the code for this check as a separate function in your game code.
Add a new custom function named checkNewLife()
(after your shootAsteroid()
function).
Add this code inside the curly braces { }
of your checkNewLife()
function:
As you can see, this code will perform the check that we planned out. However, there is some missing code that you'll need to add inside the if-else statements.
Add Phaser commands to award an extra life:
Increase the value of shipLives
by 1
Change the width of livesCrop
by calculating its new value based on shipLives
Set the crop for livesBar
using the livesCrop
rectangle
Update the crop for livesBar
Play lifeSound
Make the game screen flash a bright green color by using this command: game.camera.flash(0x00ff00, 500);
Add Phaser commands to replenish the player's health (if the maximum number of lives is already reached):
Change the value of player.health
to be equal to player.maxHealth
Scale the healthBar
Play lifeSound
Most importantly, we need to call the checkNewLife()
function, in order for it to actually run. We could call this function either inside the shootAsteroid()
function (after the code that increases the score
) OR inside the update()
function (after the collide()
commands is a good spot) — choose just one of those places to call the function with this command:
Refresh your HTML preview to verify that an extra life is awarded when the score reaches 10000 points. Another extra life should be awarded at 20000 points. If possible, verify that the shields will be replenished to maximum if you already have 5 ships when you reach the next new life score.
At this point, you have a working version of Asteroids with lots of features. Now we're going to add a simple "start screen" and "game over screen" to finish it.
At the start of the game, let's show an image of our game's title (Asteroids 2084).
Add code to do the following:
Add a global variable named gameTitle
for the title image.
Preload assets/images/asteroids-2084-title.png for the title image. Assign a unique asset key name to the image, such as title
.
At the end of your create()
function (after the code that added and cropped the livesBar
), add the title image 100 pixels above the game's center, and assign the image to the gameTitle
variable.
Set the title image's anchor to be its center.
Set the scale for the title image to be 0.75
for both its width and height.
Refresh your HTML preview to verify that the game title appears on the screen. Currently, it will remain on-screen during the game, but we'll fix that in a bit.
Let's add a way for the player to start the game when he or she is ready. We'll wait to show the spaceship until the player has pressed the fire key (spacebar). Once the fire key is pressed, we'll remove the game title (and a start instruction) from the screen, show the spaceship, and let the gameplay begin.
Add code to do the following:
Add a global variable named startText
for the start instruction text.
In your create()
function (after the code that added the gameTitle
), add startText
as a text object positioned 200 pixels below the game's center. Set the text to display: 'Press Fire to Start Mission'
. For the font style, use Arial, 30px, bold, #00ff00 (which will make the text slightly larger and bright green).
Set the text's anchor to be its center.
Refresh your HTML preview to verify that the start instruction text appears on the screen. (Just like the game title, it currently remains on-screen during the game, but we'll change that soon.)
Now let's hide the spaceship until the player actually presses the fire key to start the mission.
Add these Phaser commands (inside your create()
function after the code that added the player animation):
This code hides the player
sprite by removing it from the game. Later, your code will add the sprite back into the game by setting the exists
property back to true
.
Refresh your HTML preview to verify that the spaceship no longer appears on the screen.
You probably discovered that pressing the fire key (spacebar) doesn't do anything yet. We're about to change that.
You might have also discovered that pressing the up arrow key will cause the engineSound
to get louder (as if the spaceship were accelerating, except it doesn't exist yet).
Modify your existing if statement for the arrowKey.up
, so it will check whether the arrowKey.up
is being pressed AND the player exists.
Hint: Look at your if statement for the fireKey
as an example of how to do this
Refresh your HTML preview to verify that the engine sound will not get louder if you press the up arrow key when the spaceship is not visible.
Now we want to add a signal that will "start" the game when the player presses the fire key. However, we only want this signal to run one time. Once the game is "started", we want the fire key to fire the spaceship's laser.
Add this Phaser command in your create()
function (after the code that added the fireKey
as a keyboard input):
This command will add a one-time signal to the fireKey
: when this key is pressed down, it will call a custom function named startGame
(which you will need to create).
Add a new custom function named startGame()
(after your checkNewLife()
function).
Add this code inside the curly braces { }
of your startGame()
function:
As you can see, this code adds several to
tweens that change the startText
and gameTitle
. All these tweens use the Phaser.Easing.Cubic.Out
pattern, and true
means the tweens will start automatically (once the startGame()
function is called).
The startText
will fade out (to an alpha value of zero, which is transparent) over 0.25 seconds (250
milliseconds).
The gameTitle
will fade out over 3 seconds but only starts after a 0.25 second delay (the 250
after true
represents a delay value).
At the same time, the gameTitle
will also zoom out over 3 seconds (3000
) by changing the scale of the image to 3 in both directions. This will also only start after a 0.25 second delay.
Now you need to add some missing code in this function to fade in the player
sprite:
Play the teleportSound
(as audio feedback to the player)
Make the player
sprite exist (by changing its exists
property to true
)
Add a to
tween to make the player
sprite fade into view by changing its alpha value to 1
over 2 seconds. Use the Phaser.Easing.Cubic.Out
pattern, and set the tween to start automatically (with no delay).
Refresh your HTML preview to verify that pressing the fire key will "start" the game (fading out the start instruction, fading and zooming out the game title, fading in the spaceship and playing the teleport sound).
Now let's add a simple "game over" screen once the player is out of lives. We'll display some text and allow the player to restart a new game (by pressing the fire key).
Add code to do the following:
Add a global variable named gameOverText
In your create()
function (after the code that added the startText
), add gameOverText
as a text object positioned 100 pixels above the game's center. Set the text to display: 'Game Over'
. For the font style, use Arial, 48px, bold, #ff0000 (which will make the text red).
Set the text's anchor to be its center
Make the text invisible (i.e., transparent) by setting its visible
property to false
Now we need to display the gameOverText
and the startText
when the player has run out of lives.
Add Phaser code in your player.events.onKilled
function (inside the else
curly braces for when the player has no lives left) to do the following:
Make gameOverText
visible
Set the scale for gameOverText
to be 3
for both its width and height
Add a to
tween to make gameOverText
fade into view by changing its alpha value to 1
over 1 second. Use the Phaser.Easing.Cubic.Out
pattern, and set the tween to start automatically (with no delay).
Add a to
tween to make gameOverText.scale
zoom in by changing its value to 1
(for both directions) over 1 second. Use the Phaser.Easing.Cubic.Out
pattern, and set the tween to start automatically (with no delay).
Add a to
tween to make startText
fade into view by changing its alpha value to 1
over 0.5 seconds. Use the Phaser.Easing.Cubic.Out
pattern, and set the tween to start automatically after a 2 second delay.
Refresh your HTML preview to verify that the game over text and start instruction text appear on the screen when the player has run out of lives (game over text should fade and zoom in first, then start instruction should fade in).
Finally, when the game is over, we want to add a signal that will "restart" the game when the player presses the fire key again. However, we only want this signal to run one time.
Add a Phaser command in your player.events.onKilled
function (when the game is over) to create a one-time signal that will call a custom function named restartGame
when the firekey
is pressed.
Add a new custom function named restartGame()
(after your startGame
function).
The restartGame()
function needs to reset some of the game variables (such as: score
, shipLives
, etc.) back to their original values, and then restart the game.
Add code inside the curly braces { }
of your restartGame()
function to do the following:
Change the value of score
back to 0
Change the value of shipLives
back to 3
Change the value of newLife
back to 10000
Change the value of the asteroids' maxSpeed
back to 100
Restart the game state with this command:
Refresh your HTML preview to verify that a new game can be restarted after the current game is over.
Congratulations, you've completed your second practice game! Hopefully, the new Phaser commands made sense — you'll get the chance to use them again in the next practice game.
If you have the time and interest, you can add an enemy spaceship in Step 15. Be forewarned that it will require a lot of additional code, and only a small amount will be provided to you. Luckily, most of the necessary code is similar to things you've already done (so you'll be able to copy and modify portions of your existing game code).
The original Asteroids game had enemy spaceships that would periodically appear and shoot at the player, adding an extra challenge to the game — but rewarding the player with a large score point bonus if the player destroys the enemy.
In this step, you can add an enemy ship to your game. You'll add a timer event to make the enemy ship appear every 30 seconds and move across the screen in a random direction and speed. You'll also add a simple AI (artificial intelligence) to make the enemy aim and shoot at the player.
Adding this enemy ship will involve lots of sub-steps with lots of additional code (but most of it is similar to things you already have in your code). However, adding the enemy ship will be worth it because it really increases the excitement and challenge of the gameplay experience.
Add global variables for the following:
enemy
(enemy spaceship sprite)
enemyLaser
(enemy weapon)
enemyExplosion
(animated explosion sprite for enemy)
enemyFireSound
(sound played when enemy weapon is fired)
enemyAlarmSound
(sound played when enemy is alive)
In your preload()
function, load the following assets:
Load assets/images/enemy-ship.png as a spritesheet, and assign it a unique asset key name, such as enemy-ship
. Each animation frame in this spritesheet is 64 pixels in width and 64 pixels in height.
Load assets/images/enemy-laser.png as an image, and assign it a unique asset key name, such as enemy-bullet
Load assets/sounds/enemy-fire.wav as audio, and assign it a unique asset key name, such as enemy-fire
Load assets/images/alarm.mp3 as audio, and assign it a unique asset key name, such as enemy-alarm
The enemyExplosion
will just use the same spritesheet as the player explosion
does (so your code does not need to load it a second time).
In your create()
function (after the code that added the player
sprite), add code for the enemy
sprite:
Add the enemy
sprite at position 0, 0
(which is a temporary position) using the enemy-ship
asset
Set the sprite's anchor to its center
Enable Arcade Physics for the sprite
Set the sprite's body collision area to be a circle with a radius of 30 pixels that is offset 2 pixels from the left edge of the frame and 2 pixels from the top edge
Have the game automatically kill the sprite when it leaves the game world boundaries by adding this code:
Add an animation to the sprite to play all 3 frames (in order 0-2) at 10 frames per second in a continuous loop
Play the animation (so it will already be playing anytime the enemy
is on-screen)
Hide the sprite (until it is spawned) by setting its exists
property to false
In your create()
function (before the code that adds the enemy
sprite), add code for the enemyLaser
weapon:
Add the enemyLaser
weapon to the game with 5 bullets using the enemy-bullet
asset
Set the bullets to be killed automatically if they leave the camera boundaries
Set the bullet speed to 400
pixels per second
Set the fire rate to 750
(once every 0.75 seconds)
Set the bullet body collision area to be a rectangle 16 pixels in width and 16 pixels in height that is offset 4 pixels from the left edge and 4 pixels from the top edge
After the code that adds the enemy
sprite, add a command to:
Set the enemyLaser
weapon to track the enemy
sprite with no offset but don't track the sprite's rotation (set to false
)
In your create()
function (after the code that adds the explosion
sprite used for the player), add code for the enemyExplosion
sprite:
Add the enemyExplosion
sprite at position 0, 0
(which is a temporary position) using the explosion
asset (same one used for player's explosion)
Set the sprite's anchor to its center
Add an animation to the sprite to play all 16 frames (in order 0-15) at 30 frames per second (with no loop)
Hide the sprite (until it is needed) by setting its visible
property to false
In your create()
function, add code for enemyFireSound
:
Add enemyFireSound
to the game with a volume of 0.5
After the code that adds the enemyLaser
, add an enemyLaser.onFire
function containing a command to play the sound.
You're going to create a custom function to spawn (generate) an enemy spaceship. Later, you'll add a timer to call (run) this custom function every 30 seconds.
Every time a new enemy
is spawned, we want it to appear at a random location along one of the edges of the screen. The enemy
should then move across the screen at a random speed and in a random direction.
After your restartGame()
function, add a new custom function called spawnEnemy()
.
Add this Phaser code inside the curly braces { }
of the function:
The first line of this code checks to see whether the player
sprite exists. If the player hasn't started the game yet or the game is over, player.exists
will be false
. If that's the case, the function will simply return
(i.e., end) without performing the other code in this function.
The rest of the code in this function generates a random number between 1-4 to decide whether the enemy will be spawned from the top, right, bottom, or left of the game screen. Then it generates a random starting position to place the enemy
somewhere along the edge of the screen. It also generates a random velocity to move the enemy
across the screen towards the opposite side (in a straight line). The ranges for the random values have been chosen to keep the enemy
on-screen for several seconds.
The last command brings the enemy
sprite back to life.
In your create()
function, add enemyAlarmSound
to the game with a volume of 0.1
, and set the sound to loop continuously.
In your spawnEnemy()
function, play the sound when the enemy is brought back to life.
In your create()
function (after the code that adds the enemy
), add an enemy.events.onKilled
function containing a command to stop the sound.
We want to add a simple AI (artificial intelligence) to control the enemy
. The enemy
will automatically move across the screen based on the velocity it was given when it was spawned. However, as it moves, we want the enemy
to rotate to face towards the player
, so the enemy
can fire its laser at the player
.
Add this Phaser code in your update()
function (after the code that spawns new asteroids):
When the enemy
exists on-screen, this code will rotate the enemy
sprite to face towards the player
(by calculating the angle between these two sprites). Then it adds a small amount of error to the enemyLaser.fireAngle
. (Otherwise, the enemy is always a perfect shot, making it too difficult for the player to avoid being hit.) Then the enemyLaser
fires towards the direction of the player
. The enemyLaser.fireRate
(which you set in the create()
function) will automatically determine how frequently the enemy actually fires its weapon.
In your create()
function (after the code that adds the enemy
), add this Phaser command:
This command adds a timer event that will run in a loop: Every 30 seconds, it will call (run) the custom function named spawnEnemy
.
Refresh your HTML preview to verify that the enemy will appear once every 30 seconds (from a random edge location - top, right, bottom, or left). As the enemy flies across the screen, it should rotate to face towards the player and fire its weapon. The alarm sound and enemy fire sound should play.
However, at this point, the enemy and its laser bullets don't affect anything else in the game because you haven't added any collisions involving the enemy or its bullets.
In your update()
function, add a collide()
command for player
and enemy
(list in this order) that will call a custom function named collideEnemy
.
At the end of your code, add a new custom function called collideEnemy()
.
Inside the parentheses ( )
after the function's name, list these parameters (in order): player, enemy
Inside the curly braces { }
of the function, add Phaser commands to do the following:
Kill the enemy
Reset enemyExplosion
to the x and y position of the enemy
Play the enemyExplosion
animation
Play boomSound
Shake the game camera
Damage the player
by 25
health points
Scale the player's healthBar
Refresh your HTML preview to verify that the player's spaceship is damaged and the enemy spaceship explodes when they collide.
In your update()
function, add a collide()
command for laser.bullets
and enemy
(list in this order) that will call a custom function named shootEnemy
.
At the end of your code, add a new custom function called shootEnemy()
.
Inside the parentheses ()
after the function's name, list these parameters (in order): bullet, enemy
Inside the curly braces { }
of the function, add Phaser commands to do the following:
Kill the bullet
Kill the enemy
Reset enemyExplosion
to the x and y position of the enemy
Play the enemyExplosion
animation
Play boomSound
Add 2500
to the player's score
Update scoreText
to display the new score
value
Call the checkNewLife()
function (NOTE: This is not needed if a call to checkNewLife()
is already listed in your update()
function)
Refresh your HTML preview to verify that the player's laser can destroy the enemy spaceship, awarding the player 2500 score points.
In your update()
function, add an overlap()
command for player
and enemyLaser.bullets
(list in this order) that will call a custom function named shootPlayer
. As a reminder, overlap()
is similar to collide()
except overlap()
doesn't transfer momentum between the two objects. (If we used a collide()
function here, the enemy's bullets would slow down the player's spaceship or even push it backwards, which seems unnatural.)
At the end of your code, add a new custom function called shootPlayer()
.
Inside the parentheses ()
after the function's name, list these parameters (in order): player, bullet
Inside the curly braces { }
of the function, add Phaser commands to do the following:
Kill the bullet
Play boomSound
Shake the game camera
Damage the player
by 25
health points
Scale the player's healthBar
Refresh your HTML preview to verify that the enemy's laser can damage the player's spaceship.
In your update()
function, add a collide()
command for enemyLaser.bullets
and asteroidGroup
that will call a custom function named removeAsteroid
.
At the end of your code, add a new custom function called removeAsteroid()
.
Inside the parentheses ( )
after the function's name, list these parameters (in order): bullet, asteroid
Inside the curly braces { }
of the function, add Phaser commands to do the following:
Kill the bullet
Set the x and y positions of asteroidParticles
to the x and y positions of the asteroid
Kill the asteroid
Play the explode()
particle effect for asteroidParticles
Play boomSound
Refresh your HTML preview to verify that the enemy's laser will destroy an asteroid if hit.
You might have noticed that we did not add a collision between the enemy
and the asteroidGroup
. This was intentional, so that the player
has to either destroy or avoid the enemy
(rather than letting an asteroid randomly hit and destroy the enemy
). However, if you want, you could add this last type of collision to make the game slightly more realistic. It's your choice.
Congratulations, you've completed everything in Step 15! You should be feeling much more confident about your ability to use Phaser to code a video game.
Be sure that you completed Coding Steps 1-5 on the previous page.
IMPORTANT: Take the time to read each step, and try to understand how the code works.
Let's make the game respond if the spaceship collides with any of the asteroids.
Phaser's Arcade Physics system has two methods (functions) to detect when two game objects "touch" each other:
collide()
checks to see if the two objects are colliding and keeps them separated (so they don't overlap) — if one or both of the objects is moving, they can transfer momentum between each other (if you set a body.bounce
value for the objects, they'll bounce off each other)
overlap()
simply checks to see if the two objects are overlapping (but the objects will pass right through each other)
The two objects being checked can be individual objects and/or groups of objects, such as: Sprite vs. Sprite, Sprite vs. Group, Group vs. Group. For groups, each member of the group is checked individually in sequence.
Add this Phaser command in your update()
function (before the if-else statements for the player movement):
This command tells the Arcade Physics system to check for collisions between the player
sprite and any sprite in the asteroidGroup
.
Refresh your HTML preview to verify that the spaceship can collide with the asteroids (and it will change their velocity).
You probably noticed that the spaceship and asteroids act like they collide before they touch. This is because the player sprite and each asteroid sprite are actually rectangles that have transparent areas. The transparent areas of the sprites are colliding, so it seems like they collide too soon.
Luckily, Arcade Physics has two ways that we can change the collision area of a sprite's body
to more closely match its visual appearance:
body.setCircle()
allows you to change the sprite's collision area to be a circle of a specified radius (which you can offset from the top-left corner of the sprite)
body.setSize()
allows you to change the sprite's collision area to be a rectangle with a specified width and height (which you can offset from the top-left corner of the sprite)
Since the asteroids are basically circles, let's set their collision areas to be circles.
The asteroid sprite frames are 40 pixels in width and 40 pixels in height. If viewed in an image editor, you would see that the asteroid is about 30 pixels across in diameter with about 5 pixels of transparent area on all sides:
The pink circle drawn above represents the circular collision area that we want each asteroid to use (instead of the full image).
Add this Phaser command inside the for
loop that adds the asteroids to the asteroid group (after the code that sets the anchor for each asteroid):
The setCircle(15, 5, 5)
sets the body collision area to be a circle with a radius of 15 pixels (30 pixels in diameter) that is offset from the left edge of the frame by 5 pixels in the x-direction and offset from the top edge by 5 pixels in the y-direction. This offset will center the circle within the frame.
Let's do something similar with the spaceship, which has frames that are 64 pixels in width and 64 pixels in height. Visually, the spaceship is not a rectangle, and it's not a circle — it's more like a triangle. However, triangle is not an option for the body collision shape. Let's just use a circle because it's close enough (besides the ship rotates in a circular motion):
The pink circle drawn above represents the circular collision area that we'll use for the spaceship. It's not a perfect match, but it's better than the using the full image for collisions.
Add a Phaser command to set the body collision area of the player
sprite to be a circle with a radius of 20 pixels that is offset by 12 pixels from the left of the frame and 12 pixels from the top of the frame. Add the command in the create()
function (after the command that enables physics on the player
sprite).
Refresh your HTML preview to verify that the spaceship and asteroids are now visually closer when they collide with each other.
What should really happen in our game when the spaceship collides with an asteroid is that the spaceship should be damaged or destroyed and the asteroid should be destroyed.
Modify your existing collide()
statement in the update()
function, so it looks like this:
This command tells the game to run a custom function named collideAsteroid
when the physics system detects a collision between the player
sprite and any sprite in the asteroidGroup
. (null, this
are two additional parameters that you'll always include). The two specific sprites involved in the collision will be passed into the custom function as parameters (variables).
Now you have to actually create a custom function named collideAsteroid
, and add code inside to do something when this type of collision occurs.
Add this code to the very end of your code.js file (after the update()
function):
This creates the custom function, though it has no code yet inside its curly braces { }
.
Notice that we included player, asteroid
inside the parentheses after the function's name. These represent the parameters — variables that are passed into the custom function when it is called. The parameters represent the specific sprites involved in the collision, in the same order they are listed in the game.physics.arcade.collide()
function.
The parameter names act as local variables inside the custom function, so the names of the parameters do not have to exactly match the sprite names in the collide()
function. However, it will be much easier to understand what's happening in the custom function if the names are either identical or similar. Inside the custom function, whatever you do to the parameter variables will affect the sprites they represent.
Within the custom function collideAsteroid()
, the parameter player
represents the player
sprite (easy to understand because both use the same name). The parameter asteroid
represents the specific asteroid sprite from the asteroidGroup
that collided with the player
sprite. (We could use ship
as the parameter name to represent the player
sprite and rock
as the parameter name to represent the sprite from the asteroidGroup
— and the custom function would work exactly the same. However, the code might seem less clear to us.)
Alright, enough of that — let's destroy the asteroid involved in the collision.
Add this Phaser command inside the collideAsteroid()
function by pasting it between the curly braces { }
:
The kill()
command removes a sprite from the game. Phaser actually keeps the sprite object saved in memory, in case you decide to reuse the sprite later. Phaser has commands to revive()
or reset()
a "dead" sprite to add it back into the game.
Refresh your HTML preview to verify that each asteroid will disappear if it collides with the spaceship.
Now let's add a sound effect that will play one time whenever a collision occurs:
Use boomSound
as the name of the global variable for the sound.
Use assets/sounds/boom.wav for the sound file. Assign a unique asset key name to the sound, such as boom
.
Add boomSound
to the game with a volume of 0.3
.
Add a Phaser command inside the collideAsteroid()
function to play boomSound
.
Refresh your HTML preview to verify that the explosion sound plays whenever an asteroid collides with the spaceship.
Add a Phaser command inside the collideAsteroid()
function to kill the player
sprite.
Let's also add a visual effect as feedback to make the collision feel more realistic.
Add this Phaser command inside the collideAsteroid()
function:
This command will cause the game screen to "shake" briefly when a collision occurs:
The first number 0.02
represents the intensity of the camera shaking as a percentage of the camera size (0.02 = 2%).
The second number 250
represents the duration of the shaking in milliseconds (1000 milliseconds = 1 second).
Refresh your HTML preview to verify that the game display "shakes" and the spaceship disappears when it collides with an asteroid.
Let's add an explosion effect when the player
sprite is killed. The explosion will be an animated sprite that we'll hide from view until needed.
Use explosion
as the name of the global variable for the sprite.
Use assets/images/explosion.png for the spritesheet image. It contains 16 animation frames that are each 128 pixels in width and 128 pixels in height. Assign a unique asset key name to the spritesheet, such as explosion
.
In your create()
function (after the for
loop that added the asteroids), add the explosion
sprite to position 100, 100
of the game (this will just be a temporary position), and set the sprite's anchor to its center.
Add an animation to the explosion
sprite named explode
that will play all 16 frames in order (list an array from 0-15) at a frame rate of 30, with loop set to false
.
Refresh your HTML preview to verify that the first frame of the explosion sprite is visible in the upper left of the game screen.
We're actually going to hide the explosion
sprite until we need to display it (and then we'll move it to the player
sprite's position when it's killed).
Add this Phaser command to your create()
function (right after the command that added the animation for the explosion
sprite):
Refresh your HTML preview to verify that the explosion sprite is no longer visible.
When the player
sprite is killed, we will move the explosion
sprite to the same position where the player
was located, make the explosion
sprite visible, play the explode
animation, and then remove the explosion
sprite again.
Add this Phaser code in your create()
function (after the code that added the player
animation, but before the code that added the asteriodGroup
):
This code adds an event when the player
sprite is killed. This event will perform the code listed inside the function's curly braces { }
:
reset()
makes the explosion
sprite visible again and moves it to the x-y coordinates listed inside the parentheses. In this case, we want to make it appear at the player
sprite's position.
The command to play the animation includes an extra argument at the end:
explode
is the animation key name
30
is the frame rate
false
means do not loop the animation (play it only once)
true
means kill (remove) the sprite after the animation has ended
Refresh your HTML preview to verify that the explosion animation will appear (and then disappear after playing) when and where the spaceship collides with an asteroid.
Let's give your player
a way to fight back. It's time to add a weapon.
Phaser's Arcade Physics system includes a Weapon object with properties and methods related to firing bullets. The weapon is really just a group of bullet objects.
The "bullets" for a weapon can be any image (or spritesheet) that you want: bullets, lasers, arrows, fireballs, etc. If you want the weapon to fire smiley face emojis, you can do that.
Adding a weapon to the game involves 3 steps:
Declare a global variable for the weapon.
Load an image (or spritesheet) for the bullets into memory in the preload()
function.
Add the weapon to the game in the create()
function.
The weapon can then be fired in the update()
function.
Let's get ready to add your weapon:
Add a global variable named laser
for the weapon.
Preload assets/images/laser.png for the bullet image. Assign a unique asset key name to the image, such as bullet
.
Remember that the order in which visual objects are added in the create()
function determines which objects appear in front of (or behind) other objects. We want the weapon's bullets to look like they are being fired from beneath the spaceship, so we need to add the weapon before the player
sprite is added in the code.
Add this Phaser code in your create()
function (before the code that adds the player
sprite):
Let's examine this code:
The first line adds the weapon. 10
represents the number of bullets to create in the bullet group. The bullets will be recycled as they are fired, so this sets an upper limit on the maximum number that could be on-screen at any one time. 'bullet'
is the asset key name that it will use for each bullet.
The second line will kill (remove) a bullet if it leaves the game display boundaries. Phaser has multiple options for bulletKillType.
The third line sets the bullet speed in pixels per second.
The fourth line sets a limit on the firing rate. In this case, a bullet can only be fired every 250 milliseconds (even if the player holds down the fire key).
The last line changes the bullet's collision area to be a rectangle with a width of 24 pixels and height of 12 pixels offset 6 pixels from the left edge of the bullet image and offset 6 pixels from the top edge of the bullet image.
There are many other Phaser.Weapon properties that you could use in other games, if needed.
We want the laser
weapon to be associated with the player
sprite. Phaser has a method for the weapon to track (follow) another sprite, so the bullets will be fired from that sprite's position.
Add this Phaser command in your create()
function (after the code that added the player onKilled
event):
This command will ensure the laser
weapon tracks the position of the spaceship:
player
represents the name of the sprite that the weapon will track
0, 0
represents an x-y offset from the sprite's anchor position. Using 0, 0
means the weapon will fire from the player
sprite's center.
true
means that the weapon should also track the sprite's rotation. We need this for our game since the ship rotates, but other games may not need this (so they could list false
or nothing at all).
Now we just need a way to fire the laser
weapon. Remember that way back in Step 2, you assigned the spacebar as an input named fireKey
.
Add an if
statement in your update()
function (after the if-else statements for the player movement) to check when the fireKey
is being pressed.
Add this Phaser command to your code, so it will be performed when your new if
statement is true:
Refresh your HTML preview to verify that your spaceship fires the laser weapon when the spacebar is pressed.
You'll see that the laser "bullets" don't do anything yet — we'll have to add a collision between the laser bullets and the asteroids.
You'll also discover that the laser can still be fired even after the player
sprite has been killed.
Modify your existing if
statement for the fireKey
, so it will check whether the fireKey
is being pressed AND the player exists.
Remember that &&
is used to represent AND
Command to check if player exists (returns true or false): player.exists
Refresh your HTML preview to verify that the laser weapon can only be fired when the spaceship exists.
Let's add a sound effect that will play whenever the weapon is fired:
Use fireSound
as the name of the global variable for the sound.
Preload assets/sounds/fire.wav for the sound file. Assign a unique asset key name to the sound.
Add fireSound
to the game with a volume of 0.1
. Add the sound after the code that set the laser
to track the player
sprite.
Add this Phaser code inside your create()
function (after the code that added fireSound
):
This code will play the fireSound
every time the laser
weapon is fired. The reason for playing the sound using this code is the weapon has a fireRate
that limits how frequently it can fire. If we played the sound whenever the fireKey
is being pressed, the sound would play too frequently (and would play even when a new bullet is not actually fired). So the code above will keep the sound effect synced with the actual weapon firing.
Refresh your HTML preview to verify that the sound effect plays whenever the laser weapon is fired.
In your update()
function (directly after your existing collide()
function), add a new Arcade Physics collide()
function to check for collisions between the laser bullets and the asteroids:
Check for collisions between laser.bullets
and asteroidGroup
. List these two sprite groups in this order.
When a collision is detected, it should run a custom function called shootAsteroid
.
Add a new custom function named shootAsteroid()
(after your collideAsteroid()
function). Inside the parentheses ()
after the function name, list bullet
and asteroid
as parameters (in that order).
Inside the curly braces { }
of the shootAsteroid()
function, add Phaser commands to:
kill the asteroid
kill the bullet
play the boom sound effect
Refresh your HTML preview to verify that the asteroids are destroyed when they are hit by the laser.
We have an explosion animation that plays when the player
sprite is killed, so let's add a particle effect to show the asteroid breaking up into pieces when it's destroyed.
Phaser's Arcade Physics system includes a Particle Emitter object with properties and methods related to emitting particles. An emitter is just a group of particle objects (similar to how a weapon is just a group of bullet objects).
Particle emitters are useful for one-time events (such as explosions, etc.) or continuous effects (such as rain, snow, flames, etc.).
Adding a particle emitter to the game involves 3 steps:
Declare a global variable for the particle emitter.
Load an image (or spritesheet) for the particles into memory in the preload()
function.
Add the particle emitter to the game in the create()
function.
The particle emitter can then be activated in the create()
function or update()
function, depending on what you need for your game. The particle emitter can be a single point or can be an area of a specified width and height.
Let's get ready to add your particle emitter:
Add a global variable named asteroidParticles
for the particle emitter.
Preload assets/images/asteroid-particle.png for the particle image. Assign a unique asset key name to the image, such as particle
. This particle image is just a scaled down version (20 pixels by 20 pixels) of the first frame from the asteroid spritesheet:
Add this Phaser code in your create()
function (after the for
loop that added the asteroids, but before the code that adds the explosion
sprite):
Let's examine this code:
The first line adds the particle emitter to the game. 0, 0
represent the x and y coordinates where the particles will be emitted from. (Later, we'll change the x and y coordinates to match the x and y coordinates of the asteroid being killed.) 50
represents the maximum number of particles to create in the emitter group. We won't be using all 50 for a single particle explosion — we just want to have enough available in the game's memory, in case we need multiple particle explosions on-screen at the same time.
The second line adds the particle objects to the emitter group using the provided asset key name.
The third line sets the gravity
property of the particles to zero (since we don't want any gravity for our particular game). By default, particles will have a gravity value of 100, unless you change it.
The fourth line will make the particles fade out of view from an alpha of 1
(opaque) to 0
(transparent) over 1000
milliseconds (1 second).
When an asteroid collides with the spaceship or with a laser bullet, we want to create an "explosion" of particles at the asteroid's position.
Add this Phaser code in your collideAsteroid()
function (right before the asteroid.kill()
command):
Add the same code in your shootAsteroid()
function (right before the asteroid.kill()
command).
Let's examine this code:
The first two lines set the x and y position of the asteroidParticles
emitter to be the same as the x and y position of the asteroid involved in the collision.
The third line creates an explosion effect from the particle emitter. 1000
represents the lifespan of the particles in milliseconds (1000 ms = 1 second), and 5
represents the number of particles to emit.
The particles will be emitted in random directions with slightly different speeds, so the explode()
effect looks slightly different each time.
Remember in the create()
function that we set our particles to fade out over 1000 milliseconds. Since we also set our particles to have a lifespan of 1000 milliseconds during the explode()
effect, they will be removed as soon as they fade out.
If we didn't fade out the particles, they would disappear abruptly after 1000 milliseconds, which would look odd. The fading makes the particle explosion seem more natural.
Refresh your HTML preview to verify that a particle explosion occurs when the asteroids are destroyed.
Our game is not going to last very long if there are only 10 asteroids to shoot. As asteroids are destroyed, the game should periodically spawn (generate) new asteroids to replace them, so the game can keep going.
As part of our game's design, we'll keep it so there are never more than 10 asteroids at any one time. (Of course, we didn't have to design our game that way.)
Here are a few different options for when to spawn new asteroids:
Immediately spawn a new asteroid whenever an existing asteroid is destroyed
Use a timer to spawn a new asteroid after an existing asteroid is destroyed (such as: making new asteroid appear 5 seconds after one is destroyed)
Use a random number to decide when to spawn a new asteroid after an existing asteroid has been destroyed (i.e., timing of new asteroid will be random)
Let's go ahead and use a random number for our asteroid spawning. It will add some variation and unpredictability to the asteroid spawning.
Add this Phaser code inside your update()
function (after the code that makes the asteroids wrap around the game world):
There is some missing code that you will need to insert, but let's first explain how the provided code works:
Each time a loop of the update()
function occurs, the game will generate a random number (which will be a decimal value between 0 and 1). If the the random number is less than 0.02
, then it will check for a dead asteroid. This means that each loop of the update()
function has a 2% chance of checking for a dead asteroid. Even though 2% is a very low chance, the game loop runs many times per second, so it doesn't take that much time for a dead asteroid check to occur — however, the amount of time between each dead asteroid check will be random, which is what we wanted.
When the random number is less than 0.02
, the code will check for the first dead (killed) sprite in the asteroidGroup
and assign that dead sprite to a local variable called asteroid
. If there aren't any dead sprites at the moment, then asteroid
will be assigned a value of false
.
The next check is whether asteroid
is true
— meaning was there a dead sprite? If so, then it performs the rest of the code (which will reset the asteroid
sprite, so it is "alive" again).
Notice the tween
command at the end. A tween is a command that allows you to change one or more properties of an object over a specified period of time. Tweens are very useful for adding different types of automated effects in your game. In this case, the tween will make the new asteroid fade into view by changing it from transparent to opaque over 0.5 seconds:
The tween is added to the asteroid
sprite, and it is a to
tween.
Inside the curly braces { }
, it lists all the properties of the sprite that will be tweened. In this example, we're just going to change the alpha
property (which represents the sprite's transparency) from its current value (which we set to 0
in the previous line of code) to a new value of 1
(which is opaque).
500
represents the time period (in milliseconds) for the tween. So it will take 0.5 seconds for the alpha
property to change from its current value to its new value.
The change in the property will follow a pattern called Phaser.Easing.Cubic.Out
(which basically means it will change faster at first but then slow down — making this particular tween seem more "natural"). Phaser has different types of easing patterns that can be used for tweens.
true
tells Phaser to start the tween automatically. (Otherwise, you can use false
to set up a tween without running it, and then use a start()
command to run the tween.)
Now it's time for you to add the missing code inside the curly braces of the if (asteroid)
statement:
Add a Phaser command to reset()
the asteroid
at a random X-position and random Y-position in the game world. (Hint: Look back at Step 6 to see how you reset the explosion sprite, and look at Step 5 to see how you can pick a random X and Y position in the game world)
Add Phaser code to give the asteroid a random speed and direction. (Hint: How did you do this in Step 5?)
Because we are "recycling" a dead asteroid sprite from the asteroidGroup
, the "new" asteroid will still have the same properties as the original sprite — unless we change those properties.
So we did give the asteroid a new random starting position and a new random speed and direction.
However, notice that we did not have to add or change several other existing properties for the asteroid:
We didn't need to set the anchor position for the asteroid. It will still use its same anchor position that was set when it was first added to the asteroidGroup
in the create()
function.
We also didn't need to change the asteroid collision area to be a circle (because it was already set to a circle when it was first created).
We also didn't have to add animations to the asteroid because it still has them from before. The sprite will also continue to play the same animation (spin-clock
or spin-counter
) that it was already playing before.
Refresh your HTML preview to verify that "new" asteroids will randomly spawn and fade in after other asteroids are destroyed.
Now your game will keep recycling the same 10 sprites in the asteroidGroup
, so the gameplay can continue for as long as the player can stay alive.
Now that our game will keep spawning new asteroids as the player shoots them, let's have the game become more challenging as the player progresses.
Every time the player shoots an asteroid, let's increase the asteroid maxSpeed
by a small amount. This won't change the speed of any existing asteroids. However, it will affect the possible speed of new asteroids.
A higher maxSpeed
means each new asteroid has the potential to be slightly faster than the previous asteroids. Since the speed of each new asteroid is calculated as a random value (up to the maxSpeed
), some of the new asteroids will still be slow — but over time (as the player keeps shooting asteroids), some of the new asteroids will be much faster — increasing the challenge of the game in response to the player's skill. (Our game's attempt at promoting flow.)
In your shootAsteroid()
function, add a line of code to increase the value of maxSpeed
by 1
:
Refresh your HTML preview to verify that some of the new asteroids seem to be faster as the game progresses.
The change in the maximum possible speed is small, so it would require destroying at least 50 or more asteroids for it to start to become obvious that some of the new asteroids are faster — but don't worry, the asteroids will definitely get faster as the game progresses.
If you want to really speed things up, you could temporarily change the line of code to increase the maxSpeed
by 10 — and then test it out to verify that it does affect the speed of new asteroids.
Let's give the player some score points for every asteroid destroyed by a laser.
So we'll need a variable to keep track off the player's score, and we'll need to display the score as text in the game display.
As a reminder, adding text to the game involves 2 steps:
Declare a global variable for the text.
Add the text to the game in the create()
function.
Once the text has been added, you can use the variable's text
property to change what the text displays. This is typically done in your update()
function or in a custom function.
Here's what you need to do:
Declare a global variable called score
and assign it an initial value of 0
Declare a global variable called scoreText
In your create()
function (after the code that adds the keyboard inputs), add scoreText
as a text object at position 20, 20
. Set the text to display: 'Score: ' + score
(which will display the text in the quotes followed by the current value of the score
variable). Use the same font styling that you used in Practice 1 (Arial, 20px, bold, #ffffff)
You do not need to set the text's anchor or add a text shadow.
If you need hints, look back at Step 7 of Practice 1.
Refresh your HTML preview to verify that the score (with a value of zero) appears in the upper left of the game.
Now we need to increase the score every time the player shoots an asteroid.
Add Phaser commands in your shootAsteroid
function to do the following (in order):
Increase the value of score
by 250
Change the text
property of scoreText
by assigning it a value of: 'Score: ' + score
If you need hints, look back at Step 9 of Practice 1.
Refresh your HTML preview to verify that the score increases by 250 every time you shoot an asteroid.