P2: Steps 6-10
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.
Step 6: Add Player Collision with Asteroids
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 abody.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).
Change Collision Areas of Sprites
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.)
Kill Asteroid
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.
Add Collision Sound Effect
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 of0.3
.Add a Phaser command inside the
collideAsteroid()
function to playboomSound
.
Refresh your HTML preview to verify that the explosion sound plays whenever an asteroid collides with the spaceship.
Kill Player and Add Camera Shake
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.
Add Explosion When Player Killed
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 theexplosion
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 theplayer
sprite's position.The command to play the animation includes an extra argument at the end:
explode
is the animation key name30
is the frame ratefalse
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.
Step 7: Add Player Weapon
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 track0, 0
represents an x-y offset from the sprite's anchor position. Using0, 0
means the weapon will fire from theplayer
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 listfalse
or nothing at all).
Fire Weapon When Fire Key Pressed
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 ANDCommand 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.
Add Weapon Sound Effect
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 of0.1
. Add the sound after the code that set thelaser
to track theplayer
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.
Add Collision Between Weapon Bullets and Asteroids
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
andasteroidGroup
. 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.
Step 8: Add Particle Effect to Asteroids
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) to0
(transparent) over1000
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), and5
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.
Step 9: Add Asteroid Spawning
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 than0.02
, then it will check for a dead asteroid. This means that each loop of theupdate()
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 theasteroidGroup
and assign that dead sprite to a local variable calledasteroid
. If there aren't any dead sprites at the moment, thenasteroid
will be assigned a value offalse
.The next check is whether
asteroid
istrue
— meaning was there a dead sprite? If so, then it performs the rest of the code (which will reset theasteroid
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 ato
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 thealpha
property (which represents the sprite's transparency) from its current value (which we set to0
in the previous line of code) to a new value of1
(which is opaque).500
represents the time period (in milliseconds) for the tween. So it will take 0.5 seconds for thealpha
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 usefalse
to set up a tween without running it, and then use astart()
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()
theasteroid
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 thecreate()
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
orspin-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.
Increase Maximum Speed of Asteroids
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.
Step 10: Add Score
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 of0
Declare a global variable called
scoreText
In your
create()
function (after the code that adds the keyboard inputs), addscoreText
as a text object at position20, 20
. Set the text to display:'Score: ' + score
(which will display the text in the quotes followed by the current value of thescore
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
by250
Change the
text
property ofscoreText
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.
Steps 11-15 continue on next page
Last updated