Be sure that you completed Coding Steps 6-10 on the previous page.
IMPORTANT: Take the time to read each step.
Add at least one new obstacle that hinders the player in some way. For example, add a spike, new enemy, etc. that the player must avoid.
The obstacle could be an individual object or group of objects.
Be sure to add the obstacle(s) at the desired location(s) in the game. Also be sure to set whatever properties are necessary for the obstacle(s).
Be sure to add the necessary collide()
or overlap()
method to detect when the player has encountered the obstacle. You’ll also need to create a custom function to perform whatever actions should occur when the obstacle is encountered.
Add at least one new resource that helps the player’s character in some way. For example, add a diamond or health pack that the player can collect, a weapon that the player can use, allies that help the player, etc.
The resource could be an individual object or group of objects.
Be sure to add the resource(s) at the desired location(s) in the game, and set whatever properties are necessary for the resource(s).
Be sure to add the necessary collide()
or overlap()
method for the resource. You’ll also need to create a custom function to perform whatever actions should occur when the resource is collected or used.
Also be sure to add more coins throughout the rest of the level.
You'll need to add the x-y position of each new coin as additional JSON data within the coinData
array. Refer back to Step 5.
Add at least two new sound effects to the game.
(1) Add code to play a sound effect when the player collects a coin.
Use coinSound
as the variable name for the audio
Use a copy of the coin.wav audio file from your Practice 1 game.
(2) Add code to play a sound effect when the player collects a power-up.
Use powerUpSound
as the variable name for the audio
Use a copy of the power-up.wav audio file from your Practice 1 game.
Be sure to place the audio files into the sounds subfolder of your game's assets folder.
Optional: Add code to play a sound effect when the player jumps. You can use an open-source audio file, or create your own sound effect.
Optional: Add music or other sound effects to the game.
Freesound is a website that allows people to share open-source audio files. You can search for and download free-to-use audio files.
You will need to create a free account in order to download files.
You want the audio files to be either MP3 or WAV files. (If absolutely necessary, you can use Zamzar to convert the files to the correct format.)
You should check the length (time) of the sound — so it's not too short or too long.
You should check the size of the file — so it's not too large. Try to use files that are less than 1 MB (ideally less than 100 KB). Large sound files can slow down your game.
ChipTone is a web app that allows you to create and download your own sound effects as WAV files.
ChipTone has lots of features, but the best way to learn how to use it is to simply play around with different settings:
You can change the sound type (such as: coin, zap, boom, etc.).
You can change the wave form. Clicking a second time on the same wave form will reverse its shape.
You can select a different note (tone) on the keyboard.
You can turn various effects (vibrato, harmony, etc.) on or off. Each effect has its own settings that can be adjusted. You can combine effects.
Once you have a sound that you want to use, click the "Save .WAV" button in the lower-right to download the sound file.
NOTE: The ChipTone website requires the Adobe Flash plug-in. Google Chrome has Flash built-in, but you might need to grant access for ChipTone to use Flash. If ChipTone doesn't load:
Open the Settings for Chrome (click 3-dot icon at upper-right, and select Settings).
At the bottom of the Settings, click Advanced.
In the Privacy and Security section, click Content Settings. Then click Flash.
Be sure "Allow sites to run Flash" is toggled on (to the right).
Add sfbgames.com to your list of allowed websites for Flash.
OPTIONAL: Create a new animated sprite for an object in the game — or modify an existing animated sprite (player, cat, etc.) — or convert a non-animated image (diamond, etc.) into an animated sprite.
The new (or modified) sprite should have at least 4 animation frames. For example, you could have 2 animation sequences (such as “left” and “right”) with at least 2 frames each — or you could have 1 animation sequence with at least 4 frames. (Of course, you can have more than 4 frames total.)
Keep in mind that all the animation frames for a sprite must have the same width and same height. (For example, each of the frames in the existing player
spritesheet are 32 pixels in width and 48 pixels in height.) You decide which size to use, but each frame will have to use that same size.
Be aware that it can take a fair amount of time to create or modify animation frames, since you are typically drawing or editing them manually pixel-by-pixel.
Be sure to place the spritesheet into the images subfolder of your game's assets folder.
Be sure to modify your game's code as needed to use the new spritesheet and play the animations.
Piskel is a web app that allows you to create, preview, save, and download your own animated sprites. You can also import existing images or sprites, and modify them.
You will need to create a free account in order to save your sprites. The easiest way to do so is to sign in with your Google account.
Be sure to sign in to your Piskel account before creating a sprite, so you don't lose your work.
Be sure to periodically save your sprite as you work. (It does not auto-save.)
Sketch out each of the new or modified frames for your animation sequences.
Include enough frames so that the animation will be recognizable — but try to limit how many unique frames you need to create.
For example, a simple walking animation could be created using just 2 frames (though you may want more frames to make the animation smoother and more realistic).
Decide on the best size (width and height) to use for all the frames.
Use the size of other objects in the game as a guide, so your new sprite isn't too large or too small compared to these objects.
Make the frames large enough to fit the maximum width and maximum height needed for your animation sequence. Remember that all frames will end up using this same size.
Try to minimize the amount of unused transparent area in your frames.
It may help to first draw each frame to scale using graph paper and colored pencils.
Create the digital frames in Piskel using your drawings as a reference.
Piskel has a set of basic drawing and editing tools.
You can start with a blank frame — or you can import an existing image or spritesheet, and then modify it.
Click Resize tool (at right) to change the width and height of your frames.
You can add, delete, duplicate, and reorder frames.
You can use the Duplication tool to make copies of existing frames, and you can use the Transform tool to flip frames horizontally (creating mirror images).
For example, if you’ve created the first frame in the “left” animation, you can duplicate this frame, and then modify the copy to create the next frame in the “left” animation.
For example, if you’ve created all the frames for the “left” animation, you can duplicate each of these frames, and then flip them horizontally to instantly create all the necessary frames for the “right” animation.
Piskel shows a preview of your animated sprite in the upper right. You can change the speed (frames per second) to see which looks the best. The preview plays all the frames (in order) — unfortunately, there isn't a way to select a subset representing a single animation sequence.
Be sure to periodically save your sprite as you work.
Download your spritesheet by clicking Export. Then select tab for PNG, change the spritesheet layout so the number of columns equals the number of frames, and finally click Download.
Ask another person to playtest your game, in order to gather feedback on the gameplay experience. What works well? What could be improved? If time allows, try to incorporate minor changes that will improve the game.
For example, here's something important to consider: How do you know when the player has completed the level? In other words, what's the objective of the game?
Does completing the level mean reaching a specific location or object in the game?
Does it mean collecting all the coins or other objects?
Does it mean defeating all the enemies or a specific enemy?
Is it some combination of these — or something else entirely?
How could you modify the game design so the player clearly understands the objective?
How would you modify the game code so it can detect when the objective has been completed?
Congratulations, you've completed your third practice game! Of course, there are many other game features and types of games that can be created using Phaser. Now it's time for your team to start designing its own game.
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 to represent the player's character:
Declare a global variable named player
for the player's sprite.
In your preload()
function, load assets/images/dude.png as the spritesheet. It contains 9 animation frames that are each 32 pixels in width and 48 pixels in height. Assign a unique asset key name to the spritesheet, such as: 'dude'
In your create()
function, add the player
sprite to the game at position 25, 300
using the 'dude'
asset
Then set the sprite's anchor to its center: 0.5, 0.5
For help with this code to add a sprite, you can refer back to Step 4 of Practice 1 (or look at your game code for Practice 1 or Practice 2).
Refresh your HTML preview to verify that the player sprite is centered vertically near the left edge of the game. The sprite should display just the first frame of the spritesheet.
Now let's add some physics properties to the player
sprite.
As a reminder, we first need to start a Phaser Physics system (Arcade, Ninja, or P2) for the game. Then we need to enable physics on each object in the game that we want to be affected by physics (e.g., anything that will move or will be involved in collisions).
As the first line of code in your create()
function, start the Arcade Physics system for your game:
After your code that adds the player
sprite, enable Arcade Physics for the sprite:
Now we can change the physics properties for the sprite's body.
In this game, we want to simulate gravity by having the player
sprite fall downwards (if it isn't standing on something).
The Phaser Arcade Physics system actually lets you set a gravity
value for the x
direction (left-right) and/or the y
direction (up-down) of a sprite's body
:
Setting body.gravity.x
to a positive value will cause the sprite to be pulled to the right.
Setting body.gravity.x
to a negative value will cause the sprite to be pulled to the left.
Setting body.gravity.y
to a positive value will cause the sprite to be pulled downwards.
Setting body.gravity.y
to a negative value will cause the sprite to be pulled upwards.
After the command that enabled physics on the player
sprite, add this Phaser command to set a gravity value for the sprite:
This command will set the player's gravity to 450 pixels per second squared in the downward direction.
Refresh your HTML preview to verify that the player sprite will be pulled downward by gravity.
You'll see that the gravity will keep pulling the sprite downward until it disappears out of the game. That's because there isn't anything for the sprite to collide with, in order to stop it from falling.
Phaser has a property to make a sprite's body collide with the game world boundaries. We can use this property to keep the player
sprite from leaving the game world.
Add this Phaser command to make the player
sprite collide with the game world boundaries:
Refresh your HTML preview to verify that the player sprite will stop moving downwards when it collides with the bottom of the game world.
Experiment with different values for the gravity to see how it affects the sprite. Try a larger value. Try a smaller value. Try a negative value. Try changing the gravity to act in the x-direction. Try setting gravity values for both the x-direction and the y-direction. When you're done, be sure to set the gravity back to the the original value shown in the code above.
Next let's make the player
sprite bounce a bit when it collides with something after falling.
The Phaser Arcade Physics system lets you set a bounce
value for the x
direction (left-right) and/or the y
direction (up-down) of a sprite's body
.
The body.bounce.x
and body.bounce.y
properties are typically set to a decimal value between 0-1, which determines the rebound velocity of the sprite when it reverses direction after the bounce. Here are some examples:
Using a bounce
value of 1
will cause the sprite to reverse direction without losing any velocity (speed).
Using a bounce
value of 0.8
will cause the sprite to reverse direction and rebound at only 80% of its original velocity. In other words, each bounce will reduce its velocity by 20%.
Using a bounce
value of 0.5
will cause the sprite to reverse direction and rebound at only 50% of its original velocity. Therefore, each bounce will reduce its velocity by 50%.
Using a bounce
value of 0.25
will cause the sprite to reverse direction and rebound at only 25% of its original velocity. Therefore, each bounce will reduce its velocity by 75%.
If you wanted, you can use a bounce
value greater than 1, which will actually cause the sprite to rebound with a greater velocity with every bounce (sort of like a person bouncing on a trampoline).
Add this Phaser command to give the player
sprite a small amount of bounce
in the y
direction:
Refresh your HTML preview to verify that the player sprite will bounce slightly when it collides with the bottom of the game world.
Experiment with different values for the bounce to see how it affects the sprite. Try a value of 1. Try a value of 0.5. Try a value of 1.1 to see what happens. When you're done, be sure to set the bounce back to the the original value shown in the code above.
This game will use the keyboard arrow keys as the inputs for the player
sprite movement:
Pressing the left arrow key will move the sprite to the left.
Pressing the right arrow key will move the sprite to the right.
Pressing the up arrow key will make the sprite jump up.
Pressing the down arrow key won't do anything (unless you decide later to add an action for this)
Add code to make the arrow keys into inputs:
Declare a global variable named arrowKey
for the arrow key inputs.
In your create()
function, add the Phaser command make the cursor keys (arrows) into inputs assigned to the arrowKey
variable.
For help with this code to add the cursor keys (arrows) as inputs, you can refer back to Step 2 of Practice 2 (or look at your Practice 2 game code).
For this game, we're going to use the velocity
property to move the player
sprite left or right (and to jump up).
The Phaser Arcade Physics system lets you set a velocity
value for the x
direction (left-right) and/or the y
direction (up-down) of a sprite's body
:
Setting body.velocity.x
to a positive value will make the sprite move to the right.
Setting body.velocity.x
to a negative value will make the sprite move to the left.
Setting body.velocity.y
to a positive value will make the sprite move down.
Setting body.velocity.y
to a negative value will make the sprite move up.
As we typically do in a game, we'll use a series of if-else conditional statements to check the player inputs, in order to determine which direction to move the sprite.
Logically, there should be three possibilities for moving the player
sprite right or left: (1) the right arrow key is being pressed, (2) the left arrow key is being pressed, or (3) neither the right nor the left arrow key is being pressed.
Of course, in reality, the player might happen to press both the left and right arrow keys at the same time. To handle this situation, we need to decide whether our code should cause the player sprite to stop moving (make the two keys cancel each other) — or whether the code should prioritize one of the keys over the other.
In this game, the player
will start at the far left of the game world and will need to move to through the game world to the far right end. Most of the time the player will be trying to move to the right. However, it's also common for a player to accidentally press more than one key at a time.
So for both of these reasons, it would probably be best in this game to prioritize the right arrow key over the left arrow key, in the event that both are pressed at the same time. An easy way to prioritize the right arrow key is to check it first before checking the left arrow key.
In your update()
function, add if-else conditional statements with Phaser commands to move the player
sprite left or right based on the arrowKey
inputs:
If the right arrow key is pressed down, set the sprite's body.velocity.x
to 200
, like this:
Else if the left arrow key is pressed down, set the sprite's body.velocity.x
to -200
Else set the sprite's body.velocity.x
to 0
For help with this code, you can refer back to Step 3 of Practice 2 (or look at your Practice 2 game code). Just keep in mind that this game will use body.velocity.x
instead of body.angularVelocity
to move the player left and right.
Refresh your HTML preview to verify that you can use the left and right arrow keys to move the player sprite left or right. When neither key is pressed, the sprite should stop.
Experiment with different values for the velocity to see how it affects the speed of the sprite. Try a larger value. Try a smaller value. When you're done, be sure to set the velocity back to the the original value shown in the code above.
You'll notice that the player
sprite still just displays the first frame of its spritesheet (which shows the character facing to the left). That's because we haven't added any animations yet, but we'll do that in just a bit.
In your update()
function, add Phaser commands to make the player
sprite jump:
If the up arrow key was just pressed down, set the sprite's body.velocity.y
to -300
Hint: Refer back to Step 5 of Practice 1 for the difference between a key is being pressed down versus was just pressed down.
Notice that we did not include an else statement to set body.velocity.y
to zero when the up arrow key isn't pressed. The reason is that the body.gravity.y
will automatically slow down the player
sprite's upward velocity and eventually make the sprite fall downwards, just how gravity actually works in the real world when you jump up.
Refresh your HTML preview to verify that you can use the up arrow key to make the player sprite jump up. The player's gravity should make the sprite fall back down again.
You probably discovered that you can press the up arrow key multiple times to keep jumping, even when the sprite is in mid-air. Obviously, this is not realistic for jumping (though it would be realistic for a sprite that was supposed to be flying or swimming). We'll fix this later when we get to Step 4.
Experiment with different values for the velocity to see how it affects the speed and height of the jump. Try a larger value. Try a smaller value. Also try changing the gravity value to see how it affects the jump. When you're done, be sure to set the velocity and gravity back to the the original values shown in the code above.
In your create()
function (after the code that adds the player
sprite), add code to create two animations for the player
sprite:
Add an animation named 'left'
to play frames 0-3 (in order) at 10 frames per second in a loop.
Add an animation named 'right'
to play frames 5-8 (in order) at 10 frames per second in a loop.
Inside your if-else statements in your update()
function, add Phaser commands to do the following:
When the player
sprite is moving to the right, play the 'right'
animation.
When the player
sprite is moving to the left, play the 'left'
animation.
When the player
sprite is not moving, stop the animations, and set the sprite to display frame 4
(which shows the character facing us).
For help with this code, you can refer back to Step 3 of Practice 2 (or look at your Practice 2 game code).
Refresh your HTML preview to verify that the correct animation plays when you move the player sprite left or right. When the sprite is not moving, the sprite should face you (which is frame 4).
Some games are single-screen games, where the game display represents the entire game world. Practice 1 (Emoji Match) and Practice 2 (Asteroids 2084) were both like this. However, for Practice 3, your game world will extend beyond the size of the game display.
The size of the game display is set when you create your Phaser.Game object (named game
).
The beginning of your code.js file already contains this statement (so don't copy and paste this a second time):
The numbers 800, 600
set the width and height (in order) of the game display. This makes your game display 800 pixels in width by 600 pixels in height. This represents the size of your Game.Stage and your Game.Camera.
First, you're going to make your game display a little bit wider:
Modify your existing Phaser.Game object statement to make your game display 1000
pixels in width by 600
pixels in height.
If you also used the starter CSS code which applies a width to #my-game
, then modify your CSS so this width is also set to 1000
pixels.
Refresh your HTML preview to verify that your game display is now slightly wider. (It may be necessary to increase the width of your code editor preview pane, in order to see the whole game display.)
Next, you're going to make your game world wider than the game display itself.
Phaser allows you to make the width and/or height of your Game.World larger than the game display. Phaser can also automatically scroll the game display as the player moves through the game world.
At the beginning of your create()
function (after the command that started the Arcade Physics system), add this Phaser command:
As you can see, this Phaser command resizes the game world:
The first two numbers 0, 0
represent (in order) the x
and y
position of the top-left corner of your game world. (Typically, you'll use 0, 0
for your top-left corner.)
The last two numbers 5000, 600
represent (in order) the width and height (in pixels) of your game world. This can be set to any values that you want. Typically, the width and height are each set to be greater than or equal to the width and height of the game display.
Refresh your HTML preview to verify that the player sprite can move to the right out of the game display (traveling off into the rest of the game world) and can then move left back into the game display again.
Normally, when the game world is larger than the game display, we'll want the game display to automatically scroll to follow the player.
In your create()
function (after the code that adds the player
sprite), add this Phaser command:
To help convince you that the game world is actually larger, add this temporary code at the end of your create()
function:
Refresh your HTML preview to verify that the player sprite can move back and forth throughout the entire game world (5000 pixels in width) and the game display camera will automatically follow the player.
You're going to add and layer several images to form the background for your game. In the images subfolder inside your assets folder, take a look at these 3 images:
sky-clouds.jpg is an image of a blue sky with some clouds
mountain-skyline.png is an image of a green mountain range. Note that the "sky" portion of this image is actually transparent (so it will show whatever color or images are behind it). The image preview might show the transparent area as a checkerboard pattern.
city-skyline.png is an image of tall city buildings in silhouette. Note that the "sky" portion of this image is also transparent.
All three images are exactly 1000 pixels in width and 600 pixels in height, which is also the exact size of your game display. All three images have also been designed for "seamless" scrolling (their left and right edges match up perfectly).
Near the top of your code.js file, add global variables for the following:
sky
mountains
city
Hint: You can add all 3 variables at once with one line of code, like this:
In your preload()
function, add Phaser commands to:
Load assets/images/sky-clouds.jpg as an image, and assign it a unique asset key name, such as 'sky'
Load assets/images/mountain-skyline.png as an image, and assign it a unique asset key name, such as 'mountains'
Load assets/images/city-skyline.png as an image, and assign it a unique asset key name, such as 'city'
In your create()
function (after the command that set the game world boundaries), add a Phaser command to:
Add a tilesprite assigned to the sky
variable that will be positioned at 0, 0
, set to a width and height of 1000, 600
, and use the 'sky'
image asset.
For help with this code to add a tilesprite, you can refer back to Step 4 of Practice 2 (or look at your game code for Practice 2).
Refresh your HTML preview to verify that sky and clouds image appears in the game. If you move the player sprite to the right, you will see that the sky tilesprite only covers the first 1000 pixels of the game's width. (We'll take care of this in a little bit.)
Next you'll add the mountains image in front of the sky image.
As a reminder, when 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.
Whichever visual object is added first in the create()
function 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.
In your create()
function (after the command that added the sky
tilesprite), add a Phaser command to:
Add a tilesprite assigned to the mountains
variable that will be positioned at 0, 0
, set to a width and height of 1000, 600
, and use the 'mountains'
image asset.
Refresh your HTML preview to verify that the mountains image appears in front of the sky and clouds (which are partially visible due to the transparent "sky" area in the mountains image).
Now you'll add the city image in front of the mountains image.
In your create()
function (after the command that added the mountains
tilesprite), add a Phaser command to:
Add a tilesprite assigned to the city
variable that will be positioned at 0, 0
, set to a width and height of 1000, 600
, and use the 'city'
image asset.
Refresh your HTML preview to verify that the city image appears in front of mountains. Both the mountains and the sky/clouds are partially visible due to the transparent "sky" area in the city image).
To cover the entire background of the game world, there are a few options:
We could add the same images several more times at new positions spaced out 1000 pixels apart in the x-direction (so that we cover all 5000 pixels of the game world width).
We could add different images at positions spaced out across the game world width. This would be useful if we want to have the player travel through different backgrounds (such as a city, then a forest, then a desert, then a night-time scene, etc.).
We could make the images stay in place by "fixing" them to the game camera, so the images stay in view even as the player moves around the game world.
For this game, we're going to use the third option.
In your create()
function (after the commands that added the tilesprites), add this Phaser code:
Refresh your HTML preview to verify that the sky image stays fixed in place as the player moves through the game world. However, the mountains and city images won't stay fixed (yet).
Add similar Phaser commands to make the mountains
and city
stay fixed to the camera.
Refresh your HTML preview to verify that the sky, mountains, and city all stay fixed in place as the player moves through the game world.
Now our game has a background that covers the entire game world (by staying fixed in place).
The only problem is that a completely static, non-moving background doesn't feel realistic. In the real world, objects in the background appear to shift slowly as we move. Currently, our background doesn't budge at all.
As you remember from Practice 2, tilesprites are designed to be used for scrolling, so we can add code to do that. However, even that still might not feel entirely realistic.
In the real world, we experience a visual phenomenon called parallax — more distant objects seem to shift or move more slowly compared to closer objects. So if we have clouds in the distant background, then mountains a bit closer, and a city skyline even closer, they should each appear to shift or move at different rates as we move by them.
We can simulate parallax in our game code by scrolling the tilesprites at different rates. The sky
tilesprite should move more slowly because it is supposed to be the "farthest" away. The city
tilesprite should move more quickly because it is supposed to be the "closest" to the player.
Furthermore, we need to scroll the tilesprite positions in the opposite direction of the player's motion. So if the player sprite is moving to the right, the background tilesprites should appear to scroll to the left.
In your update()
function (after the if-else statements for the player movement), add this Phaser code:
This code will scroll the positions of the tilesprites relative to the game camera position (which is following the player). The negative numbers cause the tilesprite to scroll in the opposite direction of the player's movement. A smaller number will cause a tilesprite to scroll at a slower rate than a larger number, so the tilesprites will each scroll at different rates, with the sky
scrolling more slowly and the city
scrolling more quickly.
Refresh your HTML preview to verify that the sky, mountains, and city scroll at different rates as the player moves through the game world, simulating a parallax effect, which will seem more realistic.
Experiment with different values for the tilesprite scrolling to see how it affects the speed and realism of the parallax scrolling. Try larger values. Try smaller values. Try using the same value for all three tilesprites (which will seem less realistic because it won't have any parallax). When you're done, be sure to set the scrolling back to the the original values shown in the code above.
The game world will have a series of floating platforms that the player sprite can run and jump on to move through the level and collect various resources.
The platforms will simply be dark green rectangles (though you could replace them with another image). Inside the images subfolder of your assets folder are several platform images. They are all rectangles 25 pixels in height, but they range in width from 50 pixels up to 500 pixels.
You'll add a Phaser group to your game that will contain all the platforms. Remember that a Phaser group is simply a set of game objects with similar properties, such as a set of enemy sprites, etc. You'll be adding several different groups to your game.
Declare a global variable named:
platformGroup
(will contain all the platform objects)
In your preload()
function, load these images for the platforms:
Load assets/images/platform-050w.png with 'platform-50'
as its asset key
Load assets/images/platform-100w.png with 'platform-100'
as its asset key
Load assets/images/platform-200w.png with 'platform-200'
as its asset key
Load assets/images/platform-300w.png with 'platform-300'
as its asset key
Load assets/images/platform-400w.png with 'platform-400'
as its asset key
Load assets/images/platform-500w.png with 'platform-500'
as its asset key
Now you're ready to add the platformGroup
to the game. Remember that visual objects in the game world are layered in the order that they are added within the create()
function. So you'll want to add the platformGroup
after the background tilesprites but before the player sprite.
Add this Phaser code in your create()
function (after the code that adds the background tilesprites, but before the code that adds the player sprite):
This code does several things:
The first line of code adds a new group assigned to the platformGroup
variable.
The second line of code enables physics for every member in the plaftformGroup
(this can be done in advance, even before you add any members to the group)
The third line of code creates a member in the platformGroup
at x-y position 0, 575
(0
is the far left x-position, and 575
is 25 pixels up from the bottom y-position) using the 'platform-500'
asset (which is a rectangle image 500 pixels in width and 25 pixels in height). This member of the group is assigned to a local variable called ground
. Remember that by default, the top-left corner of an object represents its position (unless you change the objects's anchor to a different position, such as the object's center).
The fourth line of code scales the ground
object to fit across the entire bottom of the game world. The image for ground
will be scaled to 10
times its width and 1
times its height, so the image will become 5000 pixels wide, while keeping its same height of 25 pixels.
Refresh your HTML preview to verify that a platform appears across the bottom of the game world.
You will notice that the player
sprite doesn't stand on top of the platform — that's because we haven't yet instructed the game to make the player
collide with the platformGroup
. We'll do that in just a bit after we add some more platforms.
Add this code to create more objects in the platformGroup
:
This code creates 9 new members in the platformGroup
. The numbers represent the x and y positions (in order) of the top-left corner of the object. The members use different image assets (depending on how wide we want each platform to be).
Notice that for this code, we did not use a local variable (such as ground
) to create each platform. However, if we had needed to modify specific properties of a platform (such its scale, etc.), then we would have needed to assign the platform to a variable, so we could then use the variable name to change the properties of the specific platform object.
Refresh your HTML preview to verify that the other platforms appear in the game. (The player still doesn't interact with the platforms in any way.)
The beginning of your game world should look like this (plus you'll have two other small platforms off-screen to the right):
Now let's make the player collide with the platforms. This will allow the player to stand, run, and jump on the platforms.
Add this Phaser command in your update()
function (before the if-else statements that check for keyboard input by the player):
Refresh your HTML preview to verify that the player sprite now collides with the platforms. However, you will notice that the platforms do not stay in place.
When the player sprite collides with the platforms, the player pushes and moves the platforms (which can even cause them to leave the game world).
For some games, a platform that "collapses" or that can be "pushed" might be exactly what you want. However, for this game, we want all the platforms to be immovable.
Add this Phaser command in your create()
function (after the code that created the platforms):
The setAll()
methods allows you to change the value of a property for every member in a group. Inside the parentheses, you first list the name of the property (in quotes) and then list the value that you want to set for the property. In this case, the property body.immovable
for each group member will be set to the value of true
.
Refresh your HTML preview to verify that the platforms are now immovable and stay in place when the player collides with them, allowing the player to jump onto the platforms.
Next, let's fix the issue with the player's jump: we don't want the player to be able to keep jumping while in mid-air. Instead we only want the player to be able to jump if the player sprite is on a platform (or another object).
Luckily, Arcade Physics has a built-in property to detect if a sprite's body is touching (i.e., colliding with) another object. This property can detect touching along each side of the sprite:
player.body.touching.up
will be true
if the top of player
is touching another object
player.body.touching.down
will be true
if the bottom of player
is touching another object
player.body.touching.left
will be true
if the left of player
is touching another object
player.body.touching.right
will be true
if the right of player
is touching another object
player.body.touching.none
will be true
if player
is not touching any object
So we can use player.body.touching.down
to detect when the player
sprite is standing on a platform (or any other object).
In your update()
function, modify your existing if statement that checks whether the up arrow key has just been pressed, so it looks like this:
Now, in order to jump, the up arrow key has to have been just pressed down AND the bottom of the player sprite has to be touching another object (such as a platform).
Refresh your HTML preview to verify that player's character can only jump when it is on top of the ground or another platform.
There's one other minor fix that we're going to make. If the player
sprite is on a platform near the top of the game, the player
might collide with the upper game world boundary when the player
jumps. This is because we included a command to make the player
sprite's body collide with the game world boundaries.
Arcade Physics has a command that allows you to turn off the collision for a particular game boundary, such as up
, down
, left
, or right
. This command affects all sprites that are set to collide with the game world boundaries.
Add this Phaser command to your create()
function (after the command that starts the Arcade Physics system):
Refresh your HTML preview to verify that player's character won't collide with the top game world boundary if the character jumps up while standing on a platform near the top.
Now you're going to add another group, which will contain walls.
Similar to the platforms, the walls will simply be dark green rectangles (though you could replace them with another image). Inside the images subfolder of your assets folder are several wall images. They are all rectangles 25 pixels in width, but they range in height from 50 pixels up to 250 pixels.
Declare a global variable named:
wallGroup
(will contain all the wall objects)
In your preload()
function, load these images for the walls:
Load assets/images/wall-050h.png with 'wall-50'
as its asset key
Load assets/images/wall-150h.png with 'wall-150'
as its asset key
Load assets/images/wall-250h.png with 'wall-250'
as its asset key
In your create()
function, add code to do the following (after the code that adds the platforms, but before the code that adds the player sprite):
Add wallGroup
to the game
Enable physics for all members of wallGroup
Create a member in wallGroup
at position 525, 525
using asset 'wall-50'
Create a member in wallGroup
at position 1000, 425
using asset 'wall-150'
Create a member in wallGroup
at position 2000, 525
using asset 'wall-50'
Create a member in wallGroup
at position 3000, 525
using asset 'wall-50'
Create a member in wallGroup
at position 4000, 525
using asset 'wall-50'
Set all the members of wallGroup
to have their 'body.immovable'
property set to a value of true
In your update()
function, add code to do the following:
Make player
and wallGroup
collide with each other
Refresh your HTML preview to verify that the 5 walls appear in the game and that the player sprite collides with the walls.
At this point, you can delete the temporary code in your create()
function that added the distance marker text (1000px, 2000px, etc.).
Refresh your HTML preview to verify the distance marker text (1000px, 2000px, etc.) is gone.
The game will have coins that the player can collect for points. You'll create a Phaser group for the coins. You'll add some physics properties and a spinning animation to the coins.
Declare a global variable named:
coinGroup
(will contain all the coin objects)
In your preload()
function, load the spritesheet for the coins:
Load assets/images/coin.png as a spritesheet. Use 'coin'
as the asset key. The image contains 6 animation frames in a single row. The entire image is 192 pixels in width and 32 pixels in height. Use this information to determine the width and height of each frame.
In your create()
function, add code to do the following (after the code that adds the walls, but before the code that adds the player sprite):
Add coinGroup
to the game
Enable physics for all members of coinGroup
Next you're going to add members to the coinGroup
. However, instead of using a series of coinGroup.create()
statements (like how you added the platforms and walls), you're going to use a JSON array that stores data for the x-y position of all the coins.
Add this code to your create()
function (after the command that enabled physics for coinGroup
):
This code creates a local variable named coinData
which is an array containing JSON data for each coin to be added to the game.
Square brackets [ ]
are used to contain the items in an array. Within the array, the JSON data for each item is listed inside curly braces { }
using name-value pairs: the name of a variable followed by its value. A colon is used to separate the variable name and its value.
Within the JSON data for an item, a comma is used to separate different name-value pairs (so you can list multiple variables for each item). As you can see, the JSON data list an x
and y
variable for each coin. If you were using variables that have strings (text) for their values, then list the values within quotes.
Commas are also used to separate the different items within the array (i.e., the comma that appears after the right-hand curly brace). Notice that you don't include a comma after the last item in the array.
In the example code above, we've listed the JSON data for each item on its own separate line, but you could list multiple items per line (as shown below) to save vertical space in your code.
Each item in an array is identified using an index number, which represents the order in which the items are listed. The first item in an array is always numbered as index 0
. Since there are 15 items listed in this array, they are numbered in order as 0-14. For example, coinData[1]
actually refers to the 2nd item in the array, which is the data: { "x":150, "y":0 }
The variable values for an item in an array can be referenced by using the item's array index number followed by a period and then the variable name. For example, coinData[1].x
is 150
and coinData[1].y
is 0
.
Now you're going to loop through this JSON array to use the data to add coins to coinGroup
.
Add this code to your create()
function (after the code listing the JSON array for coinData
):
This for
loop will iterate through the entire coinData
array, using i
to represent the index number of the current item: i
will start at 0
and increase by one after each loop (i++
) until it reaches the end of the array (which is represented by coinData.length
).
Inside the for
loop a local variable named coin
is created as a new member in the coinGroup
using the x
and y
values stored in coinData[i]
.
There is some missing code inside the for
loop that you will add in just a bit.
Refresh your HTML preview to verify that the 15 coins appear in the game at the positions listed in the coinData array.
Most of the coins will appear along the top of the game, while a few appear in mid-air.
Now you'll add the missing code inside the for
loop to give each coin some physics properties and an animation.
Inside your for
loop (after the command that creates the coin in the coinGroup
), add code to do the following for each coin
:
Set the anchor for coin
to be its center (0.5, 0.5
)
Set its body.gravity.y
to 400
Set its body.bounce.y
to 0.5
Add an animation named 'spin'
that will play frames 0-5 (in order) at 10 frames per second in a loop
Play the 'spin'
animation
Next, you need to make sure the coins collide with the platforms and walls, similar to what you did for the player.
In your update()
function (after the collide()
statements for the player
), add code to do the following:
Make coinGroup
and platformGroup
collide with each other
Make coinGroup
and wallGroup
collide with each other
Refresh your HTML preview to verify that the coins fall, bounce, and spin. The coins should collide with the platforms and walls.
Declare a global variable named:
score
and assign it an initial value of 0
scoreText
(used to display score on-screen)
In your create()
function, add code to the following:
Add scoreText
to the game as text with these properties:
The text should be positioned at: 20, 20
The text should display: 'Score: ' + score
The text should use this style: { fontSize: '20px', fill: '#222222' }
(by default, Phaser will use Arial bold for the font unless you change to a different font or fontWeight)
Make scoreText
stay fixed to the camera (so it doesn't move when the game world scrolls)
Refresh your HTML preview to verify that "Score: 0" appears at the upper-left of the game screen and stays fixed in place, even as the game world scrolls.
When the player
sprite collides with a coin, the coin should disappear and the player's score should increase.
Add this code in your update()
function (after the other collide()
statements):
This collide()
command will call a custom function named collectCoin
whenever the player
collides with a member of the coinGroup
.
Add this code after your update()
function to create the custom function:
Inside the curly braces { }
of your collectCoin()
function, add code to do the following:
Remove the coin
using the kill()
method
Increase the score
by 50
Update the text
property of scoreText
to display: 'Score: ' + score
Refresh your HTML preview to verify the player can collect the coins and the score increases by 50 for each coin.
If you wanted to add a sound effect when the player collects a coin, you would add a command to play the sound inside the collectCoin()
function.
In this third practice, you're going to create a side-scrolling game that follows the player's character as it moves through an extended game world. The instructions will help you make a partial game, which you'll need to finish designing and creating yourself. You'll also create some of your own custom assets (sound effects and an animated sprite) to add to your game. This practice will allow you to apply your existing knowledge of Phaser, plus show you a few more new features.
PREVIEW VIDEO: Demo of Totally Awesome Untitled Game
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 30 images and 2 sounds. Place the assets folder into your game template folder. (For this game, you won't necessarily use all the provided images. It will be up to you to decide whether to use some of them.)
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. (If you don't get the solid black box, consult the Troubleshooting tips at the end of the Phaser Game Template page.)
This third practice game will be coded in 15 steps. Most of the steps will involve coding that's similar to things you did previously. However, some of the steps will 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 Coding Steps 1-5 on the previous page.
IMPORTANT: Take the time to read each step, and try to understand how the code works.
This game will feature a group of cats as an "enemy". Cats are normally great companions, but these cats don't want to be your friend. If the player touches a cat, the player's health will be reduced.
Declare a global variable named:
catGroup
(will contain all the cat objects)
In your preload()
function, load the spritesheet for the cats:
Load assets/images/cat.png as a spritesheet. Use 'cat'
as the asset key. The image contains 4 animation frames in a single row. The entire image is 128 pixels in width and 32 pixels in height. Use this information to determine the width and height of each frame.
In your create()
function, add code to do the following (after the code that adds the coins, but before the code that adds the player sprite):
Add catGroup
to the game
Enable physics for all members of catGroup
Rather than placing the cats at specific locations, we're just going to space the cats evenly across the top of the game world, give them a random velocity, and let them drop down onto the platforms.
An easy way to do this is to just use a for
loop (similar to how you added the coins but without using JSON data for the positions).
Add this code to your create()
function (after the command that enabled physics for catGroup
):
This for
loop will create 25 cats in the catGroup
. There is some missing code that you'll add in a bit.
The x-position for each cat will be calculated as: i * 200 + 100
. This will place the cats 200 pixels apart, with the first cat positioned at 100:
The x-position for the first cat (i = 0
) will be 100 because 0 * 200 + 100 = 100
The x-position for the next cat (i = 1
) will be 300 because 1 * 200 + 100 = 300
The x-position for the next cat (i = 2
) will be 500 because 2 * 200 + 100 = 500
The x-position for the next cat (i = 3
) will be 700 because 3 * 200 + 100 = 700
and so on...
The y-position for each cat will be 0
(the top of the game display).
Refresh your HTML preview to verify that the 25 cats appear evenly spaced apart along the top of the game.
Now you'll add the missing code inside the for
loop to give each cat some physics properties and an animation.
Inside your for
loop (after the command that creates the cat in the catGroup
), add code to do the following for each cat
:
Set the anchor for cat
to be its center (0.5, 0.5
)
Set its body.gravity.y
to 300
Set its body.bounce.x
to 1
Set its body.collideWorldBounds
to true
Add an animation named 'left'
that will play frames 0-1 (in order) at 10 frames per second in a loop
Add an animation named 'right'
that will play frames 2-3 (in order) at 10 frames per second in a loop
Add this code to give each cat a random velocity (and a 50% chance of having its direction reversed):
Next, you need to make sure the cats collide with the platforms and walls, similar to what you did for the player and coins.
In your update()
function (after the collide()
statements for the player
), add code to do the following:
Make catGroup
and platformGroup
collide with each other
Make catGroup
and wallGroup
collide with each other
You'll also add some code to make sure each cat plays the correct animation based on its direction of movement (which will change as the cats bounce off the walls and game boundaries).
Add this code in your update()
function (after the code that added the background parallax):
This function will check the velocity of each cat in the catGroup
. If the cat's velocity is less than zero, that means the cat is moving to the left. Otherwise, the cat is moving to the right.
Refresh your HTML preview to verify that the cats fall down. The cats should move at different speeds and should collide with the platforms and walls. If a cat collides with a wall or a game boundary, the cat should reverse direction. The cats should play the correct animation as they move and change direction.
For this game, we're going to add a simple AI (artificial intelligence) for the cats: if a cat is on a platform, then it will patrol back and forth on the platform (instead of running off the edge).
We'll add this behavior using a custom function that will run whenever a cat is on top of a platform (i.e., colliding with the platform).
Modify your existing collide()
command for the catGroup
and platformGroup
:
When a collision occurs, it should call a custom function named patrolPlatform
.
Add this code after your update()
function to create the custom function :
The patrolPlatform()
function checks for two possible conditions:
The enemy
is moving to the right (velocity.x > 0
) AND the right edge of the enemy
sprite is hanging over the right edge of the platform
sprite
OR
The enemy
is moving to the left (velocity.x < 0
) AND the left edge of the enemy
sprite is hanging over the left edge of the platform
sprite
If either of those conditions is true, then the function simply reverses the direction of the enemy
by multiplying its velocity.x
by -1
. For example, if the velocity.x
was -125 (moving to the left), it would become 125 (moving to the right).
Refresh your HTML preview to verify that any cats landing on a platform will patrol back and forth on the platform. Even cats on the ground (which is also a platform) should patrol back and forth.
If the player's character touches a cat, the player will have its health reduced slightly. So let's first add a health bar for the player.
Declare a global variable named:
healthBar
(green bar that will be scaled to represent health value)
In your preload()
function, load these images for the health bar:
Load assets/images/bar-red.png with 'red-bar'
as its asset key
Load assets/images/bar-green.png with 'green-bar'
as its asset key
Load assets/images/bar-outline.png with 'bar-outline'
as its asset key
In your create()
function (after the code that adds the player
sprite), add commands to do the following:
Set the health
property for the player
to 100
Set the maxHealth
property for the player
to 100
In your create()
function (after the code that adds scoreText
), add code to do the following:
Declare a local variable named healthText
that adds text to the game with these properties:
To declare a local variable, just list var
in front of the variable name
The text should be positioned at: 325, 20
The text should display: 'Health'
The text should use this style: { fontSize: '20px', fill: '#222222' }
Make healthText
stay fixed to the camera (so it doesn't move when the game world scrolls)
Declare local variables named barBackground
and barOutline
, like this:
var barBackground, barOutline;
Add an image assigned to barBackground
at position 400, 20
using the 'red-bar'
asset
Make barBackground
stay fixed to the camera
Add an image assigned to healthBar
at position 400, 20
using the 'green-bar'
asset
Make healthBar
stay fixed to the camera
Add an image assigned to barOutline
at position 400, 20
using the 'bar-outline'
asset
Make barOutline
stay fixed to the camera
Refresh your HTML preview to verify that the health bar appears at the top center of the game. The bar should be green with a dark gray outline. (Remember that the red bar background is currently hidden because it is covered by the green bar.)
Let's add a sound for the cats that will play whenever the player touches a cat.
Declare a global variable named:
catSound
In your preload()
function, load the audio file for the sound:
Load assets/sounds/meow.wav with 'cat-sound'
as its asset key
In your create()
function, add the sound to the game:
Add catSound
to the game as audio using the 'cat-sound'
asset with a volume of 0.2
Now let's detect when the player touches a cat. Besides reducing the player's health, we'll play the cat sound and also make the cat jump away from the player.
Rather than using a collide()
method to detect when the player touches a cat, we'll use an overlap()
method. They both detect when objects touch. However, collide()
also allows moving objects to transfer momentum, so if we used a collide()
method, then the cats would speed up every time they touch the player.
In your update()
function (after the collide()
method between the catGroup
and platformGroup
), add the following:
Add a overlap()
method between player
and catGroup
(list in this order) that will call a custom function named touchCat
After your update()
function, add the following:
Add a new custom function named touchCat()
. Inside the parentheses ( )
, list player, cat
as parameters.
Inside the curly braces { }
of your touchCat()
function, add code to do the following:
Reverse the direction of the cat
by multiplying its body.velocity.x
by -1
Make the cat
jump by setting its body.velocity.y
to -150
Move the cat away from the player by changing its x-position with this code:
Play the catSound
Reduce the player
health by 5
using the damage()
method
Scale the width of the healthBar
to be: player.health / player.maxHealth
(keep the scale for the height as 1
).
If you need help with the code for scaling the health bar, look back at Step 11 of Practice 2.
Refresh your HTML preview to verify that the player's health bar is reduced when the player touches one of the cats. The cat should reverse direction and jump away from the player. You should hear the cat sound play when this happens.
If the player runs out of health, Phaser will automatically kill()
the sprite. When that happens, let's reset the player's character back at the beginning of the level.
In your create()
function (after the code that adds the player
), add code to do the following:
Add an onKilled
event for the player
that will run a function.
If you need help with the code for adding an onKilled
event, look back at Step 6 of Practice 2.
Inside the curly braces { }
of the onKilled
event function, add commands to do the following:
Reset the player
back to position 25, 300
with a health value of 100
using this command:
Scale the width of the healthBar
Refresh your HTML preview to verify that when the character's health runs out, the player is reset back to the start of the level with full health.
Next we're going to add some "power-up" objects to the game that will temporarily boost the player's running and jumping speeds. We'll use a star as the power-up object. We'll set a timer to control how long the power-up works.
Declare a global variable named:
powerUpGroup
(will contain all the power-up objects)
powerUpActive
and assign it an initial value of false
In your preload()
function, load this image for the power-up:
Load assets/images/star.png with 'star'
as its asset key
In your create()
function (after the code that adds the coinGroup
), add commands to do the following:
Add powerUpGroup
to the game as a group
Enable physics for the members of powerUpGroup
Create a member of powerUpGroup
at position 1000, 200
using the 'star'
asset
Create a member of powerUpGroup
at position 3000, 400
using the 'star'
asset
Set all the members of powerUpGroup
to have their 'anchor.set'
property set to a value of 0.5
Refresh your HTML preview to verify that the star objects appear in the game at correct positions. They should be floating in mid-air (since you didn't add any gravity to them).
When the player collects a star, we'll display a message on the screen while the power boost is active (and then hide the message after the power-up timer has run out).
So let's add a blank text object to the game that we can use later to display a message to the player. We'll also use this same text object for a different message in Step 8.
Declare a global variable named:
messageText
(will be used to display text)
In your create()
function, add code to the following:
Add messageText
to the game as text with these properties:
Position the text at: 500, 100
Have the text display nothing for now (use empty quotes): ''
Style the text using: { fontSize: '48px' }
Set the text anchor position to be its center: (0.5)
Set a shadow for the text using these properties: (2, 2, '#000000', 2)
Make the text stay fixed to the camera
Hide the text by setting its visible
property to false
Now let's detect when the player touches a star. This is when we'll start the power boost. We'll display a message on the screen, change the player's character to a green color, and start a 10-second timer. While the power-up is active, we'll increase the player's running and jumping speeds. When the timer runs out, we'll change everything back to normal.
In your update()
function (after the collide()
method between the player
and coinGroup
), add the following:
Add a collide()
method between player
and powerUpGroup
(list in this order) that will call a custom function named collectPowerUp
After your update()
function, add the following:
Add a new custom function named collectPowerUp()
. Inside the parentheses ( )
, list player, powerUp
as parameters.
Inside the curly braces { }
of your collectPowerUp()
function, add code to do the following:
Remove the powerUp
object (the star) using the kill()
method
Change the value of powerUpActive
to true
Set the text
property of messageText
to display 'Power Boost'
Set the fill
color for messageText
to be green using: '#00ff00'
Make messageText
visible
Change the player
sprite to a green color by setting its tint
property to 0x00ff00
Use this command to start a timer event that will run a custom function named stopPowerUp
after 10 seconds have elapsed:
Before we forget, let's add this custom function that will stop the power-up.
Add another new custom function named stopPowerUp()
. Inside the curly braces { }
of this function, add code to do the following:
Change powerUpActive
to false
Hide messageText
by changing its visible
property
Remove the green color from the player
by setting its tint
to 0xffffff
(represents no tint)
We haven't added the code to change the player's speed yet, but let's test out what we've added so far.
Refresh your HTML preview to verify that the "Power Boost" message appears when the player collects a power-up star. The player should turn a green tint. The player's speed will still be normal (because we haven't added that code yet). After 10 seconds, the message should disappear and the player should turn back to normal.
Now we're ready to add the code to increase the player's running and jumping speeds while the power-up is active.
We'll use the value of powerUpActive
to detect when the power-up is supposed to be active. If powerUpActive
is true
, we'll use a larger value for the velocity that makes the player run or jump. Let's increase the velocity by 50% (e.g., if the normal running speed is 200
, we'll boost to 300
).
So we'll modify the existing code in the update()
function that detect the player's input. Inside those if-else statements , we'll add local variables to represent the normal values for runSpeed
and jumpSpeed
. If powerUpActive
is true
, we'll increase the values of these variables.
Modify your existing if-else statements in the update()
function that check the player input, so the code looks like this:
Refresh your HTML preview to verify that the player's running and jumping speeds are boosted while the power-up is active.
Next let's add to the challenge of the game by adding a countdown timer for the player to complete the level. Instead of displaying the time as text, we'll add a timer bar that decreases to show the amount of time remaining. Let's give the player 2 minutes (120 seconds) to complete the level.
Declare a global variable named:
timeBar
(yellow bar that will be scaled to represent amount of time remnaining)
timeUp
and assign it an initial value of false
timeLimit
and assign it an initial value of 120
(which represents the number of seconds)
In your preload()
function, load these images for the health bar:
Load assets/images/bar-black.png with 'black-bar'
as its asset key
Load assets/images/bar-yellow.png with 'yellow-bar'
as its asset key
In your create()
function (after the code that adds barBackground
, healthBar
, and barOutline
), add code to do the following:
Declare a local variable named timeText
that adds text to the game with these properties:
To declare a local variable, just list var
in front of the variable name
The text should be positioned at: 720, 20
The text should display: 'Time'
The text should use this style: { fontSize: '20px', fill: '#222222' }
Make timeText
stay fixed to the camera
Add an image assigned to barBackground
at position 780, 20
using the 'black-bar'
asset
Make 'barBackground
stay fixed to the camera
Add an image assigned to timeBar
at position 780, 20
using the 'yellow-bar'
asset
Make 'timeBar
stay fixed to the camera
Add an image assigned to barOutline
at position 780, 20
using the 'bar-outline'
asset
Make 'barOutline
stay fixed to the camera
Refresh your HTML preview to verify that the time bar appears at the top center of the game. The bar should be yellow with a dark gray outline. (The black bar background is currently hidden because it is covered by the yellow bar.)
Now let's add a custom function to determine how much time has elapsed in the game and use that to update the time bar to show how much time is remaining.
Luckily, Phaser has a built-in method called game.time.totalElapsedSeconds()
to calculate how much time (in seconds) has elapsed since the game started.
So we can just subtract the game's elapsed time from the timeLimit
to determine how much time is left over. We can then use this result to scale the timeBar
.
In your update()
function, add this as the first line of code inside the curly braces (before any of the collide()
methods):
This will run a custom function named displayTimeLeft()
as the first step every time the update()
loop occurs.
After your update()
function, add this code to create the custom function:
Notice that we had to detect if the value of timeLeft
was less than zero. If so, we set to be exactly zero. Otherwise, if we didn't do that, the timeBar
would scale to show negative timeLeft
(the yellow bar would actually start expanding outside of its outline). We also use this to detect when to set the value of timeUp
to true
.
Also notice that you scale the timeBar
similar to how you scale the healthBar
. For the timeBar
, you divide the timeLeft
by the timeLimit
to get the proportion of time remaining. For example, if only 30 seconds remain out of a 120-second time limit, then the timeBar
should be scaled to 0.25 of its original width (30 / 120 = 0.25).
Refresh your HTML preview to verify that the yellow time bar decreases in width to show the time remaining in the game until it reaches zero (time bar will be completely black).
Now let's add a "game over" function that will run when the time limit for the game is over. We can use the value of timeUp
to detect when this has occurred.
When timeUp
is true
, we want to display a game over message to the player, and make the player
sprite disappear (so the game is effectively over).
Modify the first line of code inside your update()
function, so it looks like this instead:
Now if timeUp
is true
, it will run the custom function named gameOver()
. Otherwise (when timeUp
is false
), it will run the custom function displayTimeLeft()
.
Next you need to add the gameOver()
function.
After your update()
function, add a new custom function named gameOver()
. Inside the curly braces { }
of this function, add code to do the following:
Set the text
property of messageText
to display 'Time Up'
Set the fill
color for messageText
to be red using: '#ff0000'
Make messageText
visible
Make the player
sprite disappear by setting its exists
property to false
Notice that instead of using the kill()
method to remove the player
sprite, we simply set its exists
property to false
. The reason for this is because we previously added an onKilled
event for the player
(when its health runs out) that resets the sprite back to the beginning of the game with full health. However, when the time limit runs out, we want the game to be completely over, so that's why we used the exists
property instead of the kill()
method. Both of them remove the player, but we want to avoid triggering the onKilled
event in this situation.
Refresh your HTML preview to verify that "Time Up" message appears and the player disappears when time runs out.
What you’ve made so far is a partial game. Now it's up to you to design and build the rest of the level.
In this step, you’ll create of a map of your design for the complete level. In Steps 10-15, you’ll implement your design and playtest it.
A recommended scale for the graph is to have each square represent either 20 or 25 pixels (choose one). Use the same scale for both the horizontal and vertical axes of the graph.
You will probably need to split up your level map into multiple sections (either on the same graph sheet or across multiple sheets).
Mark and label your horizontal and vertical scales for your game world on the graph. Unless you've changed it, your game world is 5000 pixels in width and 600 pixels in height.
The x-positions start with 0 as the left edge of the game world, and they increase as you move towards the right edge.
The y-positions start with 0 as the top edge of the game world, and they increase as you move towards the bottom edge.
OPTIONAL: If desired, you can reduce the width of the game world from 5000 pixels to a smaller size (but make it at least 3000 pixels wide). The existing platforms are placed within the first 1500 pixels of the game world.
Include the size and locations of the existing platforms, walls, and other objects (coins, etc.) in your level map. (If desired, you can change the size and/or locations of these existing objects.)
For the platforms and walls, the position at which they were created represents their top-left corner (since we didn't change the default anchor position for these objects).
As a reminder, all the platforms are 25 pixels in height. The width of the platforms range from 50 pixels to 500 pixels (depending on which platform asset is used).
As a reminder, all the walls are 25 pixels in width. The height of the walls range from 50 pixels to 250 pixels (depending on which wall asset is used).
You do not necessarily need to include the locations of moving objects (cats, etc.) unless it is helpful to you. For example, if you want the player to encounter a "boss" enemy at a certain location, it may help to include that on the map.
Be sure to include the size and locations of the new objects that you plan to add in Steps 10-12.
Partial scale map of level (first 2500 pixels of width) showing starting position of player, size and location of existing platforms and walls, locations of coins (after falling), and location of power-up (star). Next step would be to add new objects, and create rest of map.
RECOMMENDED: Preview the instructions in Steps 10-15 to see more details about what you’ll need to do to complete this practice. Here’s a brief summary:
In Step 10, you’ll add more platforms (and walls) throughout the rest of the level. You need to add at least 10 more platforms. You can modify the size and/or location of any existing platforms or walls (but you don’t have to).
In Step 11, you’ll add a new obstacle that hinders the player in some way, such as a spike, new enemy, etc. You can add an individual obstacle or a group of obstacles.
In Step 12, you’ll add a new resource that helps the player in some way, such as a diamond, health pack, weapon, etc. You can add an individual resource or a group of resources. You'll also add more coins for the player to collect.
In Step 15, you’ll have someone else playtest your game to provide feedback.
Add additional platforms (and walls) throughout the rest of the level:
Add at least 10 more platforms to the game. You decide the size and location of the additional platforms (and any additional walls).
OPTIONAL: You can change the size and/or location of any existing platforms or walls.
OPTIONAL: If desired, you can reduce the width of the game world from 5000 pixels to a smaller size (at least 3000 pixels). Refer back to Step 3 to see how to make this change in your code.
OPTIONAL: If desired, you can also reduce the width of the game display from 1000 pixels back to 800 pixels. Refer back to Step 3 for help with the code. However, if you do this, be sure to also:
Change the width of #my-game
in your CSS file to 800px
Adjust the locations of the health bar and time bar to fit within the narrower game display.
IMPORTANT: After adding the rest of the platforms (and walls), playtest your game to ensure the player
can navigate through the platforms and walls to complete the level. If it's not possible to navigate the level, you'll need to do one or both of the following (and then retest your game):
Modify the size and/or location of specific platforms (or walls).
AND / OR
Modify certain properties of the player
, such as its running speed, jumping speed, gravity, etc.
Use to create a scale map of the complete game level showing the size and location of all the objects (including new objects you’ll add in Steps 10-12), such as platforms, walls, coins, etc.
In Step 13, you’ll add more sound effects to the game. You’ll use existing sounds from Practice 1 to play a sound effect when the player collects a coin and when the player collects a power-up. Optionally, you create your own sound effects using a web app called .
In Step 14, you’ll have the option to create an animated sprite using a web app called .