P2: Steps 11-15
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.
Step 11: Add Player Shields (Health Bar)
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 use100
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 use100
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 Player Health and Damage
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.
Add Health Bar for Shields
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 addedscoreText
), addhealthText
as a text object at position210, 20
. Set the text to display:'Shields'
. Use the same font styling that you used forscoreText
(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).
Scale the Health Bar
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.
Step 12: Add Player Lives
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.
Add Lives Bar
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 addedhealthBar
), addlivesText
as a text object at position590, 20
. Set the text to display:'Ships'
. Use the same font styling that you used forscoreText
andhealthText
(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 thelivesBar
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.
Crop the Lives Bar
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 of3
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, and25
is the height.The second line applies the crop for the
livesBar
image using thelivesCrop
rectangle.
Refresh your HTML preview to verify that the only 3 spaceship icons appear at the top of the game.
Subtract Life When Player Killed
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
by1
Change the width of
livesCrop
by calculating its new value:livesBar.width = shipLives * 25;
Apply the crop for
livesBar
using thelivesCrop
rectangle
Refresh your HTML preview to verify that only 2 spaceship icons will be shown after the spaceship is destroyed.
Spawn New Spaceship If Lives Remain
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 zerobrings the
player
sprite back to life with maximum health using therevive()
method (alternatively, we could have used thereset()
method)fades the
player
sprite into view using ato
tween on the sprite'salpha
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.
Add Teleport Sound Effect
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 of0.5
.Add a Phaser command to play
teleportSound
inside yourplayer.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.
Step 13: Add Extra Life Reward
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 of5
Add a global variable named
newLife
and assign it an initial value of10000
Add a global variable named
lifeSound
for the sound effectPreload assets/sounds/extra-life.wav and assign it a unique asset key name, such as
life
Add
lifeSound
to the game with a volume of0.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 thanmaxLives
. 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 itsmaxHealth
. If that's true, we'll set the health back to its maximum.In either case, we need to increase
newLife
by another10000
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
by1
Change the width of
livesCrop
by calculating its new value based onshipLives
Set the crop for
livesBar
using thelivesCrop
rectangleUpdate 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 toplayer.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.
Step 14: Add Game Start and Game Over
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.
Add Game Title
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 thelivesBar
), add the title image 100 pixels above the game's center, and assign the image to thegameTitle
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.
Add Start Instruction
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 thegameTitle
), addstartText
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.)
Hide Spaceship Until Game Started by Player
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.
Add Signal and Custom Function to Start Game
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 (the250
aftertrue
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 itsexists
property totrue
)Add a
to
tween to make theplayer
sprite fade into view by changing its alpha value to1
over 2 seconds. Use thePhaser.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).
Show Game Over Text When Player is Out of Lives
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 thestartText
), addgameOverText
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 tofalse
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
visibleSet the scale for
gameOverText
to be3
for both its width and heightAdd a
to
tween to makegameOverText
fade into view by changing its alpha value to1
over 1 second. Use thePhaser.Easing.Cubic.Out
pattern, and set the tween to start automatically (with no delay).Add a
to
tween to makegameOverText.scale
zoom in by changing its value to1
(for both directions) over 1 second. Use thePhaser.Easing.Cubic.Out
pattern, and set the tween to start automatically (with no delay).Add a
to
tween to makestartText
fade into view by changing its alpha value to1
over 0.5 seconds. Use thePhaser.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).
Add Signal and Custom Function to Restart Game
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 to0
Change the value of
shipLives
back to3
Change the value of
newLife
back to10000
Change the value of the asteroids'
maxSpeed
back to100
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).
Step 15: Add Enemy Ship (Extra Credit)
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 Enemy
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)
Preload Assets for Enemy
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).
Add Enemy
In your create()
function (after the code that added the player
sprite), add code for the enemy
sprite:
Add the
enemy
sprite at position0, 0
(which is a temporary position) using theenemy-ship
assetSet 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 tofalse
Add Enemy Laser
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 theenemy-bullet
assetSet the bullets to be killed automatically if they leave the camera boundaries
Set the bullet speed to
400
pixels per secondSet 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 theenemy
sprite with no offset but don't track the sprite's rotation (set tofalse
)
Add Enemy Explosion
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 position0, 0
(which is a temporary position) using theexplosion
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 tofalse
Add Enemy Fire Sound
In your create()
function, add code for enemyFireSound
:
Add
enemyFireSound
to the game with a volume of0.5
After the code that adds the
enemyLaser
, add anenemyLaser.onFire
function containing a command to play the sound.
Add Function to Spawn Enemy
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.
Add Enemy Alarm Sound
In your
create()
function, addenemyAlarmSound
to the game with a volume of0.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 theenemy
), add anenemy.events.onKilled
function containing a command to stop the sound.
Add Enemy AI to Rotate and Fire
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.
Add Timer to Spawn Enemy
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.
Add Collision for Player and Enemy
In your
update()
function, add acollide()
command forplayer
andenemy
(list in this order) that will call a custom function namedcollideEnemy
.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 theenemy
Play the
enemyExplosion
animationPlay
boomSound
Shake the game camera
Damage the
player
by25
health pointsScale 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.
Add Collision for Player's Bullets and Enemy
In your
update()
function, add acollide()
command forlaser.bullets
andenemy
(list in this order) that will call a custom function namedshootEnemy
.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 theenemy
Play the
enemyExplosion
animationPlay
boomSound
Add
2500
to the player'sscore
Update
scoreText
to display the newscore
valueCall the
checkNewLife()
function (NOTE: This is not needed if a call tocheckNewLife()
is already listed in yourupdate()
function)
Refresh your HTML preview to verify that the player's laser can destroy the enemy spaceship, awarding the player 2500 score points.
Add Overlap for Player and Enemy's Bullets
In your
update()
function, add anoverlap()
command forplayer
andenemyLaser.bullets
(list in this order) that will call a custom function namedshootPlayer
. As a reminder,overlap()
is similar tocollide()
exceptoverlap()
doesn't transfer momentum between the two objects. (If we used acollide()
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
by25
health pointsScale the player's
healthBar
Refresh your HTML preview to verify that the enemy's laser can damage the player's spaceship.
Add Collision for Enemy's Bullets and Asteroids
In your
update()
function, add acollide()
command forenemyLaser.bullets
andasteroidGroup
that will call a custom function namedremoveAsteroid
.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 theasteroid
Kill the
asteroid
Play the
explode()
particle effect forasteroidParticles
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.
Last updated