Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Just as people like different types of books or movies, people like different types of video games. If you're going to design a video game, you'll need to know more about what motivates people to play certain games.
You'll start by taking a survey to determine your own Gamer Motivation Profile. Later in the project, each team will decide what gamer motivations their game will target.
Quantic Foundry is a game analytics consulting company that has gathered data from over 300,000 video game players to construct a scientific model that describes different types of gamer motivations.
This model identified 12 motivations that clustered into 6 pair groups: Action, Social, Mastery, Achievement, Immersion, and Creativity. For example, Challenge and Strategy are motivations that belong to the Mastery group.
VIDEO: This gamer motivation model was constructed using a statistical method called factor analysis. If you're interested, Quantic Foundry has a video explaining how this model was built using survey data collected from thousands of video game players.
Take the online survey to determine your Gamer Motivation Profile. The survey takes about 5-7 minutes.
Record your results in this assignment document.
Discuss and compare your results with others in the class.
Some of the assignments in this project guidebook include links to Google Drive templates (document, spreadsheet, slide presentation, drawing, etc.). All the templates are shared as "View Only." Students must create a copy of the template in order to modify it. (Students should not request edit access, as the file is a master template for all students.)
MAKE A COPY: To use a Google template, be sure you're logged in to your Google Account. From the File menu of the template, select Make a copy. Save the copy to your shared team folder in Google Drive. The copied file can now be modified.
We know that video game players are motivated by different types of gameplay. For example, some players are motivated to play games featuring challenge and competition, while some players might be motivated by games featuring story and strategy.
Once you've designed a game that players will be interested in playing, you want to keep those players engaged in the gameplay. There are other motivational factors that can help accomplish this.
We can use research in psychology to understand what factors motivate people to perform behaviors in general (whether it is playing a game or doing something else). Two of the possible ways to classify these motivational factors include:
External vs. Internal — External motivations originate outside ourselves, while internal motivations come from within ourselves.
Negative vs. Positive — Negative motivations involve avoiding painful experiences, while positive motivations involve seeking out pleasing experiences.
This diagram is a model that combines both of these dimensions of motivations:
Rewards and punishments are external motivations. As we know, rewards are positive, and punishments are negative. They can be very effective at getting people to do things. However, the effectiveness depends on the amount and frequency of the rewards or punishments. Psychologist B.F. Skinner conducted numerous studies on the effectiveness of external motivations on behavior.
Rewards and punishments are commonly used in video games. Rewards and punishments often help players learn how the gameplay works — and are often a core part of the gameplay experience.
Rewards in games might include: receiving points, gaining special abilities, etc.
Punishments in games might include: losing health, losing resources, etc.
Even things like sounds can be used in a game as reward or punishment: an annoying sound might play while an enemy is alive, a pleasing sound might play when the enemy is defeated, etc.
An advantage of using external motivations is they quickly teach and reinforce desired behaviors. People quickly learn the cause-and-effect behind rewards and punishments.
However, the drawback with external motivation is that people will also quickly stop performing a behavior if the rewards or punishments are removed — unless the person has already developed some internal motivation to keep performing the behavior.
In fact, studies have shown that people's internal motivation to perform a behavior can actually become lower after becoming dependent on external motivation.
In this assignment, you will explore how external motivations are used in games. The next assignment will focus on internal motivations in games.
Follow the instructions and links in this assignment to playtest two games to compare their use of rewards and punishments.
Discuss your findings as a class.
Your project challenge is to design and build a video game that people want to play.
Your team will solve the project challenge by applying a user-centered design process that follows an iterative "Learn-Build-Measure" cycle. In this project, the players of the game are your users.
A key source of information during the game design process will come from playtesting — having users play the game in order to get feedback on specific aspects of the game. Playtesting of a game is similar to user testing of an app.
Research Problem Space - Let's start by exploring what makes a game engaging and motivating to players. Then you'll explore the elements that help define all games. You'll practice using the Phaser JS game engine to code some sample video games. You'll also practice creating sound effects and animated art for games.
Define Problem to Solve - Your team will identify the gaming motivations and experiences that your game is intended to provide for your target players. Then your team will generate ideas for games that might appeal to these target players. Your team will produce game treatments for several possible game concepts. After evaluating the game treatments with target players, your team will select a final game concept to focus on.
Design Solution & Deliver Proposal - Create a game design document, representing a conceptual prototype of your game. Then create a paper prototype of your game to playtest, evaluate, and refine your game's design. Present your proposed game design for critique by other teams.
Build & Develop Solution - Create a plan to develop a digital prototype of your game design. During development, track your team's progress and any issues that arise. Program your game code in stages, starting with the core game mechanics and iteratively adding, testing, and refining other features. Create and add the visual and audio assets for the game. Create a marketing website to promote your game.
Evaluate & Improve Solution - After creating your game and marketing website, test them with target players to gather feedback and identify possible improvements to make. If feasible, implement improvements.
Reflect & Present Project Results - At a public poster presentation, explain your team's design process and demonstrate your game. Be prepared to respond to questions. Each team member will reflect on his or her project experience.
Be sure that you completed the Prep Steps on the previous page.
IMPORTANT: Take the time to read each step, and try to understand how the code works.
Add a sprite for the player. As a reminder, adding a sprite to the game involves 3 steps:
Declare a global variable for the sprite.
Load its spritesheet into memory in the preload()
function.
Add the sprite to the game in the create()
function.
Use player
as the name of the global variable for the sprite.
Use assets/images/spaceship.png for the spritesheet image. It contains 3 animation frames that are each 64 pixels in width and 64 pixels in height. Assign an asset key name to the spritesheet, such as ship
.
Add the player
sprite to the center of the game world, and set the sprite's anchor to its center.
For help with the code for sprites, you can refer back to Step 4 of Practice 1 (or look at your Practice 1 game code).
Refresh your HTML preview to verify that the player sprite appears in the center of the game.
This game will played using keyboard inputs. Most keyboard-based games use either the WASD keys or the arrow keys to move the player's character.
Your game will use the following keys to control the player's spaceship:
left arrow key will rotate the ship counter-clockwise
right arrow key will rotate the ship clockwise
up arrow key will move the ship forward
spacebar key will fire the ship's laser
Since it is common for games to use the arrow keys for input, Phaser has a command to add all four arrow keys as inputs (rather than having to add them individually). Phaser refers to the arrow keys as cursor keys (since many computer programs use the arrow keys to move the cursor).
Create a global variable named arrowKey
.
Add this Phaser command in the create()
function:
This command will make all 4 arrow keys (left, right, up, down) into inputs assigned to the arrowKey
variable. The individual keys are identified using the properties left
, right
, up
, and down
. So in your game, these inputs will be:
arrowKey.left
(used to rotate ship counter-clockwise)
arrowKey.right
(used to rotate ship clockwise)
arrowKey.up
(used to move ship forward)
arrowKey.down
(not used in game)
Create a global variable named fireKey
.
Add a Phaser command in the create()
function to add the spacebar key as an input assigned to the fireKey
variable.
For help with the code to add an individual key as input, you can refer back to Step 5 of Practice 1 (or look at your Practice 1 game code).
Let's make the player's spaceship move in response to the player's inputs. For that, we're going to need to use physics.
Many games features simulated physics. Objects in the games can move and accelerate. The objects can be affected by gravity and friction. Objects can collide and bounce off each other — and will respond differently based on their masses.
The cool thing about simulated physics is that you can make the game world behave however you want: You can change gravity so that objects will "fall up" instead of falling down. You can selectively apply gravity only to certain objects — or eliminate gravity entirely. You get to decide which physics properties to use and how to use them.
Phaser has several physics systems to choose from. We're going to use the Arcade Physics system, which is a good choice for most games. Arcade Physics has lots of useful physics properties and methods, plus support for weapons (i.e., the ability to shoot or throw objects such as bullets, lasers, arrows, fireballs, etc.). It's more simplistic than the other physics systems, but it runs much faster.
First, you have to "start" the physics system by including a Phaser command at the beginning of your create()
function. Then you have to "enable" physics for specific objects in your game. Not every object in the game actually needs to use physics, so you only enable it on the ones that do.
Start the Arcade Physics system by adding this Phaser command as the first line of code in the create()
function:
Enable physics on the player
sprite by adding this Phaser command in the create()
function (after the command that set the anchor for the sprite):
This will create an Arcade Physics body
object associated with the player
sprite. So let's add a couple of physics properties to the player.body
.
Add this Phaser code in the create()
function (after the "enable" physics command for the sprite):
The first line simply sets a limit on the velocity (speed) of the sprite. In Phaser, velocity is measured in pixels per second.
The second line adds a small amount of drag (friction) as the sprite moves. In reality, a spaceship in outer space would experience almost zero drag (because there is no air to cause drag), but since our game screen is relatively small, we'll add a small amount of drag so the spaceship doesn't drift off-screen with every forward movement.
Another thing to notice is that we did not set a gravity value for the player.body
because our game is set in space far from any planets or stars that would create a strong gravity field. Instead, we just want the spaceship to float along, affected only by its engines (and the small amount of drag that we added). In your next practice game, we'll start using gravity.
Let's add code to rotate and move the player
spaceship based on the arrow key inputs.
In Phaser, there are two properties for a sprite's rotation:
angle
can be used to get or set the angle of rotation of the sprite, measured in degrees
rotation
can be used to get or set the angle of rotation of the sprite, measured in radians (2π radians = 360°)
Both angle
and rotation
do the same thing — they just use different units of measurement. Either can be used to get the current value of the sprite's angle of rotation. Either can be used to set the sprite to a specific angle of rotation.
Add this Phaser command in the create()
function (as part of the commands after adding the sprite):
Refresh your HTML preview to verify that the player sprite is now pointed up, instead of to the right.
Setting the angle
to a negative value (up to -180) will rotate the sprite counter-clockwise from its original orientation. Setting the angle
to a positive value (up to 180) will rotate it clockwise from its original orientation. However, it instantly switches the rotation to a specific angle.
Instead, what we want is to make the player
sprite turn in a continuous motion, when the player presses the left or right arrow keys.
There are a couple of Arcade Physics properties that will allow you to turn (rotate) a sprite's body
in a continuous motion:
body.angularVelocity
can be used to get or set the speed of sprite's rotation (measured in pixels per second) — negative values = counter-clockwise rotational speed, positive values = clockwise rotational speed
body.angularAcceleration
can be used to get or set the acceleration of the sprite's rotation (measured in pixels per second squared) — negative values = decrease the rotational speed, positive values = increase the rotational speed
We're going to use body.angularVelocity
, so the spaceship will rotate at a constant speed:
If the left arrow key is pressed, we'll turn the sprite counter-clockwise at a speed of 200 pixels per second.
Otherwise, if the right arrow key is pressed, we'll turn the sprite clockwise at a speed of 200 pixels per second.
Otherwise, if neither the left nor right arrow key is pressed, we'll stop turning the sprite by setting the speed to 0.
To move the player
sprite forward, there is an Arcade Physics method (i.e., function) called accelerationFromRotation()
that will allow us to accelerate a sprite's body in the direction of its current angle of rotation:
If the up arrow key is pressed, we'll move the player
sprite forward by accelerating the sprite at 200 pixels per second squared in the direction of its current rotation.
Otherwise, if the up arrow key is not pressed, we'll stop accelerating the player
sprite by setting the acceleration to 0. The sprite will stop speeding up, but it will continue to drift forward at its current speed — only being slowed down by the drag that we applied to the player
sprite's body (or if the player turns the ship around and starts accelerating in the opposite direction).
Add this code in the update()
function to check the inputs and move the spaceship:
Refresh your HTML preview to verify that you can rotate and fly the spaceship using the arrow keys.
You probably discovered that you can actually fly the spaceship out of the game display. If you fly off-screen, you can turn around and fly back into the game display (but it can be a little tricky to do when you can't see where the ship is pointed).
We want to keep the spaceship within the game world boundaries.
Add this Phaser command in the create()
function (to go along with your other player.body
properties):
Refresh your HTML preview to verify that the spaceship will stay within the game world boundaries as you fly around.
Next let's add an animation and sound effect to the spaceship to make the flying feel more realistic.
Remember that the player
spritesheet has 3 animation frames:
Add this Phaser command in the create()
function (somewhere after adding the player
sprite):
As you can see, this command will add an animation for the player
sprite. Let's examine the code inside the parentheses to understand how it works:
'moving'
assigns a key name to the animation. It is similar to an asset key name or variable name. You decide what name to use, as long as it is a unique name.
[0, 1, 2]
is an array listing the spritesheet frame numbers to use (in order) for the animation. In our case, we're using all 3 frames (0-2). In other cases, a spritesheet might contain frames for different animations, so you only list the specific frames needed for each animation.
10
represents the frame rate that will be used for the animation, in frames per second. Depending on the animation, you might change this number to make it play faster or slower.
true
indicates that this animation should play repeatedly in a loop. If you didn't want the animation to loop, then you can list false
.
Similar to how sounds work in Phaser, the animation won't actually play until you use a command telling it to start playing.
Add this Phaser command in the update()
function, so that the animation will play when the up arrow key is pressed:
Add this Phaser command in the update()
function, so that the animation will stop when the up arrow key is not pressed:
This command stops the animation, and then sets the player
sprite to display frame number 1 (the second frame), which has the smallest engine exhaust flames (to make it seem like the spaceship engine is "idling").
Refresh your HTML preview to verify that the animation plays as you move the spaceship forward and stops when you release the up arrow key.
Now let's add a sound effect for the spaceship's engines.
As a reminder, adding a sound to the game involves 3 steps:
Declare a global variable for the sound.
Load the sound into memory in the preload()
function.
Add the sound to the game in the create()
function.
Use engineSound
as the name of the global variable for the sound.
Use assets/sounds/engine.mp3 for the sound file. Assign a unique asset key name to the sound, such as engine
.
Add engineSound
to the game with a volume of 0.3
. Also, set this sound to loop repeatedly when it plays.
Let's have engineSound
play from the very beginning of the game. To do this, add the command to play the sound in the create()
function (after setting the sound to loop).
For help with the code for sounds, you can refer back to Step 6 of Practice 1 (or look at your Practice 1 game code).
Add this Phaser command in the update()
function to make the sound louder when the up arrow key is pressed:
Then add a similar command in the update()
function to turn this sound back to normal (0.3
) when the up arrow key is not pressed.
Refresh your HTML preview to verify that the engine sound plays continuously but gets louder when you move the spaceship forward.
Now that you've got a spaceship that can fly around realistically, let's add a realistic background.
Preview the image file called space-stars.jpg in your assets/images folder. This image is 800 pixels in width and 600 pixels in height, which is exactly the same size as your game display.
You can add an image as a static background for your game. In fact, you can add multiple images to build a more complex background for a game.
In this game, we're going to add this image as a special background called a tilesprite. A tilesprite is an image with a repeating texture or pattern. Phaser can move and wrap the tilesprite in the game display to simulate a scrolling background.
Adding a tilesprite to the game involves 3 steps:
Declare a global variable for the tilesprite.
Load the tilesprite's image into memory in the preload()
function.
Add the tilesprite to the game in the create()
function.
Add a global variable named space
for the tilesprite.
Add this Phaser command in your preload()
function:
As you can see, this is similar to commands for loading a spritesheet or sound:
'space'
represents an asset key — again, you get to decide the name of the key, as long as each key has a unique name.
'assets/images/space-stars.jpg'
represents the folder path and filename of the image to load.
Add this Phaser command in your create()
function (before the command that adds the player
sprite):
Let's explain what's inside the parentheses:
0, 0
represent (in order) the x and y pixel coordinates for positioning the tilesprite image. This represents the upper-left of the game. This is normally where you want to position background images.
800, 600
represent (in order) the width and height to use for the tilesprite (which is also the same width and height that we set for the game display — we want to fill up the entire background).
'space'
is the asset key name of the image to use for the tilesprite.
Refresh your HTML preview to verify that the space background appears (but doesn't move yet).
ORDER MATTERS: As you create your game world, all the visual objects — such as images, sprites, text, etc. — are added to the game display in layers (meaning they can overlap other objects behind them). The order in which they are added in the create()
function determines the stacking of these layers in the game world.
So if you have an image that is supposed to be a background for your game, then that image should be added first in the create()
function, so it will be the farthest back layer. Each new visual object (image, sprite, etc.) that is added in the code will appear in front of the previous layers.
For example, if you add the player's sprite first and then add the tilesprite background, the player's sprite will be hidden behind the tilesprite. Try testing this out in your code.
Now let's make the tilesprite scroll as the spaceship moves. In order to make the scrolling look realistic, we need to do two things: 1. Scroll the tilesprite in the opposite direction of the spaceship's movement. So if the spaceship is moving to the right, the space background should scroll to the left. 2. Scroll the tilesprite at a slower speed than the speed of the spaceship. Because the space background represents stars that are in the far distance, they should appear to move slower than the spaceship does.
Add this Phaser code in your update()
function (after the if-else statements for the player movement):
This code changes the position of the tilesprite. As you can see, the x-position and y-position are set independently. You can also see that Phaser breaks down the velocity (speed) of the player.body
into x and y components.
By subtracting the player's velocity (after dividing it by 40), we are moving the tilesprite's position in the opposite direction.
By dividing the player's velocity by 40, we are making the change in the tilesprite position smaller (so the tilesprite moves slower than the spaceship). There's nothing magical about 40 — that number happens to produce results that feel right for this particular game when you fly the spaceship. If you don't divide by 40 (or change 40 to a small number), the background will scroll too fast. If you change 40 to a large number, the background will scroll too slowly.
Refresh your HTML preview to verify that the space background appears to scroll in the opposite direction of the spaceship's movement (which makes the flying seem more realistic).
However, you will notice that the tilesprite scrolling will stop when the spaceship collides with the game world boundaries. This is because the "collision" causes the spaceship's velocity to become zero (which then causes the tilesprite position to stop changing).
We want our game to feel like the spaceship can keep flying forever, so we want the tilesprite to keep scrolling even if the spaceship is at or near the edge.
So to fix this, we're going to add some code to keep the spaceship within the game world without using a collision with the game world boundaries. We'll check the position of the spaceship to make sure it never gets any closer than 50 pixels to any boundary (left, right, top, or bottom). This will keep the spaceship on-screen but not actually change its velocity in the game's memory. (It's a bit of a trick because the spaceship will visually stop moving because we keep resetting its position, but the game will think the spaceship is still moving.)
This extra 50 pixels will also leave us some "blank" space along the edge of the game to add the game's user interface elements (such as the score, etc.).
Add this Phaser code in your update()
function (right before the code that scrolls the tilesprite):
As you can see, Phaser has properties to represent the x-position of a sprite's left
and right
edges, as well as the y-position of a sprite's top
and bottom
edges. We can get or set these properties to check and/or change the sprite's position.
Refresh your HTML preview to verify that the spaceship will stay within the game world (inset a bit from the edge) and the space background will keep scrolling if the ship still has forward velocity.
As the spaceship's speed and direction change (due to your flying maneuvers and the ship's drag), the scrolling of the space background should appear to speed up or slow down accordingly.
Now it's time to add some asteroids to your game.
Phaser allows you to create a Group of game objects, such as sprites, images, text, etc. The objects that you add as members of a group are usually related in some way, such as a group of enemy sprites, a group of platforms or walls, etc.
Typically, the members of a group have many similarities — but they can also have some differences. For example, a group of enemy sprites might use the same spritesheet and move at the same speed, but each enemy sprite is probably in a different location and might be moving in a different direction.
One advantage of using a group is that is makes it easier to perform checks or actions on each member of the group. For example, one line of code could be used to set a property for every member in a group, such as setting the gravity value for each group member. Your code could also quickly loop through each member in a group to check a condition and decide what action to take for each individual member, such as deciding whether each enemy sprite in a group is close enough to the player sprite to fire at it.
Another advantage of using a group is that your code can "recycle" objects in the group (which helps save memory and make your game run faster). For example, if you have a group of enemy sprites and one of the enemy sprites is "killed", your game can reuse that sprite later to create another enemy elsewhere in the game.
You're going to create a group of 10 asteroid sprites for your game.
Adding a group to the game usually involves 4 steps:
Declare a global variable for the group.
If the group members will use an image or spritesheet, load the image or spritesheet into memory in the preload()
function.
Add the group to the game in the create()
function.
Add individual members to the group — usually done in the create()
function (but can also be done in the update()
function).
You can even use different images or spritesheets for the members in a group. For example, you could load and use different spritesheets for different types of enemies in a group. (You could also create separate groups for each type of enemy — it just depends on your game's design and what makes more sense for coding it.)
Add a global variable named asteroidGroup
for the asteroid sprites.
Each asteroid in the group will use the same spritesheet. Preload assets/images/asteroid.png for the spritesheet image. It contains 16 animation frames that are each 40 pixels in width and 40 pixels in height. When these frames are played, it will make the asteroid look like it is spinning. Assign an asset key name to the spritesheet, such as asteroid
.
Add this Phaser command in your create()
function (after the code related to the player):
Similar to what we did for the player
sprite, we will need to enable physics on every asteroid sprite that we add to the group. Rather than needing to do this for each individual asteroid, we can use a single line of code to enable physics for the entire group (even before we add any members to the group).
Add this Phaser command in the create()
function (after the command that added the group):
Now we can start adding members to the group. There are several ways to do this:
Add each member using separate lines of code for each member.
Use a for
loop to quickly add multiple members using the same lines of code.
Do both: use a for
loop to add a set of members, and then use separate code to individually add certain members that have very unique properties.
We're going to use a for
loop to quickly add each of the 10 asteroids to the group. In our game, we want each asteroid to start at a random location at the start of the game. Then we'll also make each asteroid move at a random speed in a random direction.
Add this code in the create()
function (after the command that added the group):
A for
loop contains a set of code within curly braces { }
that will be performed for a certain number of times (that's how it gets its name).
The number of times that the for
loop repeats is determined by the code in the parentheses ( )
after the word for
:
var i = 0;
sets an initial value for a local variable that will be used as a loop counter. Traditionally, you will see i
used as the variable name for the loop counter because it stands for the word "iteration" (which means repetition).
i < 10
sets a condition that is checked at the beginning of each loop — if the condition is true, the loop will occur — meaning the code inside the curly braces { }
will be performed. In this particular case, if the value of i
is less than 10, then another loop will occur.
i++
increases the loop counter by 1 at the end of each loop. (i++
is shorthand for i = i + 1
).
Here's how it would work when the game code runs:
At the start of the for
loop, i
equals 0, and since 0 is less than 10, it will perform a loop — performing the code inside the curly braces.
At the end of the first loop, i
will be increased from 0 to 1. Since 1 is still less than 10, it will perform another loop — performing the code inside the curly braces another time.
At the end of the second loop, i
will increase to 2. Since 2 is still less than 10, it will perform another loop of the code.
This process will keep repeating until i
has increased to 10. At that point, 10 is not less than 10 (duh, they're equal) — so the condition becomes false, which causes the for
loop to stop repeating. The for
loop will have repeated itself exactly 10 times.
Check your understanding: If you wanted to create 15 asteroids in the group, what would you change in the code?
Now let's look at the Phaser commands inside the for
loop curly braces { }
:
The first command creates a new member in the asteroidGroup
and assigns that member to a local variable named asteroid
(local variables only exist inside a particular loop, statement, or function). Inside the parentheses, you indicate (in order) the x and y coordinates of the object's location in the game and then the asset key name to use for the object. In this case, each asteroid will get a random x and random y position and will use the asteroid
spritesheet.
The second command sets the anchor point for the asteroid's position to be the center of the sprite. This command should be familiar to you by now.
Refresh your HTML preview to verify that 10 asteroids appear in random locations in the game. Refresh it a few more times to verify that the asteroids' locations are different every time.
If you fly the spaceship around, you will see that the asteroids remain fixed in location (because you haven't added any code to make them move yet). You'll also discover that the spaceship will fly behind the asteroids. This is because we added the asteroid group after adding the player sprite. If you were to switch the order of their code in your create()
function, the spaceship would fly in front of the asteroids. Let's leave it so that player sprite is added before the asteroid group is added.
Let's add two possible animations for each asteroid: either spinning clockwise or spinning counter-clockwise.
Add a Phaser command inside the for
loop (after the command that sets the asteroid anchor) to add an asteroid
animation named spin-clock
that will play all 16 frames in order (list an array from 0-15) at a frame rate of 16 (meaning it will play all 16 frames in one second) and set it to loop.
For help, look at your code from Step 3 that added an animation for the player
sprite.
Add another asteroid
animation named spin-counter
that lists the 16 frames in reverse order (from 15 down to 0). Use the same frame rate (16) and set it to loop.
Now that you've added the two possible animations for each asteroid, let's randomly pick one to use for each asteroid. We'll make it like flipping a coin — a 50/50 chance.
Add this code inside the for
loop (after the commands that added the asteroid animations):
Remember that Math.random()
generates a random decimal value between 0 and 1. So if the value is less than 0.5, the asteroid will play the clockwise animation. Otherwise, that asteroid will play the counter-clockwise animation. Each animation has a 50% chance of being selected. Once an animation is randomly selected for an individual asteroid, it will keep playing that same animation over and over in a loop.
Refresh your HTML preview to verify that each asteroid spins either clockwise or counter-clockwise.
Let's make each asteroid move at a random speed in a random direction. We'll use the body.velocity
property to do this. If you remember from earlier, Phaser breaks down velocity into x and y components.
Velocity is a combination of speed and direction. Velocity can be either a positive value or a negative value. The speed is determined by the magnitude (size) of the value: lower numbers are slower, higher numbers are faster. The direction of the motion is determined by whether the value is positive or negative:
Positive values for body.velocity.x
move the sprite to the right. Negative values move the sprite to the left.
Positive values for body.velocity.y
move the sprite down. Negative values move the sprite up.
Our game is going to set a maximum possible speed for each asteroid, and then increase this maximum as the game progresses (we'll do this later in Step 9).
Add a global variable called maxSpeed
and assign it an initial value of 100:
Add this code inside the for
loop (after the code that randomly selects the asteroid animation):
As you can see, the x and y components of the velocity are set individually. Each velocity component will be a random number between 0 and 100 (the current value of maxSpeed
). This will be the speed in pixels per second.
Then each velocity component has a 50% random chance of being changed to a negative number:
*= -1
is shorthand for multiply the current value of the variable by -1
, and then assign the result to be the new value of the variable
Refresh your HTML preview to verify that each asteroid moves at a random speed and direction.
You will notice that eventually all the asteroids move off-screen and disappear. That's going to make for a very short and unexciting game.
In the original Asteroids game, if an asteroid crosses a game world boundary, the asteroid reappears on the opposite side of the screen. For example, if an asteroid moves down past the bottom boundary, it will reappear at the top of the screen. Of course, this is not realistic — but for the purposes of the game, it makes it more interesting and challenging because the player has to mentally track where each asteroid will reappear when it wraps around the screen.
Luckily, Phaser has a function named wrap()
built into the game world object to do just this.
Add this Phaser code in your update()
function (after the code that scrolls the tilesprite):
This command will loop through each member of the asteroidGroup
and perform the code listed inside the function's curly braces { }
. As you can see, the code in the function will make each asteroid wrap around the game world. The number 20
represents how many pixels the sprite can move past the game boundary before it will reappear on the opposite side.
In this case, 20
is a good number to use because that's half the size of the asteroid sprite. Because the sprite anchor is set to its center (i.e., half-way), the asteroid sprite will have to move off-screen until it disappears completely before it will start to reappear on the opposite side.
Refresh your HTML preview to verify that each asteroid wraps around to the opposite side of the game if it moves off-screen.
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.
Now that you've added a way for the player to interact with the game, let's add a sound effect as feedback to the player. We'll play a "spinning" sound as the emoji randomly changes.
Adding a sound is similar to adding a sprite:
you declare a global variable for it
then you preload it
then you add it to the game
Add a global variable named spinSound
to your code.
Within your preload()
function, add this Phaser command:
You can see the first part of the Phaser command indicates that you're loading audio into your game. Let's take a look at what's inside the parentheses:
'spin'
represents the asset key for this sound. Again, the key is a reference, similar to a variable name, and you get to decide the name for the key.
'assets/spinner.mp3'
represents the folder path and filename of the sound file that will be loaded. You can only use the following types of sound files in your game: wav, mp3, ogg
Add this Phaser command inside function create()
to add the sound to the game:
This command adds the sound to the game and assigns that sound to the spinSound
variable.
spin
is the asset key for the sound file to use
0.3
represents the volume for this sound, which can be a value between 0 to 1 (with 1 being the file's maximum volume).
Be aware that the sound won't actually play until we use a command telling it to start playing.
By default, when you do play a sound, the sound will play one time and then stop. However, we want our spinning sound to keep playing over and over, for as long as the spacebar is held down.
So add this Phaser command after the command that added the audio:
This will make the sound keep playing in a loop once it's started. The sound will keep looping until we specifically tell the sound to stop playing.
For your game, your code will start playing the sound when the spacebar is first pressed down. The sound will keep playing on a loop as long as the spacebar is still being held down. Then your code will stop playing the sound as soon as the spacebar is released.
Modify your existing code inside the update()
function, so it looks like this:
The game will check to see whether the first condition (spacebar.justDown
) is true. If that condition is true, it will start playing the sound (which will keep playing over and over since we set it to loop) and then skip the other two conditions.
Otherwise, if that first condition is false, then it will check the second condition (spacebar.isDown
). If that condition is true, it will change the sprite to a random frame and then skip the last condition.
Otherwise, if that second condition is false, then it will check the third condition (spacebar.justUp
). If that condition is true, it will stop playing the sound.
Refresh your HTML preview to verify that if you hold down the spacebar, the spinning sound will play — but as soon as you release the spacebar, the sound will stop.
Eventually, this game will have three emoji sprites that the player randomly spins, and the game will award score points for for matching emojis and subtract points if there's no match.
The score will displayed as text in the game display. So let's add this text that will eventually display the score — but in the meantime, we'll just have the text display instructions for how to spin the emojis. Later, we'll update this text to display the score.
Add a global variable named scoreText
to your code.
Add this Phaser command inside function create()
to add text to the game:
This command adds the text and assigns it to the scoreText
variable.
Let's take a look at what's inside the parentheses of this command:
First you provide the x and y coordinates (in order) of the pixel position where the text should be added to the game.
Then you can provide the actual text to display by listing it within quotes: 'Use Spacebar to Spin'
Refresh your HTML preview to verify that the text appears in the game.
Just like when we first added the sprite, the text is not centered because its top-left corner represents its x and y position. Let's fix that by setting a different "anchor" point for the positioning of the text.
Add a command to change the text's anchor point to be the center of the text. (Hint: How do you do this previously for the hello1
sprite?)
Refresh your HTML preview to verify that the text is now centered horizontally below the emoji sprite.
Now you're going to add the other two emojis to your game, so you can start building the "matching" part of the game.
In your create()
function, modify the existing command that adds the hello1
sprite, so it looks like this instead:
Refresh your HTML preview to verify that the sprite is now shifted slightly to the left of the game's center.
Now you'll add two more emoji sprites. These will use the same spritesheet as the first emoji sprite. Your code only needs to load this spritesheet one time — you'll just reuse the same spritesheet by referring to its asset key when you add each of the other sprites.
Create two more global variables called hello2
and hello3
.
Within your create()
function, add the hello2
sprite to the center of the game, and add the hello3
sprite 100 pixels to the right of the game's center. Remember that both of these new sprites will just reuse the existing 'hello'
spritesheet.
Be sure to set the anchor point for each new sprite, similar to what you did for the hello1
sprite.
Refresh your HTML preview to verify that you have 3 emoji sprites that are equally spaced in a row across the center of your game.
If you press down the spacebar, only the first sprite (which is hello1
) will randomly change, while the other two sprites stay the same. You'll fix this in the next step.
Now that you have all 3 emoji sprites added, add code within your update()
function to make hello2
and hello3
also change to a random frame whenever the spacebar is down.
Refresh your HTML preview to verify that each of the 3 emoji sprites changes randomly when the spacebar is pressed — and they keep changing randomly as long the spacebar is held down.
We want to add a score to the game, so the player will be awarded points if there are matching emojis after a spin.
Create a global variable named score
and assign it an initial value of zero for the start of the game:
You'll use score
to actually keep track of the numerical score during the game, and then use scoreText
to display the score
on the game screen.
Now that you have a score
variable, you need to add code to check for emoji matches after each spin and then update the score. You're going to add this code inside your own custom function (so you can practice making a new function).
As you've seen, a function contains a set of related code statements that perform a specific task or function, such as preloading game assets, etc.
Add the following JavaScript code to the very bottom of your code.js file (after the update()
function):
This creates a new function in your JavaScript. Right now, this function is empty — it has no code between its curly braces { }
— but you'll be adding code inside the function in just a little bit.
As you can see, checkMatch
is the name of the function. Just like variable names, you get to decide the name of the function, as long as every function in your code has a unique name. Functions always have a set of parentheses ()
at the end of their name. Sometimes there are variable names called parameters listed inside the parentheses — but we don't need parameters for this particular function.
Let's figure out the code to add inside the checkMatch()
function. As soon as the spacebar is released, we want to check to see if any of the emoji sprites match each other.
The easiest way to check for matches is to compare the frame numbers of the sprites. If they currently have the same frame number, that means they are displaying the same exact emoji.
We want to first check to see if all 3 sprites match. If they do, we'll add 100 to the score as a reward. In JavaScript (like other programming languages), you can only compare two variables at a time, so we'll need to combine multiple comparisons into one conditional statement. So if hello1
and hello2
have the same frame AND hello2
and hello3
have the same frame, then all 3 match. (We don't even need to check hello1
vs. hello3
because they will logically match if the other two comparisons were true.)
Otherwise, we want to check to see if any 2 of the sprites match. If they do, we'll add 20 to the score as a reward. There are three possibilities for a double match: hello1
and hello2
match OR hello2
and hello3
match OR hello1
and hello3
match. Again, we'll combinine these multiple comparisons into one conditional statement.
Otherwise, if both of these checks were false, it means none of the sprites match (all three are different emojis). In that case, we'll subtract 10 from the score as a punishment.
Lastly, regardless of which of these is true (all 3 match, any 2 match, or none match), we will need to update scoreText
to display the new score.
Add this code inside function checkMatch()
by pasting it between the curly braces { }
:
Let's examine this code:
&&
represents AND. By using &&
to combine multiple comparisons into one condition, the condition will be true only if every comparison in the set is true.
||
represents OR. By using ||
to list multiple comparisons within one condition, the condition will be true if any one (or more) of the comparisons is true (even if the others are false).
Hint: You can type |
by pressing shift-backslash on your keyboard
Notice we first checked for a triple match, and then checked for a double match. If we reversed the order of these checks, the code would treat 3 matching emojis as a double match. (Do you understand why?)
You can change the value of a variable, such as score
, by using a single equals sign =
. You can even refer to the previous value of the variable. In our case, the code assigns a new value to score
by taking its previous value (which is saved as score
) and then adding or subtracting a number.
You change the text being displayed by assigning a new value to the text
property of your text variable. In our case, we just want to display a number (the player's score
). If you wanted to display some actual text, you would list it inside quotes: scoreText.text = 'Triple Match!';
The checkMatch()
function won't actually run until and unless we tell the game to do so. You run a function by "calling it" — which simply means listing its name with a set of parentheses. (If the function happened to require parameters, then you'd also list parameters within the parentheses — but your function doesn't require these.)
Since we want to perform the check for matches as soon as the emojis stop changing, add this line of code within your update()
function, so that it will call your custom function when the spacebar has just been released:
The game will recognize that this is the name of one of your custom functions — so it will go and perform all the code listed inside that custom function, and then it will return back to where it was.
Refresh your HTML preview and play the game to verify that it correctly identifies when a match occurs and correctly awards points based on a triple match, double match, or no match.
Your game should be functioning correctly at this point. The score should be changing, though it may be a little challenging to determine if it is changing accurately. We'll fix this in the last step, so the game provides clear feedback to the player.
For this last step, you'll add some things to improve the game. You'll make it easier for the player to understand what's happening and also make the game a little more fun.
As some visual feedback to the player, let's change the game's background color to a random color after every spin. It helps make it clear when a "spin" has ended — plus it adds another interesting random element to the game.
Add this Phaser command within your update()
function, so that it will occur when the spacebar has just been released:
Refresh your HTML preview to play the game and verify that the game background changes to a random color after each spin.
You will notice that the white color of the scoreText
can be hard to read when the background color randomly changes to a light color. To help fix that, let's add a shadow behind the text to make it easier to read.
Add this Phaser command in your create()
function after the command that added scoreText
to the game:
Refresh your HTML preview to verify that the text shadow is visible.
Okay, let's add sound effects for a triple match and a double match.
Create two more global variables called match2Sound
and match3Sound
.
Within your preload()
function, load the sound files in your assets folder called coin.wav and power-up.wav. Be sure to assign a unique asset key name to each sound (use something that makes sense to you).
Within your create()
function, add the sounds to your game. Use coin.wav for match2Sound
, and use power-up.wav for match3Sound
. Set a volume for each sound (which you can modify later after testing them out in the game). Since we only want these sounds to play one time whenever we do play them, you don't need to adjust their loop
property (it will be set to false
by default).
Within your checkMatch()
function, add a command to play match2Sound
when a double match occurs, and add another command to play match3Sound
when a triple match occurs.
Refresh your HTML preview to play the game and verify that the correct sound plays whenever a double match or triple match occurs (and no sound plays if there's no match).
Your final addition to the game will be some text feedback after each spin to confirm whether a triple match, double match, or no match occurred.
Create a global variable called matchText
.
Within your create()
function, add matchText
to the game by positioning it 120 pixels below the center of the game. Set the text to read Match 2 or 3 to Win
, and use the same font styling as you did for scoreText
.
Add a command to change the matchText
anchor point to be the center of the text.
Add a command to add a text shadow to matchText
(use the same shadow settings as you did for scoreText
).
Within your update()
function, add a command to do the following:
When the spacebar is first pressed down, clear out the text in matchText
. You do this by assigning an empty string (quotes with nothing inside) to its text
property, like this:
Within your checkMatch()
function, add commands to do the following:
When a double match occurs, change matchText
to read Match Two +20
, and change its text color to green (#00ff00
) by changing its fill
property, like this:
When a triple match occurs, change matchText
to read Match Three +100
, and change its text color to green.
When no match occurs, change matchText
to read No Match -10
, and change its text color to red (#ff0000
).
Refresh your HTML preview to play the game and verify that the correct feedback text appears after each spin.
What else do you think could be added or changed to improve this game? Why?
Congratulations, you've completed your first practice game! Hopefully, the Phaser commands made sense — you'll get the chance to use them again in the next practice game.
In the previous assignment, you explored the use of external motivations, such as rewards and punishments, in games. In this assignment, you'll explore internal motivations in games.
People naturally want to avoid feeling bad, such as having a fear of failure, avoiding embarassment, etc. This negative internal motivation is rooted in avoiding fear, anxiety, and unhappiness. (Avoiding feeling bad is not necessarily the same as feeling good. It may mean feeling neutral — and probably after already experiencing some fear, anxiety, or unhappiness.)
Research has shown that positive internal motivation is much more effective in the long-term for encouraging sustainable behavior. People naturally want to do things that make them feel good. Obviously, there is a variety of ways to feel good: having fun, feeling relaxed, feeling excited, feeling a sense of satisfaction, being mentally engaged, etc.
When doing certain tasks, people can sometimes experience a mental state of deep engagement called flow. This is sometimes referred to this as "being in the zone". It could be considered the ultimate form of positive internal motivation.
People can experience flow in various types of tasks, including: art, music, sports, games, work, etc. However, experiencing flow requires certain conditions.
When you are experiencing flow:
You are completely focused on the task (e.g., you might even forget to eat, etc.)
You have a feeling of control (e.g., you know what you need to do, you feel confident, etc.).
You aren't thinking about yourself (e.g., your worries melt away, etc.).
You lose awareness of time (i.e., usually time seems to fly by — hours can seem like minutes).
You enjoy doing the task for its own sake (i.e., positive internal motivation).
Conditions needed to experience flow include:
Avoiding distractions
Having clear goals
Receiving clear immediate feedback
Having proper balance of challenges vs. skills
Challenge plays a critical role in whether or not doing a task can lead to flow. This diagram shows how flow is affected by the balance of challenges vs. skills:
If a challenge is too high compared to your current skills, it leads to anxiety — and you'll probably stop doing the task.
If a challenge is too low compared to your current skills, it leads to boredom — and you'll probably stop doing the task.
If a challenge is well-matched to your current skills, it leads to focus and a sense of control — and you'll probably stay engaged in the task. You're in the flow state.
Over time, your skills tend to increase, so the challenges also need to increase in order to keep you in a flow state.
As a game designer, you can make sure your game has the right conditions to encourage flow:
Eliminate distractions in the game that aren't related to the intended gameplay. For example, excessive or unnecessary visual effects or sound effects might be distracting. Glitches or unresponsiveness in the game can distract players.
Make sure your game provides clear goals and feedback to the player. Make it easy for the player to understand the game's objectives and controls. (This is why many games have tutorial modes in the beginning.) Use sound effects, animations, and the user interface to provide feedback to the player (but avoid becoming distracting).
Make sure your game appropriately balances challenges vs. skills. Avoid or limit gameplay that's too easy or too hard. Increase the challenges in the game as the player's skills progress. This may involve making the challenges more difficult or introducing new challenges. Games that require repeated practice for players to improve can lead to flow.
On the other hand, some games are specifically designed to to be very hard to promote a constant state of anxiety (e.g., Flappy Bird), while other games are designed to be easy in order to provide a relaxing experience. This is obviously fine — not every game has to necessarily provide a flow experience.
Nonetheless, incorporating flow conditions — such as clear objectives, clear feedback, appropriate challenge, etc. — will typically improve your game's design, regardless of whether your game actually leads to a flow state.
Discuss your findings as a class.
Be sure that you completed Coding Steps 1-5 on the previous page.
IMPORTANT: Take the time to read each step, and try to understand how the code works.
Let's make the game respond if the spaceship collides with any of the asteroids.
Phaser's Arcade Physics system has two methods (functions) to detect when two game objects "touch" each other:
collide()
checks to see if the two objects are colliding and keeps them separated (so they don't overlap) — if one or both of the objects is moving, they can transfer momentum between each other (if you set a body.bounce
value for the objects, they'll bounce off each other)
overlap()
simply checks to see if the two objects are overlapping (but the objects will pass right through each other)
The two objects being checked can be individual objects and/or groups of objects, such as: Sprite vs. Sprite, Sprite vs. Group, Group vs. Group. For groups, each member of the group is checked individually in sequence.
Add this Phaser command in your update()
function (before the if-else statements for the player movement):
This command tells the Arcade Physics system to check for collisions between the player
sprite and any sprite in the asteroidGroup
.
Refresh your HTML preview to verify that the spaceship can collide with the asteroids (and it will change their velocity).
You probably noticed that the spaceship and asteroids act like they collide before they touch. This is because the player sprite and each asteroid sprite are actually rectangles that have transparent areas. The transparent areas of the sprites are colliding, so it seems like they collide too soon.
Luckily, Arcade Physics has two ways that we can change the collision area of a sprite's body
to more closely match its visual appearance:
body.setCircle()
allows you to change the sprite's collision area to be a circle of a specified radius (which you can offset from the top-left corner of the sprite)
body.setSize()
allows you to change the sprite's collision area to be a rectangle with a specified width and height (which you can offset from the top-left corner of the sprite)
Since the asteroids are basically circles, let's set their collision areas to be circles.
The asteroid sprite frames are 40 pixels in width and 40 pixels in height. If viewed in an image editor, you would see that the asteroid is about 30 pixels across in diameter with about 5 pixels of transparent area on all sides:
The pink circle drawn above represents the circular collision area that we want each asteroid to use (instead of the full image).
Add this Phaser command inside the for
loop that adds the asteroids to the asteroid group (after the code that sets the anchor for each asteroid):
The setCircle(15, 5, 5)
sets the body collision area to be a circle with a radius of 15 pixels (30 pixels in diameter) that is offset from the left edge of the frame by 5 pixels in the x-direction and offset from the top edge by 5 pixels in the y-direction. This offset will center the circle within the frame.
Let's do something similar with the spaceship, which has frames that are 64 pixels in width and 64 pixels in height. Visually, the spaceship is not a rectangle, and it's not a circle — it's more like a triangle. However, triangle is not an option for the body collision shape. Let's just use a circle because it's close enough (besides the ship rotates in a circular motion):
The pink circle drawn above represents the circular collision area that we'll use for the spaceship. It's not a perfect match, but it's better than the using the full image for collisions.
Add a Phaser command to set the body collision area of the player
sprite to be a circle with a radius of 20 pixels that is offset by 12 pixels from the left of the frame and 12 pixels from the top of the frame. Add the command in the create()
function (after the command that enables physics on the player
sprite).
Refresh your HTML preview to verify that the spaceship and asteroids are now visually closer when they collide with each other.
What should really happen in our game when the spaceship collides with an asteroid is that the spaceship should be damaged or destroyed and the asteroid should be destroyed.
Modify your existing collide()
statement in the update()
function, so it looks like this:
This command tells the game to run a custom function named collideAsteroid
when the physics system detects a collision between the player
sprite and any sprite in the asteroidGroup
. (null, this
are two additional parameters that you'll always include). The two specific sprites involved in the collision will be passed into the custom function as parameters (variables).
Now you have to actually create a custom function named collideAsteroid
, and add code inside to do something when this type of collision occurs.
Add this code to the very end of your code.js file (after the update()
function):
This creates the custom function, though it has no code yet inside its curly braces { }
.
Notice that we included player, asteroid
inside the parentheses after the function's name. These represent the parameters — variables that are passed into the custom function when it is called. The parameters represent the specific sprites involved in the collision, in the same order they are listed in the game.physics.arcade.collide()
function.
The parameter names act as local variables inside the custom function, so the names of the parameters do not have to exactly match the sprite names in the collide()
function. However, it will be much easier to understand what's happening in the custom function if the names are either identical or similar. Inside the custom function, whatever you do to the parameter variables will affect the sprites they represent.
Within the custom function collideAsteroid()
, the parameter player
represents the player
sprite (easy to understand because both use the same name). The parameter asteroid
represents the specific asteroid sprite from the asteroidGroup
that collided with the player
sprite. (We could use ship
as the parameter name to represent the player
sprite and rock
as the parameter name to represent the sprite from the asteroidGroup
— and the custom function would work exactly the same. However, the code might seem less clear to us.)
Alright, enough of that — let's destroy the asteroid involved in the collision.
Add this Phaser command inside the collideAsteroid()
function by pasting it between the curly braces { }
:
The kill()
command removes a sprite from the game. Phaser actually keeps the sprite object saved in memory, in case you decide to reuse the sprite later. Phaser has commands to revive()
or reset()
a "dead" sprite to add it back into the game.
Refresh your HTML preview to verify that each asteroid will disappear if it collides with the spaceship.
Now let's add a sound effect that will play one time whenever a collision occurs:
Use boomSound
as the name of the global variable for the sound.
Use assets/sounds/boom.wav for the sound file. Assign a unique asset key name to the sound, such as boom
.
Add boomSound
to the game with a volume of 0.3
.
Add a Phaser command inside the collideAsteroid()
function to play boomSound
.
Refresh your HTML preview to verify that the explosion sound plays whenever an asteroid collides with the spaceship.
Add a Phaser command inside the collideAsteroid()
function to kill the player
sprite.
Let's also add a visual effect as feedback to make the collision feel more realistic.
Add this Phaser command inside the collideAsteroid()
function:
This command will cause the game screen to "shake" briefly when a collision occurs:
The first number 0.02
represents the intensity of the camera shaking as a percentage of the camera size (0.02 = 2%).
The second number 250
represents the duration of the shaking in milliseconds (1000 milliseconds = 1 second).
Refresh your HTML preview to verify that the game display "shakes" and the spaceship disappears when it collides with an asteroid.
Let's add an explosion effect when the player
sprite is killed. The explosion will be an animated sprite that we'll hide from view until needed.
Use explosion
as the name of the global variable for the sprite.
Use assets/images/explosion.png for the spritesheet image. It contains 16 animation frames that are each 128 pixels in width and 128 pixels in height. Assign a unique asset key name to the spritesheet, such as explosion
.
In your create()
function (after the for
loop that added the asteroids), add the explosion
sprite to position 100, 100
of the game (this will just be a temporary position), and set the sprite's anchor to its center.
Add an animation to the explosion
sprite named explode
that will play all 16 frames in order (list an array from 0-15) at a frame rate of 30, with loop set to false
.
Refresh your HTML preview to verify that the first frame of the explosion sprite is visible in the upper left of the game screen.
We're actually going to hide the explosion
sprite until we need to display it (and then we'll move it to the player
sprite's position when it's killed).
Add this Phaser command to your create()
function (right after the command that added the animation for the explosion
sprite):
Refresh your HTML preview to verify that the explosion sprite is no longer visible.
When the player
sprite is killed, we will move the explosion
sprite to the same position where the player
was located, make the explosion
sprite visible, play the explode
animation, and then remove the explosion
sprite again.
Add this Phaser code in your create()
function (after the code that added the player
animation, but before the code that added the asteriodGroup
):
This code adds an event when the player
sprite is killed. This event will perform the code listed inside the function's curly braces { }
:
reset()
makes the explosion
sprite visible again and moves it to the x-y coordinates listed inside the parentheses. In this case, we want to make it appear at the player
sprite's position.
The command to play the animation includes an extra argument at the end:
explode
is the animation key name
30
is the frame rate
false
means do not loop the animation (play it only once)
true
means kill (remove) the sprite after the animation has ended
Refresh your HTML preview to verify that the explosion animation will appear (and then disappear after playing) when and where the spaceship collides with an asteroid.
Let's give your player
a way to fight back. It's time to add a weapon.
The "bullets" for a weapon can be any image (or spritesheet) that you want: bullets, lasers, arrows, fireballs, etc. If you want the weapon to fire smiley face emojis, you can do that.
Adding a weapon to the game involves 3 steps:
Declare a global variable for the weapon.
Load an image (or spritesheet) for the bullets into memory in the preload()
function.
Add the weapon to the game in the create()
function.
The weapon can then be fired in the update()
function.
Let's get ready to add your weapon:
Add a global variable named laser
for the weapon.
Preload assets/images/laser.png for the bullet image. Assign a unique asset key name to the image, such as bullet
.
Remember that the order in which visual objects are added in the create()
function determines which objects appear in front of (or behind) other objects. We want the weapon's bullets to look like they are being fired from beneath the spaceship, so we need to add the weapon before the player
sprite is added in the code.
Add this Phaser code in your create()
function (before the code that adds the player
sprite):
Let's examine this code:
The first line adds the weapon. 10
represents the number of bullets to create in the bullet group. The bullets will be recycled as they are fired, so this sets an upper limit on the maximum number that could be on-screen at any one time. 'bullet'
is the asset key name that it will use for each bullet.
The third line sets the bullet speed in pixels per second.
The fourth line sets a limit on the firing rate. In this case, a bullet can only be fired every 250 milliseconds (even if the player holds down the fire key).
The last line changes the bullet's collision area to be a rectangle with a width of 24 pixels and height of 12 pixels offset 6 pixels from the left edge of the bullet image and offset 6 pixels from the top edge of the bullet image.
We want the laser
weapon to be associated with the player
sprite. Phaser has a method for the weapon to track (follow) another sprite, so the bullets will be fired from that sprite's position.
Add this Phaser command in your create()
function (after the code that added the player onKilled
event):
This command will ensure the laser
weapon tracks the position of the spaceship:
player
represents the name of the sprite that the weapon will track
0, 0
represents an x-y offset from the sprite's anchor position. Using 0, 0
means the weapon will fire from the player
sprite's center.
true
means that the weapon should also track the sprite's rotation. We need this for our game since the ship rotates, but other games may not need this (so they could list false
or nothing at all).
Now we just need a way to fire the laser
weapon. Remember that way back in Step 2, you assigned the spacebar as an input named fireKey
.
Add an if
statement in your update()
function (after the if-else statements for the player movement) to check when the fireKey
is being pressed.
Add this Phaser command to your code, so it will be performed when your new if
statement is true:
Refresh your HTML preview to verify that your spaceship fires the laser weapon when the spacebar is pressed.
You'll see that the laser "bullets" don't do anything yet — we'll have to add a collision between the laser bullets and the asteroids.
You'll also discover that the laser can still be fired even after the player
sprite has been killed.
Modify your existing if
statement for the fireKey
, so it will check whether the fireKey
is being pressed AND the player exists.
Remember that &&
is used to represent AND
Command to check if player exists (returns true or false): player.exists
Refresh your HTML preview to verify that the laser weapon can only be fired when the spaceship exists.
Let's add a sound effect that will play whenever the weapon is fired:
Use fireSound
as the name of the global variable for the sound.
Preload assets/sounds/fire.wav for the sound file. Assign a unique asset key name to the sound.
Add fireSound
to the game with a volume of 0.1
. Add the sound after the code that set the laser
to track the player
sprite.
Add this Phaser code inside your create()
function (after the code that added fireSound
):
This code will play the fireSound
every time the laser
weapon is fired. The reason for playing the sound using this code is the weapon has a fireRate
that limits how frequently it can fire. If we played the sound whenever the fireKey
is being pressed, the sound would play too frequently (and would play even when a new bullet is not actually fired). So the code above will keep the sound effect synced with the actual weapon firing.
Refresh your HTML preview to verify that the sound effect plays whenever the laser weapon is fired.
In your update()
function (directly after your existing collide()
function), add a new Arcade Physics collide()
function to check for collisions between the laser bullets and the asteroids:
Check for collisions between laser.bullets
and asteroidGroup
. List these two sprite groups in this order.
When a collision is detected, it should run a custom function called shootAsteroid
.
Add a new custom function named shootAsteroid()
(after your collideAsteroid()
function). Inside the parentheses ()
after the function name, list bullet
and asteroid
as parameters (in that order).
Inside the curly braces { }
of the shootAsteroid()
function, add Phaser commands to:
kill the asteroid
kill the bullet
play the boom sound effect
Refresh your HTML preview to verify that the asteroids are destroyed when they are hit by the laser.
We have an explosion animation that plays when the player
sprite is killed, so let's add a particle effect to show the asteroid breaking up into pieces when it's destroyed.
Adding a particle emitter to the game involves 3 steps:
Declare a global variable for the particle emitter.
Load an image (or spritesheet) for the particles into memory in the preload()
function.
Add the particle emitter to the game in the create()
function.
The particle emitter can then be activated in the create()
function or update()
function, depending on what you need for your game. The particle emitter can be a single point or can be an area of a specified width and height.
Let's get ready to add your particle emitter:
Add a global variable named asteroidParticles
for the particle emitter.
Preload assets/images/asteroid-particle.png for the particle image. Assign a unique asset key name to the image, such as particle
. This particle image is just a scaled down version (20 pixels by 20 pixels) of the first frame from the asteroid spritesheet:
Add this Phaser code in your create()
function (after the for
loop that added the asteroids, but before the code that adds the explosion
sprite):
Let's examine this code:
The first line adds the particle emitter to the game. 0, 0
represent the x and y coordinates where the particles will be emitted from. (Later, we'll change the x and y coordinates to match the x and y coordinates of the asteroid being killed.) 50
represents the maximum number of particles to create in the emitter group. We won't be using all 50 for a single particle explosion — we just want to have enough available in the game's memory, in case we need multiple particle explosions on-screen at the same time.
The second line adds the particle objects to the emitter group using the provided asset key name.
The third line sets the gravity
property of the particles to zero (since we don't want any gravity for our particular game). By default, particles will have a gravity value of 100, unless you change it.
The fourth line will make the particles fade out of view from an alpha of 1
(opaque) to 0
(transparent) over 1000
milliseconds (1 second).
When an asteroid collides with the spaceship or with a laser bullet, we want to create an "explosion" of particles at the asteroid's position.
Add this Phaser code in your collideAsteroid()
function (right before the asteroid.kill()
command):
Add the same code in your shootAsteroid()
function (right before the asteroid.kill()
command).
Let's examine this code:
The first two lines set the x and y position of the asteroidParticles
emitter to be the same as the x and y position of the asteroid involved in the collision.
The third line creates an explosion effect from the particle emitter. 1000
represents the lifespan of the particles in milliseconds (1000 ms = 1 second), and 5
represents the number of particles to emit.
The particles will be emitted in random directions with slightly different speeds, so the explode()
effect looks slightly different each time.
Remember in the create()
function that we set our particles to fade out over 1000 milliseconds. Since we also set our particles to have a lifespan of 1000 milliseconds during the explode()
effect, they will be removed as soon as they fade out.
If we didn't fade out the particles, they would disappear abruptly after 1000 milliseconds, which would look odd. The fading makes the particle explosion seem more natural.
Refresh your HTML preview to verify that a particle explosion occurs when the asteroids are destroyed.
Our game is not going to last very long if there are only 10 asteroids to shoot. As asteroids are destroyed, the game should periodically spawn (generate) new asteroids to replace them, so the game can keep going.
As part of our game's design, we'll keep it so there are never more than 10 asteroids at any one time. (Of course, we didn't have to design our game that way.)
Here are a few different options for when to spawn new asteroids:
Immediately spawn a new asteroid whenever an existing asteroid is destroyed
Use a timer to spawn a new asteroid after an existing asteroid is destroyed (such as: making new asteroid appear 5 seconds after one is destroyed)
Use a random number to decide when to spawn a new asteroid after an existing asteroid has been destroyed (i.e., timing of new asteroid will be random)
Let's go ahead and use a random number for our asteroid spawning. It will add some variation and unpredictability to the asteroid spawning.
Add this Phaser code inside your update()
function (after the code that makes the asteroids wrap around the game world):
There is some missing code that you will need to insert, but let's first explain how the provided code works:
Each time a loop of the update()
function occurs, the game will generate a random number (which will be a decimal value between 0 and 1). If the the random number is less than 0.02
, then it will check for a dead asteroid. This means that each loop of the update()
function has a 2% chance of checking for a dead asteroid. Even though 2% is a very low chance, the game loop runs many times per second, so it doesn't take that much time for a dead asteroid check to occur — however, the amount of time between each dead asteroid check will be random, which is what we wanted.
When the random number is less than 0.02
, the code will check for the first dead (killed) sprite in the asteroidGroup
and assign that dead sprite to a local variable called asteroid
. If there aren't any dead sprites at the moment, then asteroid
will be assigned a value of false
.
The next check is whether asteroid
is true
— meaning was there a dead sprite? If so, then it performs the rest of the code (which will reset the asteroid
sprite, so it is "alive" again).
The tween is added to the asteroid
sprite, and it is a to
tween.
Inside the curly braces { }
, it lists all the properties of the sprite that will be tweened. In this example, we're just going to change the alpha
property (which represents the sprite's transparency) from its current value (which we set to 0
in the previous line of code) to a new value of 1
(which is opaque).
500
represents the time period (in milliseconds) for the tween. So it will take 0.5 seconds for the alpha
property to change from its current value to its new value.
true
tells Phaser to start the tween automatically. (Otherwise, you can use false
to set up a tween without running it, and then use a start()
command to run the tween.)
Now it's time for you to add the missing code inside the curly braces of the if (asteroid)
statement:
Add a Phaser command to reset()
the asteroid
at a random X-position and random Y-position in the game world. (Hint: Look back at Step 6 to see how you reset the explosion sprite, and look at Step 5 to see how you can pick a random X and Y position in the game world)
Add Phaser code to give the asteroid a random speed and direction. (Hint: How did you do this in Step 5?)
Because we are "recycling" a dead asteroid sprite from the asteroidGroup
, the "new" asteroid will still have the same properties as the original sprite — unless we change those properties.
So we did give the asteroid a new random starting position and a new random speed and direction.
However, notice that we did not have to add or change several other existing properties for the asteroid:
We didn't need to set the anchor position for the asteroid. It will still use its same anchor position that was set when it was first added to the asteroidGroup
in the create()
function.
We also didn't need to change the asteroid collision area to be a circle (because it was already set to a circle when it was first created).
We also didn't have to add animations to the asteroid because it still has them from before. The sprite will also continue to play the same animation (spin-clock
or spin-counter
) that it was already playing before.
Refresh your HTML preview to verify that "new" asteroids will randomly spawn and fade in after other asteroids are destroyed.
Now your game will keep recycling the same 10 sprites in the asteroidGroup
, so the gameplay can continue for as long as the player can stay alive.
Now that our game will keep spawning new asteroids as the player shoots them, let's have the game become more challenging as the player progresses.
Every time the player shoots an asteroid, let's increase the asteroid maxSpeed
by a small amount. This won't change the speed of any existing asteroids. However, it will affect the possible speed of new asteroids.
In your shootAsteroid()
function, add a line of code to increase the value of maxSpeed
by 1
:
Refresh your HTML preview to verify that some of the new asteroids seem to be faster as the game progresses.
The change in the maximum possible speed is small, so it would require destroying at least 50 or more asteroids for it to start to become obvious that some of the new asteroids are faster — but don't worry, the asteroids will definitely get faster as the game progresses.
If you want to really speed things up, you could temporarily change the line of code to increase the maxSpeed
by 10 — and then test it out to verify that it does affect the speed of new asteroids.
Let's give the player some score points for every asteroid destroyed by a laser.
So we'll need a variable to keep track off the player's score, and we'll need to display the score as text in the game display.
As a reminder, adding text to the game involves 2 steps:
Declare a global variable for the text.
Add the text to the game in the create()
function.
Once the text has been added, you can use the variable's text
property to change what the text displays. This is typically done in your update()
function or in a custom function.
Here's what you need to do:
Declare a global variable called score
and assign it an initial value of 0
Declare a global variable called scoreText
In your create()
function (after the code that adds the keyboard inputs), add scoreText
as a text object at position 20, 20
. Set the text to display: 'Score: ' + score
(which will display the text in the quotes followed by the current value of the score
variable). Use the same font styling that you used in Practice 1 (Arial, 20px, bold, #ffffff)
You do not need to set the text's anchor or add a text shadow.
If you need hints, look back at Step 7 of Practice 1.
Refresh your HTML preview to verify that the score (with a value of zero) appears in the upper left of the game.
Now we need to increase the score every time the player shoots an asteroid.
Add Phaser commands in your shootAsteroid
function to do the following (in order):
Increase the value of score
by 250
Change the text
property of scoreText
by assigning it a value of: 'Score: ' + score
If you need hints, look back at Step 9 of Practice 1.
Refresh your HTML preview to verify that the score increases by 250 every time you shoot an asteroid.
RESOURCE:
RESOURCE:
Notice that we're using a series of . The game will check these conditions in the order listed.
Then within a set of curly braces { }
, you have the option of listing style properties for the text. This example lists a font, a font size, a font style, and a fill (i.e., text color, which will be a CSS hex color code). You don't have to list all of these, but usually you'll want to provide the font, the font size, and the fill color. The Phaser API reference identifies .
RESOURCE:
When you are to see if they are equivalent to each other, you have to use double equals signs ==
. This is because JavaScript (like most other programming languages) uses a single equals sign =
to assign or change the value of a variable.
This will add a small dark shadow behind the white text, making it a little easier to read on light-colored backgrounds. If you want to learn more about the settings inside the parentheses, here is the Phaser API reference for , which explains it in detail.
As a type of play, games are supposed to be fun. As we've seen, what seems fun to one person may not be fun to another. If you haven't already figured it out, the (such as: competition, community, challenge, power, story, discovery, etc.) are examples of positive internal motivations — certain people find these types of experiences to be fun.
VIDEO: and how game designers can encourage flow.
Follow the instructions and links in to playtest two games to compare their use of flow conditions.
Phaser's Arcade Physics system includes a object with properties and methods related to firing bullets. The weapon is really just a group of bullet objects.
The second line will kill (remove) a bullet if it leaves the game display boundaries. Phaser has multiple .
There are many other that you could use in other games, if needed.
Phaser's Arcade Physics system includes a object with properties and methods related to emitting particles. An emitter is just a group of particle objects (similar to how a weapon is just a group of bullet objects).
Particle emitters are useful for one-time events (such as , etc.) or continuous effects (such as , , flames, etc.).
Notice the tween
command at the end. A is a command that allows you to change one or more properties of an object over a specified period of time. Tweens are very useful for adding different types of automated effects in your game. In this case, the tween will make the new asteroid fade into view by changing it from transparent to opaque over 0.5 seconds:
The change in the property will follow a pattern called Phaser.Easing.Cubic.Out
(which basically means it will change faster at first but then slow down — making this particular tween seem more "natural"). Phaser has different types of that can be used for tweens.
A higher maxSpeed
means each new asteroid has the potential to be slightly faster than the previous asteroids. Since the speed of each new asteroid is calculated as a random value (up to the maxSpeed
), some of the new asteroids will still be slow — but over time (as the player keeps shooting asteroids), some of the new asteroids will be much faster — increasing the challenge of the game in response to the player's skill. (Our game's attempt at promoting .)
Your team needs to gather outside feedback on your game treatments. The feedback will be used to help decide which game concept to prototype and what design improvements it might need.
The teams in the class will pair up to provide feedback on each other's game treatments through a design critique.
VIDEO: Tips for Design Critiques
Keep the design goals in mind when reviewing work.
Ask questions to better understand the design and to spark thinking about possible opportunities for improvements.
Make sure feedback is objective and specific — and explain the reasons for the feedback.
Be sure to identify what works well — as well as what could be improved.
The purpose is help improve the design, so it will better meet the design goals.
Be sure the design goals are presented along with the design work.
Be prepared to answer questions and to explain your design thinking.
Listen — keep an open mind, don't take feedback personally, and don't get defensive.
Ask your own questions to clarify feedback or to get feedback on specific items.
Take notes, so you can follow up on feedback later.
Teams should pair up, in order to provide feedback on each other's game treatments.
Critique one game treatment at a time (about 10 minutes per game treatment):
The reviewers have 3-4 minutes to read and think about the game treatment.
The reviewers then have 5-7 minutes to provide feedback and ask questions.
During this time, the design team can respond and can ask follow-up questions.
The design team should have someone recording notes on the feedback.
Then the teams switch roles to review the next game treatment. Repeat until all game treatments for both teams have been reviewed.
Your team should used the review feedback to help evaluate the game treatments, in order to select one game concept that your team will prototype for its project.
Now that your team has brainstormed a set of possible game concepts, your next task to refine and evaluate these to select your team's top 3 game concepts. Then you'll create a game treatment for each of your top 3 game concepts.
A game treatment is a brief written summary of a game concept that includes sample sketches.
In the gaming industry, game treatments are used to get feedback on game concepts, as well as to pitch game concepts to get approval and funding to create them.
The written summary typically includes:
Possible game title
Brief description of the game's story (premise, setting, characters, and story). Even if a game doesn't have characters or a story (e.g., a puzzle game, like Tetris), it still has a premise and setting.
Brief explanation of the game mechanics (objective, conflict or challenge, basic gameplay)
Brief description of the gameplay progression from start to end. For example, how does the game begin? What happens as the game progresses? How does the game end?
The sample sketches typically show:
Possible layout of the game world or level
Possible appearance of characters (if the game has characters)
The game treatment does not include all the details of the game yet — it's a high-level summary of the game concept. Instead, you'll figure out the rest of the details once you're prototyping the game.
In the next assignment, you'll use your game treatments to get outside feedback on your game concepts, in order to help improve them and select the best game concept to prototype.
As a team, discuss, refine, and evaluate your set of game concepts to select your team's top 3 game concepts. This is the time when you can start to critique, filter out, or revise ideas. Some questions to consider for each game concept include:
Does the game seem to match the team's gaming motivations and player experience goal(s)?
Does the game seem fun and innovative?
Does the game seem feasible (possible) to prototype? (Reminder: your team only needs to build one level of the game)
Use this template to create a game treatment for each of the team's top 3 game concepts.
The written summary will be brief (title + 3 paragraphs).
The sketches are just rough mockups. Black-and-white drawings are fine (though you can use color if helpful).
Decide on a plan for each team member to contribute. For example, maybe the Art Lead is responsible for the sketches. Maybe each of the other persons is responsible for writing one game treatment — or maybe someone writes a first draft, and someone else edits it.
Be sure the game treatments will be clear to other people, so you can get useful feedback.
Now your team will use its paper prototype to playtest your game, in order to evaluate the core gameplay and improve the game design.
You'll playtest your paper prototype in two stages:
Internal Playtesting → External Playtesting
Internal playtesting is having your own team play the game to evaluate it.
External playtesting is having people outside your team play the game to evaluate it. Ideally, you would recruit people that match your target player persona.
After playtesting, your team will update its game design document to reflect any necessary additions or changes to the game's design.
Playtest your paper prototype internally within your team to clarify and refine your game design. If necessary, revise your game design document and/or paper prototype.
Have one person on your team act as the player. The player can perform any actions allowed by the procedures of the game.
The rest of your team will act as the "computer" by responding to the player's actions and controlling the other characters and game objects — based on the rules of the game.
Focus on making sure the core gameplay works, makes sense, seems fun, and matches your targeted design goals (gaming motivations and player experience goals).
Recruit external playtesters by partnering with another team. Have the other team playtest your paper prototype. After 15-20 minutes, the two teams can switch roles, so the other team's paper prototype is also playtested. (If time allows, have more external playtesters play your game.)
The design team should briefly:
identify the game's targeted gaming motivations and player experience goal(s)
explain the game's premise and objective
describe and demonstrate the basic actions that the player can perform during gameplay (optional: it might help to provide a "cheat sheet" that the player can refer to)
One person from the design team should observe and record notes during playtesting (such as: what parts of the gameplay seem to work well, what issues occurred, etc.).
The rest of the design team will act as the "computer" during playtesting.
One person from the external team will act as the player, while the rest of the external team will observe the playtesting.
Both during and after playtesting, the external team can ask questions and provide feedback. The design team can respond and can ask their own questions to clarify the feedback or to get feedback on specific aspects of the game.
Use the observations and notes from the external playtesting to clarify and improve your game's design by making additions and/or revisions to your team's game design document — including your concept sketches (if needed).
Be sure that you completed Coding Steps 6-10 on the previous page.
IMPORTANT: Take the time to read each step, and try to understand how the code works.
Right now, our game is very unforgiving — if the spaceship collides with just one asteroid, the game is over.
Let's give the spaceship some "shields" that will be damaged every time the ship collides with an asteroid. Once all the shields are gone, then the ship will explode.
Many games have some kind of health bar for the player's character — when the health reaches zero, the player dies in the game. The shields that we're going to add to our game are just a health bar for the spaceship.
Phaser has built-in properties and methods to keep track of the health of a sprite. When a sprite's health reaches zero, Phaser will automatically kill()
the sprite.
health
is the property that represents a sprite's health value. You can set it to any value (but many game designers use 100
as the starting value for a sprite's health).
maxHealth
is a property that sets a maximum value for the sprite's health. You can set this to any value (but many game designers also use 100
for the maximum value). If your game has ways for the sprite's health to increase (e.g., by collecting health packs, resting, etc.), this will prevent the health from exceeding this limit.
damage()
is a method to decrease a sprite's health value. Inside the parentheses, you include a value representing the amount of damage.
heal()
is a method to increase a sprite's health value. Inside the parentheses, you include a value representing the amount of healing.
Add this Phaser code in your create()
function (after the command that set the angle of the player):
This will set the starting value of the player's health to 100 (and also set 100 as the maximum value).
Instead of killing the player
sprite when it collides with an asteroid, let's damage the player's health by 25. After 4 collisions, the player's health will be zero, and Phaser will automatically kill()
the player
sprite.
In your collideAsteriod()
function, replace the command that kills the player with this command instead:
Refresh your HTML preview to verify that it takes 4 collisions with asteroids before the spaceship explodes.
Now we just need to add a health bar, so the player has some visual feedback to track the amount of "shields" remaining for their spaceship.
Typically, a health bar is a rectangle that decreases (or increases) in width (or height) to represent the amount of health remaining. An easy way to do this in Phaser is to scale an image of a rectangle in one direction (either its width or its height).
We're going to use images of a red rectangle and a green rectangle to create our health bar:
Notice that these two bars are the exact same size. We're going to position them on top of each other as layers, so the red bar is behind the green bar.
The width of the green bar will represent the amount of health ("shields") remaining. When the shields are at maximum, the green bar will completely cover and hide the red bar.
When the player's health is damaged, we will scale down the width of the green bar, which will also reveal some of the red bar behind it. This will make it easier for the player to quickly see how much or how little health is remaining.
Let's first add a text label for the health bar (so it will be clear to the player what this bar represents):
Add a global variable for healthText
In your create()
function (after the code that added scoreText
), add healthText
as a text object at position 210, 20
. Set the text to display: 'Shields'
. Use the same font styling that you used for scoreText
(Arial, 20px, bold, #ffffff)
You do not need to set the text's anchor or add a text shadow.
Refresh your HTML preview to verify that the text label (Shields) appears at the top of the game to the right of the score.
We're going to add the red and green bars to the game as images instead of as sprites.
Add a global variable for healthBar
, which will represent the image of the green bar (we don't need to create a variable for the red bar)
Preload assets/images/health-red.png as the image for the red bar. Assign it a unique asset key name, such as red-bar
. (Hint: If you need help, look back at Step 4 to see how to preload an image.)
Preload assets/images/health-green.png as the image for the green bar. Assign it a unique asset key name, such as green-bar
.
Add this Phaser command in your create()
function (after the code that added healthText
):
This command does just what you think — it adds an image at position 300, 20
using the red-bar
asset. Because we're not going to scale or change the red bar in any way, we don't have to assign it to a variable — we can just add it to the game.
Refresh your HTML preview to verify that the red bar appears at the top of the game to the right of the "Shields" text.
Now add this Phaser command (immediately after the command that added the red bar):
This command adds the green bar at the exact same position and assigns it to the healthBar
variable (so that we can scale the green bar when the health value changes).
Because you added the red bar first and then added the green bar, the green bar will be layered on top of the red bar.
Refresh your HTML preview to verify that the green bar appears at the top of the game (and hides the red bar, which is actually behind it).
Now we just need to scale the width of the green bar, so it will shrink to represent the decrease in the player's health.
Phaser has a scale
property for game objects (such as: text, images, sprites, etc.) that allows you to scale the object's width and height to make the object larger or smaller.
To shrink the width or height, use a scale value that is between 0 and 1.
To enlarge the width or height, use a scale value that is greater than 1.
To keep the width or height the same size, use a scale value of exactly 1.
For our health bar, we only want to change the width of the green bar. We'll keep the height of the green bar the same size.
We can calculate the scale needed for the width of the healthBar
by using a simple fraction — the scale should be the player's current health value divided by its maximum possible health value.
For example, if the player's health is currently 25 and its maximum health value is 100, the scale for the width of the health bar should be 0.25 (25/100), which would look like this:
Add this Phaser command inside your collideAsteroid()
function (immediately after the command that damages the player):
This command sets the scale values for the width and height (in order) of the healthbar
image. The scale for the width will be calculated by dividing the player's health by its maximum health. The scale for the height will be kept at 1, so the height of the image doesn't change.
Refresh your HTML preview to verify that the width of the green health bar shrinks in proportion to the damage from each collision.
In our case, each collision should shrink the width of the health bar by 25% of its original width until it reaches zero (100 → 75 → 50 → 25 → 0). When the shields reach zero, the spaceship will explode.
Some games only give the player one life during the game — when the player's character is killed, the game is over. Other games give the player unlimited lives, allowing the player to restart the game again and again (often by sending the player back to a previous checkpoint in the game). Some games give the player a limited number of lives.
The original Asteroids game (like many video games from the 1970s and '80s) gave the player 3 lives at the start of the game. The player could also earn extra lives by reaching certain scores in the game.
We're going to add both of these features to our game. In this step, we'll give the player 3 lives (spaceships) at the start of the game. When the spaceship explodes, the player loses one life — but a new spaceship will appear as long as the player has at least one life left. When the player is out of lives, the game will be over.
Our game will also give the player a chance to earn an extra life as a bonus after every 10,000 score points (10,000 → 20,000 → 30,000 → 40,000, etc.). However, our game will also limit the total number of lives (spaceships) to a maximum of 5 lives.
Our game could just list the number of lives as text on the screen. Instead, we'll show spaceship icons to represent the number of lives. Visually, this will make it easier for the player to see at a glance how many lives remain. It will sort of be like a health bar for the number of lives left.
Here's the image for the number of lives remaining. It shows 5 spaceship icons because that's the maximum number of lives we decided to allow the player to have:
If we were to scale this image, we could make it look larger or smaller — but it would still show 5 spaceships.
So instead of scaling this image, we are going to hide part of it when the number of lives is less than 5.
Phaser has a method called crop()
that allows you to only show part of an image (and hide the rest). You can also change the "crop" later to show less (or more) of the original image. The "crop" can be changed or reversed whenever you need because it doesn't actually alter the original image.
Let's first add a text label for the spaceship icons (so it will be absolutely clear to the player what these icons represent):
Add a global variable for livesText
In your create()
function (after the code that added healthBar
), add livesText
as a text object at position 590, 20
. Set the text to display: 'Ships'
. Use the same font styling that you used for scoreText
and healthText
(Arial, 20px, bold, #ffffff)
You do not need to set the text's anchor or add a text shadow.
Refresh your HTML preview to verify that the text label (Ships) appears at the top of the game to the right of the Shields health bar.
Now you're going to add the spaceship icons to the game as an image:
Add a global variable for livesBar
Preload assets/images/ship-lives.png as the image for the spaceship icons. Assign it a unique asset key name, such as lives
.
Add the image to the game at position 655, 20
and assign it to the livesBar
variable.
Refresh your HTML preview to verify that the 5 spaceship icons appear at the top of the game to the right of the "Ships" text.
Next we're going to start the player with 3 lives and crop the livesBar
to only show 3 spaceships.
In order to crop an image, you create a rectangle of a specified width and height to represent the crop that you want to apply to an image. Then you can crop()
the image using this rectangle. Any part of the original image that is outside of the crop rectangle will be hidden.
The spaceship icons image is similar to a spritesheet containing frames. The image has 5 identical spaceship icons, which are each 25 pixels in width and 25 pixels in height (including the transparent area around each icon). The entire image is 125 pixels in width and 25 pixels in height.
So we can set the width of the crop rectangle based on how many lives the player has left. For example, if the player has 3 lives left, then the crop rectangle should be set to a width of 75 pixels (3×25).
We want the height of the crop rectangle to be the same as the height of the spaceship icons image (which is 25 pixels in height).
Add these variables to your code:
Add a global variable named shipLives
and assign it an initial value of 3
Add a global variable called livesCrop
to represent the crop rectangle
Add this Phaser code to your create()
function (after the command that added livesBar
):
Let's examine this code:
The first line of code creates a rectangle that you'll use for the crop. 0, 0
represent the position of the top-left corner of the crop rectangle (relative to the top-left corner of the image that it will crop). The next two numbers represent the width and height (in order) of the rectangle: shipLives * 25
is the width, and 25
is the height.
The second line applies the crop for the livesBar
image using the livesCrop
rectangle.
Refresh your HTML preview to verify that the only 3 spaceship icons appear at the top of the game.
Now we need to actually change the number of shipLives
every time the player's spaceship is destroyed. Then we will want to re-crop the livesBar
using a new width (based on how many lives remain).
As a reminder, in your create()
function, you added an event that will run a function whenever the player
is killed. This is where your game resets the explosion
sprite and plays its animation. Now you're going to add some more code inside this event function to change the number of lives remaining and adjust the crop for the livesBar
.
Add Phaser commands in your player.events.onKilled
function to do the following (in order):
Decrease the value of shipLives
by 1
Change the width of livesCrop
by calculating its new value: livesBar.width = shipLives * 25;
Apply the crop for livesBar
using the livesCrop
rectangle
Refresh your HTML preview to verify that only 2 spaceship icons will be shown after the spaceship is destroyed.
Now we need to respawn the player's spaceship if the player still has lives yet. We can use an if-else statement to determine if the number of lives left is greater than zero (otherwise the game is over).
Add this Phaser code in your player.events.onKilled
function (after the code that updated the crop for livesBar
):
If the player still has at least one life left, this code will:
position the player
sprite back at the center of the game with the spaceship pointing up (-90° angle)
sets the player
sprite's velocity and acceleration to zero
brings the player
sprite back to life with maximum health using the revive()
method (alternatively, we could have used the reset()
method)
fades the player
sprite into view using a to
tween on the sprite's alpha
property (2000
means the tween will take 2 seconds to complete)
Otherwise, the player has zero lives left, which means the game is over. In Step 14, we'll add some code to run when the game's over.
Refresh your HTML preview to verify that a new spaceship will appear after the spaceship is destroyed. After 3 spaceships have been destroyed, the game should be over.
Let's add some audio feedback to make it clear to the player that a new spaceship has appeared.
Add a sound effect that will play one time whenever a new spaceship appears:
Use teleportSound
as the name of the global variable for the sound.
Use assets/sounds/teleport.mp3 for the sound file. Assign a unique asset key name to the sound, such as teleport
.
Add teleportSound
to the game with a volume of 0.5
.
Add a Phaser command to play teleportSound
inside your player.events.onKilled
function (after the code for the tween that fades in the new spaceship).
Refresh your HTML preview to verify that the teleport sound plays whenever a new spaceship appears.
Let's reward the player with an extra life every 10,000 score points (10,000 → 20,000 → 30,000 → 40,000, etc.). However, we will set a limit of 5 lives that the player can have at any point.
If the player already has 5 lives, we'll reward the player by setting the shields (health) back to maximum.
Let's play a sound effect as feedback to the player when either reward is received. We'll also do a visual effect when a new life is awarded.
If the player already has 5 lives and maximum health, then no reward will be given, but the player will get another chance to earn a reward after another 10,000 points.
Add code to your game to do the following:
Add a global variable named maxLives
and assign it a value of 5
Add a global variable named newLife
and assign it an initial value of 10000
Add a global variable named lifeSound
for the sound effect
Preload assets/sounds/extra-life.wav and assign it a unique asset key name, such as life
Add lifeSound
to the game with a volume of 0.5
Now let's plan out the code that we'll need to check to see if the player has earned an extra life reward.
If the player's score
is greater than or equal to newLife
, then the player has earned a reward:
Next we need to check if the number of shipLives
is less than maxLives
. If that's true, we'll give the player an extra life.
Otherwise the player already has the maximum number of lives, so we'll check if the player's health
is less than its maxHealth
. If that's true, we'll set the health back to its maximum.
In either case, we need to increase newLife
by another 10000
points, so the player can earn another reward later.
Let's add the code for this check as a separate function in your game code.
Add a new custom function named checkNewLife()
(after your shootAsteroid()
function).
Add this code inside the curly braces { }
of your checkNewLife()
function:
As you can see, this code will perform the check that we planned out. However, there is some missing code that you'll need to add inside the if-else statements.
Add Phaser commands to award an extra life:
Increase the value of shipLives
by 1
Change the width of livesCrop
by calculating its new value based on shipLives
Set the crop for livesBar
using the livesCrop
rectangle
Update the crop for livesBar
Play lifeSound
Make the game screen flash a bright green color by using this command: game.camera.flash(0x00ff00, 500);
Add Phaser commands to replenish the player's health (if the maximum number of lives is already reached):
Change the value of player.health
to be equal to player.maxHealth
Scale the healthBar
Play lifeSound
Most importantly, we need to call the checkNewLife()
function, in order for it to actually run. We could call this function either inside the shootAsteroid()
function (after the code that increases the score
) OR inside the update()
function (after the collide()
commands is a good spot) — choose just one of those places to call the function with this command:
Refresh your HTML preview to verify that an extra life is awarded when the score reaches 10000 points. Another extra life should be awarded at 20000 points. If possible, verify that the shields will be replenished to maximum if you already have 5 ships when you reach the next new life score.
At this point, you have a working version of Asteroids with lots of features. Now we're going to add a simple "start screen" and "game over screen" to finish it.
At the start of the game, let's show an image of our game's title (Asteroids 2084).
Add code to do the following:
Add a global variable named gameTitle
for the title image.
Preload assets/images/asteroids-2084-title.png for the title image. Assign a unique asset key name to the image, such as title
.
At the end of your create()
function (after the code that added and cropped the livesBar
), add the title image 100 pixels above the game's center, and assign the image to the gameTitle
variable.
Set the title image's anchor to be its center.
Set the scale for the title image to be 0.75
for both its width and height.
Refresh your HTML preview to verify that the game title appears on the screen. Currently, it will remain on-screen during the game, but we'll fix that in a bit.
Let's add a way for the player to start the game when he or she is ready. We'll wait to show the spaceship until the player has pressed the fire key (spacebar). Once the fire key is pressed, we'll remove the game title (and a start instruction) from the screen, show the spaceship, and let the gameplay begin.
Add code to do the following:
Add a global variable named startText
for the start instruction text.
In your create()
function (after the code that added the gameTitle
), add startText
as a text object positioned 200 pixels below the game's center. Set the text to display: 'Press Fire to Start Mission'
. For the font style, use Arial, 30px, bold, #00ff00 (which will make the text slightly larger and bright green).
Set the text's anchor to be its center.
Refresh your HTML preview to verify that the start instruction text appears on the screen. (Just like the game title, it currently remains on-screen during the game, but we'll change that soon.)
Now let's hide the spaceship until the player actually presses the fire key to start the mission.
Add these Phaser commands (inside your create()
function after the code that added the player animation):
This code hides the player
sprite by removing it from the game. Later, your code will add the sprite back into the game by setting the exists
property back to true
.
Refresh your HTML preview to verify that the spaceship no longer appears on the screen.
You probably discovered that pressing the fire key (spacebar) doesn't do anything yet. We're about to change that.
You might have also discovered that pressing the up arrow key will cause the engineSound
to get louder (as if the spaceship were accelerating, except it doesn't exist yet).
Modify your existing if statement for the arrowKey.up
, so it will check whether the arrowKey.up
is being pressed AND the player exists.
Hint: Look at your if statement for the fireKey
as an example of how to do this
Refresh your HTML preview to verify that the engine sound will not get louder if you press the up arrow key when the spaceship is not visible.
Now we want to add a signal that will "start" the game when the player presses the fire key. However, we only want this signal to run one time. Once the game is "started", we want the fire key to fire the spaceship's laser.
Add this Phaser command in your create()
function (after the code that added the fireKey
as a keyboard input):
This command will add a one-time signal to the fireKey
: when this key is pressed down, it will call a custom function named startGame
(which you will need to create).
Add a new custom function named startGame()
(after your checkNewLife()
function).
Add this code inside the curly braces { }
of your startGame()
function:
As you can see, this code adds several to
tweens that change the startText
and gameTitle
. All these tweens use the Phaser.Easing.Cubic.Out
pattern, and true
means the tweens will start automatically (once the startGame()
function is called).
The startText
will fade out (to an alpha value of zero, which is transparent) over 0.25 seconds (250
milliseconds).
The gameTitle
will fade out over 3 seconds but only starts after a 0.25 second delay (the 250
after true
represents a delay value).
At the same time, the gameTitle
will also zoom out over 3 seconds (3000
) by changing the scale of the image to 3 in both directions. This will also only start after a 0.25 second delay.
Now you need to add some missing code in this function to fade in the player
sprite:
Play the teleportSound
(as audio feedback to the player)
Make the player
sprite exist (by changing its exists
property to true
)
Add a to
tween to make the player
sprite fade into view by changing its alpha value to 1
over 2 seconds. Use the Phaser.Easing.Cubic.Out
pattern, and set the tween to start automatically (with no delay).
Refresh your HTML preview to verify that pressing the fire key will "start" the game (fading out the start instruction, fading and zooming out the game title, fading in the spaceship and playing the teleport sound).
Now let's add a simple "game over" screen once the player is out of lives. We'll display some text and allow the player to restart a new game (by pressing the fire key).
Add code to do the following:
Add a global variable named gameOverText
In your create()
function (after the code that added the startText
), add gameOverText
as a text object positioned 100 pixels above the game's center. Set the text to display: 'Game Over'
. For the font style, use Arial, 48px, bold, #ff0000 (which will make the text red).
Set the text's anchor to be its center
Make the text invisible (i.e., transparent) by setting its visible
property to false
Now we need to display the gameOverText
and the startText
when the player has run out of lives.
Add Phaser code in your player.events.onKilled
function (inside the else
curly braces for when the player has no lives left) to do the following:
Make gameOverText
visible
Set the scale for gameOverText
to be 3
for both its width and height
Add a to
tween to make gameOverText
fade into view by changing its alpha value to 1
over 1 second. Use the Phaser.Easing.Cubic.Out
pattern, and set the tween to start automatically (with no delay).
Add a to
tween to make gameOverText.scale
zoom in by changing its value to 1
(for both directions) over 1 second. Use the Phaser.Easing.Cubic.Out
pattern, and set the tween to start automatically (with no delay).
Add a to
tween to make startText
fade into view by changing its alpha value to 1
over 0.5 seconds. Use the Phaser.Easing.Cubic.Out
pattern, and set the tween to start automatically after a 2 second delay.
Refresh your HTML preview to verify that the game over text and start instruction text appear on the screen when the player has run out of lives (game over text should fade and zoom in first, then start instruction should fade in).
Finally, when the game is over, we want to add a signal that will "restart" the game when the player presses the fire key again. However, we only want this signal to run one time.
Add a Phaser command in your player.events.onKilled
function (when the game is over) to create a one-time signal that will call a custom function named restartGame
when the firekey
is pressed.
Add a new custom function named restartGame()
(after your startGame
function).
The restartGame()
function needs to reset some of the game variables (such as: score
, shipLives
, etc.) back to their original values, and then restart the game.
Add code inside the curly braces { }
of your restartGame()
function to do the following:
Change the value of score
back to 0
Change the value of shipLives
back to 3
Change the value of newLife
back to 10000
Change the value of the asteroids' maxSpeed
back to 100
Restart the game state with this command:
Refresh your HTML preview to verify that a new game can be restarted after the current game is over.
Congratulations, you've completed your second practice game! Hopefully, the new Phaser commands made sense — you'll get the chance to use them again in the next practice game.
If you have the time and interest, you can add an enemy spaceship in Step 15. Be forewarned that it will require a lot of additional code, and only a small amount will be provided to you. Luckily, most of the necessary code is similar to things you've already done (so you'll be able to copy and modify portions of your existing game code).
The original Asteroids game had enemy spaceships that would periodically appear and shoot at the player, adding an extra challenge to the game — but rewarding the player with a large score point bonus if the player destroys the enemy.
In this step, you can add an enemy ship to your game. You'll add a timer event to make the enemy ship appear every 30 seconds and move across the screen in a random direction and speed. You'll also add a simple AI (artificial intelligence) to make the enemy aim and shoot at the player.
Adding this enemy ship will involve lots of sub-steps with lots of additional code (but most of it is similar to things you already have in your code). However, adding the enemy ship will be worth it because it really increases the excitement and challenge of the gameplay experience.
Add global variables for the following:
enemy
(enemy spaceship sprite)
enemyLaser
(enemy weapon)
enemyExplosion
(animated explosion sprite for enemy)
enemyFireSound
(sound played when enemy weapon is fired)
enemyAlarmSound
(sound played when enemy is alive)
In your preload()
function, load the following assets:
Load assets/images/enemy-ship.png as a spritesheet, and assign it a unique asset key name, such as enemy-ship
. Each animation frame in this spritesheet is 64 pixels in width and 64 pixels in height.
Load assets/images/enemy-laser.png as an image, and assign it a unique asset key name, such as enemy-bullet
Load assets/sounds/enemy-fire.wav as audio, and assign it a unique asset key name, such as enemy-fire
Load assets/images/alarm.mp3 as audio, and assign it a unique asset key name, such as enemy-alarm
The enemyExplosion
will just use the same spritesheet as the player explosion
does (so your code does not need to load it a second time).
In your create()
function (after the code that added the player
sprite), add code for the enemy
sprite:
Add the enemy
sprite at position 0, 0
(which is a temporary position) using the enemy-ship
asset
Set the sprite's anchor to its center
Enable Arcade Physics for the sprite
Set the sprite's body collision area to be a circle with a radius of 30 pixels that is offset 2 pixels from the left edge of the frame and 2 pixels from the top edge
Have the game automatically kill the sprite when it leaves the game world boundaries by adding this code:
Add an animation to the sprite to play all 3 frames (in order 0-2) at 10 frames per second in a continuous loop
Play the animation (so it will already be playing anytime the enemy
is on-screen)
Hide the sprite (until it is spawned) by setting its exists
property to false
In your create()
function (before the code that adds the enemy
sprite), add code for the enemyLaser
weapon:
Add the enemyLaser
weapon to the game with 5 bullets using the enemy-bullet
asset
Set the bullets to be killed automatically if they leave the camera boundaries
Set the bullet speed to 400
pixels per second
Set the fire rate to 750
(once every 0.75 seconds)
Set the bullet body collision area to be a rectangle 16 pixels in width and 16 pixels in height that is offset 4 pixels from the left edge and 4 pixels from the top edge
After the code that adds the enemy
sprite, add a command to:
Set the enemyLaser
weapon to track the enemy
sprite with no offset but don't track the sprite's rotation (set to false
)
In your create()
function (after the code that adds the explosion
sprite used for the player), add code for the enemyExplosion
sprite:
Add the enemyExplosion
sprite at position 0, 0
(which is a temporary position) using the explosion
asset (same one used for player's explosion)
Set the sprite's anchor to its center
Add an animation to the sprite to play all 16 frames (in order 0-15) at 30 frames per second (with no loop)
Hide the sprite (until it is needed) by setting its visible
property to false
In your create()
function, add code for enemyFireSound
:
Add enemyFireSound
to the game with a volume of 0.5
After the code that adds the enemyLaser
, add an enemyLaser.onFire
function containing a command to play the sound.
You're going to create a custom function to spawn (generate) an enemy spaceship. Later, you'll add a timer to call (run) this custom function every 30 seconds.
Every time a new enemy
is spawned, we want it to appear at a random location along one of the edges of the screen. The enemy
should then move across the screen at a random speed and in a random direction.
After your restartGame()
function, add a new custom function called spawnEnemy()
.
Add this Phaser code inside the curly braces { }
of the function:
The first line of this code checks to see whether the player
sprite exists. If the player hasn't started the game yet or the game is over, player.exists
will be false
. If that's the case, the function will simply return
(i.e., end) without performing the other code in this function.
The rest of the code in this function generates a random number between 1-4 to decide whether the enemy will be spawned from the top, right, bottom, or left of the game screen. Then it generates a random starting position to place the enemy
somewhere along the edge of the screen. It also generates a random velocity to move the enemy
across the screen towards the opposite side (in a straight line). The ranges for the random values have been chosen to keep the enemy
on-screen for several seconds.
The last command brings the enemy
sprite back to life.
In your create()
function, add enemyAlarmSound
to the game with a volume of 0.1
, and set the sound to loop continuously.
In your spawnEnemy()
function, play the sound when the enemy is brought back to life.
In your create()
function (after the code that adds the enemy
), add an enemy.events.onKilled
function containing a command to stop the sound.
We want to add a simple AI (artificial intelligence) to control the enemy
. The enemy
will automatically move across the screen based on the velocity it was given when it was spawned. However, as it moves, we want the enemy
to rotate to face towards the player
, so the enemy
can fire its laser at the player
.
Add this Phaser code in your update()
function (after the code that spawns new asteroids):
When the enemy
exists on-screen, this code will rotate the enemy
sprite to face towards the player
(by calculating the angle between these two sprites). Then it adds a small amount of error to the enemyLaser.fireAngle
. (Otherwise, the enemy is always a perfect shot, making it too difficult for the player to avoid being hit.) Then the enemyLaser
fires towards the direction of the player
. The enemyLaser.fireRate
(which you set in the create()
function) will automatically determine how frequently the enemy actually fires its weapon.
In your create()
function (after the code that adds the enemy
), add this Phaser command:
This command adds a timer event that will run in a loop: Every 30 seconds, it will call (run) the custom function named spawnEnemy
.
Refresh your HTML preview to verify that the enemy will appear once every 30 seconds (from a random edge location - top, right, bottom, or left). As the enemy flies across the screen, it should rotate to face towards the player and fire its weapon. The alarm sound and enemy fire sound should play.
However, at this point, the enemy and its laser bullets don't affect anything else in the game because you haven't added any collisions involving the enemy or its bullets.
In your update()
function, add a collide()
command for player
and enemy
(list in this order) that will call a custom function named collideEnemy
.
At the end of your code, add a new custom function called collideEnemy()
.
Inside the parentheses ( )
after the function's name, list these parameters (in order): player, enemy
Inside the curly braces { }
of the function, add Phaser commands to do the following:
Kill the enemy
Reset enemyExplosion
to the x and y position of the enemy
Play the enemyExplosion
animation
Play boomSound
Shake the game camera
Damage the player
by 25
health points
Scale the player's healthBar
Refresh your HTML preview to verify that the player's spaceship is damaged and the enemy spaceship explodes when they collide.
In your update()
function, add a collide()
command for laser.bullets
and enemy
(list in this order) that will call a custom function named shootEnemy
.
At the end of your code, add a new custom function called shootEnemy()
.
Inside the parentheses ()
after the function's name, list these parameters (in order): bullet, enemy
Inside the curly braces { }
of the function, add Phaser commands to do the following:
Kill the bullet
Kill the enemy
Reset enemyExplosion
to the x and y position of the enemy
Play the enemyExplosion
animation
Play boomSound
Add 2500
to the player's score
Update scoreText
to display the new score
value
Call the checkNewLife()
function (NOTE: This is not needed if a call to checkNewLife()
is already listed in your update()
function)
Refresh your HTML preview to verify that the player's laser can destroy the enemy spaceship, awarding the player 2500 score points.
In your update()
function, add an overlap()
command for player
and enemyLaser.bullets
(list in this order) that will call a custom function named shootPlayer
. As a reminder, overlap()
is similar to collide()
except overlap()
doesn't transfer momentum between the two objects. (If we used a collide()
function here, the enemy's bullets would slow down the player's spaceship or even push it backwards, which seems unnatural.)
At the end of your code, add a new custom function called shootPlayer()
.
Inside the parentheses ()
after the function's name, list these parameters (in order): player, bullet
Inside the curly braces { }
of the function, add Phaser commands to do the following:
Kill the bullet
Play boomSound
Shake the game camera
Damage the player
by 25
health points
Scale the player's healthBar
Refresh your HTML preview to verify that the enemy's laser can damage the player's spaceship.
In your update()
function, add a collide()
command for enemyLaser.bullets
and asteroidGroup
that will call a custom function named removeAsteroid
.
At the end of your code, add a new custom function called removeAsteroid()
.
Inside the parentheses ( )
after the function's name, list these parameters (in order): bullet, asteroid
Inside the curly braces { }
of the function, add Phaser commands to do the following:
Kill the bullet
Set the x and y positions of asteroidParticles
to the x and y positions of the asteroid
Kill the asteroid
Play the explode()
particle effect for asteroidParticles
Play boomSound
Refresh your HTML preview to verify that the enemy's laser will destroy an asteroid if hit.
You might have noticed that we did not add a collision between the enemy
and the asteroidGroup
. This was intentional, so that the player
has to either destroy or avoid the enemy
(rather than letting an asteroid randomly hit and destroy the enemy
). However, if you want, you could add this last type of collision to make the game slightly more realistic. It's your choice.
Congratulations, you've completed everything in Step 15! You should be feeling much more confident about your ability to use Phaser to code a video game.
Be sure that you completed the Prep Steps on the previous page.
IMPORTANT: Take the time to read each step, and try to understand how the code works.
In your HTML file (index.html), add a paragraph with your name after <div id="my-game"></div>
(which is the container for your game):
Refresh your preview of your HTML file to verify that the paragraph with your name shows up under the black box and is left-aligned on the webpage.
In your CSS file (style.css), let's add a background color to the webpage body and let's center all the text on the webpage body.
Add this new CSS into style.css before #my-game
(which styles the <div>
containing your game).
Refresh your preview of your HTML file to verify that the paragraph is center-aligned on the webpage and the webpage has a light gray background color (but your game doesn't).
That's all we're going to do with the HTML and CSS files for this practice, but feel free to make other changes to the webpage (such as: picking a different background color for the webpage, adding text above the game container, etc.).
Before we start coding the game, let's get oriented to the starter Phaser code already in your code.js file. We're just going to look at some code in this step.
Don't add or revise any code during this step. Just focus on understanding how the code works.
The first thing your JS code does is create a new Phaser.Game object by assigning it to a variable named game
:
Many of the Phaser commands that you'll use will start with game
because this is a reference to your variable named game
— you could use a different name for this variable if you really wanted to, but almost every example Phaser code that you'll see uses game
(so make your life easier by using this name).
You will notice two numbers — 800 and 600. These represent (in order) the width and height (in pixels) of your game display. You could change these numbers for your own game, but for right now, we'll just stick with these.
Notice where it says 'my-game'
— this represents the id name of the <div>
in your HTML file where Phaser inserts your game. You could change this id name, but you'd want to make sure the new id name matches in your HTML, CSS, and JS files.
Finally, notice that it refers to preload
, create
, and update
. These are the names of 3 core Phaser functions that will be used in your game. If you look down further in your JS file, you will see these functions listed — except they are empty at the moment. You will be adding Phaser commands inside these functions to build your game:
PRELOAD: The preload()
function is used to load game assets (such as images, sounds, etc.) into the game's memory, so they are all ready at the same time for use in the game. The preload()
function runs one time at the start of the game (i.e., when the webpage loads).
CREATE: The create()
function is used to create your game world and its user interface by adding images and sounds into the game, adding player controls, adding text, etc. The create()
function runs one time after the preload()
function is finished.
UPDATE: The update()
function is used to update the gameplay by checking for player input, checking for conditions or events, updating objects in the game world, etc. The update()
function runs in a continuous loop after the create()
function is finished. The update()
function loops many times per second, allowing for smooth animations and responsive interactions.
Two other things to notice in your code.js template:
There is a place near the top where you may need to declare global variables for certain objects in your game (such as: the player, a group of enemies, the score, etc.).
There is a place at the bottom where you may need to create custom functions to handle certain conditions or events in the game.
By default, Phaser makes the background color of your game black. You can change the background color, or you can add images as a background for your game.
In your JS file (code.js), add this Phaser command inside function create()
by pasting the command on a blank line between the curly braces { }
:
Does your code look like this now? Good.
Refresh your preview of your HTML file to verify that your game now has a blue background color.
Remember that you created a Phaser.Game object named game
at the start of your game code. An object is a special type of variable that can contain a set of properties (variables) and methods (functions).
In fact, a property within an object can be another object — in other words, an object can contain other objects inside it.
Besides the main Phaser.Game object itself, there are other types of Phaser objects that you will be creating and using in your game. Each of these objects will have its own set of properties and methods (which are defined by the Phaser JS library).
Every Phaser command is a reference to a Phaser object property or a Phaser object method. If the command has parentheses ()
, it's a reference to a method. Otherwise, it's a reference to a property.
The Phaser API documentation is a reference that describes all the Phaser objects and their properties and methods.
RESOURCE: W3Schools has a great explanation of JavaScript Objects
So let's look at this Phaser command:
The first part of this Phaser command is: game.stage
Within your Phaser.Game object named game
, there is a property (a variable) called stage
— the Phaser JS library automatically created this property (along with other properties and methods) inside your Phaser.Game object when the game
variable was created at the beginning of your code.js file.
Notice that a period is used to separate the name of the object and the name of the property (or method) within that object.
This property called stage
is a variable representing the game display. In fact, it turns out that stage
is also an object, meaning that it has its own set of properties (variables) and methods (functions).
Within the stage
object, there is a property called backgroundColor
which is exactly what it sounds like: it determines the color of the game display background. When a Phaser.Game object is first created, this backgroundColor
property within the stage
object is set to a value of black.
So game.stage.backgroundColor
is a reference to the background color of the stage within the game. The last part of the command uses an equals sign to change the value of this property to blue (#6699ff
is a CSS hex color code for blue).
That's how Phaser commands work, in a nutshell.
Let's add an image to your game. Inside your assets folder is an image file called hello-sprite.png that looks like this:
What looks like 6 separate images is actually one combined image. PNG image files can have transparent backgrounds (which this image has).
If you view this image in an image editor (such as: Pixlr, etc.), it will look like it has a checkboard background:
The image editor displays the transparent areas as a checkboard pattern to make it clear (get it? clear) what parts of the image are transparent. When you use this image in your game, you won't see any checkerboard pattern.
You're going to add this image to your game as something called a spritesheet. A spritesheet is an image that will be subdivided into a set of smaller images.
A typical use for a spritesheet is to contain a set of animation frames for an object in your game (such as: player's character, etc.). Animations work by showing a sequence of frames — one at a time — in rapid succession to create the illusion of something moving or changing.
A sprite is a game object (such as: the player's character, an enemy, etc.) that uses a spritesheet. At any given moment during gameplay, a sprite only displays one possible frame from its spritesheet. By rapidly changing which frame is displayed, the game can animate the sprite.
Every animation frame in a spritesheet has to have the same rectangular size. In our case, our frames happen to be squares that are 64 pixels in width and 64 pixels in height. Here's our spritesheet image with the frames highlighted, so you can see how it will be sliced up into smaller images:
Technically, this particular spritesheet is not really an animation. Instead, it just shows the 6 possible emojis for our matching game. Combining these 6 images into a single spritesheet image (instead of 6 separate image files) just makes it easier to use in this particular game (as you'll see later in Step 5).
Here's a spritesheet for a different game that does actually show animation frames:
This spritesheet contains 9 animation frames: the first four frames show a little dude running to the left (imagine these four frames playing over and over again in a loop), the fifth frame shows him (or her?) standing still, and the last four frames show the little dude running to the right. You'll meet this little dude again in Practice 3.
You will need to declare (i.e., create) a global variable to represent your sprite. Add this JavaScript statement near the top of your code (look for the comment line that mentions global variables).
This statement creates a new variable named hello1
. You get to decide the names of variables, as long as each variable has a unique name (and as long as the variable name is not a reserved word in the JavaScript language).
You're going to name this variable hello1
because eventually you'll have 3 sprites in your game (hello1
, hello2
, hello3
). You could call them Moe
, Larry
, and Curly
if you really wanted — as long as each variable has a unique name.
RESOURCE: W3Schools has a JavaScript tutorial on Variables
Before you can add a sprite to your game, you have to load its spritesheet into the game's memory. Add this Phaser command inside function preload()
by pasting the command on a blank line between the curly braces { }
:
Does your code look like this now? Good — just checking.
You can see the first part of the Phaser command indicates that you're loading a spritesheet into your game. Let's take a look at what's inside the parentheses:
'hello'
represents an asset key — a key is sort of like a variable name. You decide what name to use for the key. Similar to variable names, each key should have a unique name, and the name cannot contain any spaces. In other Phaser commands, if you use this specific key name, Phaser will know that you are referring to this particular spritesheet.
'assets/hello-sprite.png'
represents the folder path and filename of the spritesheet image to load.
64, 64
represent (in order) the width and height (in pixels) of each frame in this particular spritesheet. Phaser will use these measurements to divide the spritesheet into a set of individual frames.
Now that the spritesheet is loaded, the next step is to actually add your first sprite to the game world.
Add this Phaser command inside function create()
by pasting the command on a new line after the command to change the game's background color:
This command adds a sprite to the game and assigns that sprite to the hello1
variable. So now, when you refer to hello1
in your code, you are referring to this sprite.
Let's look at what's inside the parentheses:
400, 300
represent the x and y coordinates (in order) of the pixel position where the sprite will be added to the game. The top-left corner of your game is 0, 0
. The values for the x-position increase from left to right. Since your game was set as 800 pixels wide, the x-position of the right edge of your game is 799 (because the first pixel is actually numbered as 0). The values for the y-position increase from top to bottom. Since your game was set as 600 pixels high, the y-position of the bottom edge of your game is 599. So 400, 300
is basically the center of your game.
'hello'
is the asset key name of the spritesheet to use for this sprite.
Refresh your HTML preview to verify that your game now has the emoji sprite inserted.
You will notice a couple of things:
Instead of seeing all 6 emoji frames, the sprite only shows one frame — this is normal and exactly what we want. Sprites only show one frame at any given time, and a sprite will show the first frame by default until you tell it to either show a different frame or play an animation sequence.
The sprite does not look centered. Instead, the top-left corner of the sprite is positioned at the game's center (400, 300
). By default, Phaser positions objects using their top-left corner as the "anchor" point. Sometimes this is exactly what you want. However, for some game objects — like a player's character, etc. — it will make more sense to use the center of the object (or some other point) as its "anchor" point for positioning in the game.
Add this Phaser command inside function create()
on a new line after the command that added the hello1
sprite:
This command will change the sprite's anchor point for positioning to be the center of the sprite (0.5, 0.5
means set the anchor to half-way across its width and half-way down its height).
Refresh your HTML preview to verify that the sprite is now centered in the game.
In fact, rather than you needing to figure out the x and y values for the center of your game, you can use a built-in Phaser property that calculates these values automatically.
Modify your existing command that adds the sprite, so the command looks like this instead:
Refresh your HTML preview to verify that the sprite is still centered in the game.
A game needs to provide a way for the player to interact with the game. Phaser supports various types of player input: keyboard input, mouse input, multi-point touch input, and even gamepad input.
You're going to add a keyboard input. Phaser allows you to designate specific keys on the keyboard as inputs. In this game, the player will use the spacebar key to "spin" the emoji sprite, so it randomly switches between the different emoji frames.
You need to declare a global variable for each input used in your game, so create another global variable called spacebar
.
You already have a line of code that added the hello1
global variable, so you have a couple of different options for adding a new variable:
(1) You could add a new var
statement for the new variable, so your code looks like this:
OR
(2) You could modify the existing var
statement to include the new variable (use a comma to separate the names), so your code looks like this:
Just choose one of the options above. In later steps, you'll need to add even more global variables, so keep these two options in mind.
Add this Phaser command inside function create()
on a new line after the command that sets the anchor for the hello1
sprite:
This command makes a specific key on the keyboard into an input and assigns it to your variable. You just have to provide the Phaser.KeyCode for the specific key. This reference lists all the available Phaser keycodes.
Next we need to make the game actually do something when the player presses the spacebar.
Phaser has several properties for detecting input on keys. Here are four properties for a key that will have a value of either true or false:
isDown
will detect if the key is currently pressed down — this property will remain true for as long as the player continues to hold the key down
isUp
will detect if the key is currently up (i.e., not being pressed) — this property will remain true for as long as the player doesn't press the key
justDown
will detect if the key was just pressed down during the current game loop (so you can detect exactly when the key is pressed down) — this property is only true during the specific game loop when the key is first pressed down — after that loop, it becomes false, even if the player keeps pressing the key
justUp
will detect if the key was just released during the current game loop (so you can detect exactly when the key is released) — this property is only true during the specific game loop when the key is first released — after that loop, it becomes false, even if the player still isn't pressing the key
Let's detect when the spacebar key is being pressed down. If that's true, we'll change the sprite to a random frame (one of the 6 possible emojis).
Add this Phaser code inside function update()
by pasting the code on a blank line between the curly braces { }
:
This if
statement will detect whether the spacebar is currently pressed down. If this condition is true, then it will change the sprite to a random frame. Otherwise, if this condition is false (i.e., when the spacebar is not being pressed), it won't change the sprite frame.
Here's a few things to note about this code:
if
statements are commonly used in computer programs to make decisions about what actions to perform (or not perform) based on specific conditions being true or false. You will need to understand how these work.
Sprite frames are referenced by number, with the first frame numbered as 0, the second frame numbered as 1, the third frame numbered as 2, etc. The emoji spritesheet contains 6 frames, which Phaser numbers as 0 to 5.
The statement Math.floor(Math.random() * 6)
generates a random whole number between 0 and 5 (i.e., 6 possibilities). Random numbers are commonly used in games — they are very helpful for adding some variation and unpredictability to the gameplay.
Refresh your HTML preview to verify that if you press the spacebar, the sprite will randomly change to a different emoji.
If you keep holding the spacebar down, the sprite will keep changing randomly — it will change every time the update()
function completes a loop. (This should give you a good idea of how fast one game loop is.) If you release the spacebar, the sprite should stop changing.
RESOURCE: W3Schools has a JavaScript tutorial on If-Else Conditional Statements
RESOURCE: W3Schools has a JavaScript tutorial on Random Numbers
Your team will develop and deliver a presentation to explain your proposed game design to the class. After your presentation, the audience will provide feedback through a design critique.
Your team's presentation should briefly explain and show:
Names and Roles of Team Members
Design Goals for Game (Gaming Motivations and Player Experience Goals)
Target Player Persona
Working Title of Game
Game's Premise and Setting
Game's Characters and Story (if applicable)
Game's Objective and Conflict/Challenge
Basic Gameplay & Gameplay Progression
Be sure to incorporate visuals into your presentation, such as:
Concept Sketches of Game World
Concept Sketches of Characters
Photos or Video of Paper Prototype
Know your audience — design your content and delivery for your audience's needs and expectations.
Keep it simple — be clear and concise, focus on your key message, have a clear structure from start to finish, etc.
Make your content engaging — capture your audience's interest, keep slide text limited, make numbers meaningful, use visuals and stories when possible, etc.
Make your delivery engaging — make eye contact, act confident and enthusiastic (even if you're nervous), use non-verbal communication, don't read slides directly, etc.
Practice your presentation — rehearse to become comfortable with your content and delivery.
Develop and deliver a team presentation to explain your proposed game design.
Your team will have 5-7 minutes to present. Your presentation should have about 7-12 slides.
Each team member should contribute meaningfully by helping create the presentation slides and/or by helping deliver the verbal presentation. Your teacher will confirm the specific expectations for participation.
If a team member were to be absent, the rest of the team should still be ready to give the full presentation.
After your team's presentation, the audience will provide feedback through a design critique.
The audience has about 5 minutes for a design critique.
Designate someone on your team to record notes of your critique (so you can follow up on possible issues or improvements)
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.
It's time to introduce you to the technology that your team will be using later to develop it's own game. This will be your first practice coding a browser-based video game using the Phaser JS game engine.
For this practice, all the game assets (i.e., images and sounds) and nearly all the game code will be provided. Focus on becoming familiar with how the code works.
This first practice will create a simple game. You're going to code an emoji matching game, similar to a slot machine. This game is based entirely on chance — no player skill involved. However, it will introduce you to some of the basic concepts of using Phaser to create a game.
PREVIEW VIDEO: Demo of Emoji Match
Check out the Phaser Introduction to better understand what Phaser does and to download a copy of the latest version of Phaser (phaser.min.js).
Follow the instructions to prepare your Phaser Game Template.
Download this assets.zip file, and then extract (decompress) the file contents, which will be a folder named assets that contains one image file and three sound files. (To extract the file on a Windows computer, right-click on the downloaded zip file, and select Extract All. On a Mac, double-click on the downloaded zip file.)
Place the assets folder into your game template folder.
Test your Phaser game template by previewing the HTML file online. If everything's ready to go, you should see a solid black box (i.e., a blank Phaser game canvas) on your webpage.
PHASER NOT LOADING? If you only see a black outline around a white box (or see nothing at all), then Phaser isn't loading correctly. Try these troubleshooting tips:
Be sure you downloaded the correct Phaser JS file: phaser.min.js
Be sure your Phaser JS file is named: phaser.min.js — if necessary, rename the file in your game folder to this exact name.
Be sure you placed phaser.min.js into the same folder that has your HTML, CSS, and JS files. If Phaser is in a different folder (or inside a subfolder), it will not load (unless you modify the <script>
tag in your HTML file to load Phaser from its correct folder location).
Be sure your index.html file includes <script>
tags to load phaser.min.js and code.js (in that order) from your game folder.
Be sure your code.js file lists the command to create a Phaser.Game object and includes the preload()
, create()
, and update()
functions (must be present even if empty at first)
Be sure your HTML, CSS, and JS files are running on a web server (such as: Editey web editor in Google Drive, CodePen, Codeanywhere, etc.).
Check your browser's JavaScript console to see if there are errors listed.
This first practice game will be coded in 10 steps. Overall, it doesn't take that much code to create this game; however, every step will explain how the code works.
In Step 1, you'll make a couple of changes to your HTML file and CSS file to demonstrate that the rest of the webpage surrounding your game can be modified. That will be useful later when your team develops its own game — you'll be able to customize the webpage to match your game's theme.
The rest of the steps are for coding your game, which will be done in your code.js file using Phaser JavaScript commands (as well as regular JavaScript).
The steps are outlined below. The instructions for Steps 1-5 start on the next page.
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.
Before your team starts generating specific ideas for its game, it will help to narrow your design focus by defining: 1. what kind of gameplay experience you want to design 2. who are the target players you're designing for
The kind of gameplay experience that you'll design is first defined by the gaming motivations that your team wants to target (such as: Strategy, Story, etc.).
Most games target more than one gaming motivation, often from more than one motivation group (e.g., Mastery + Immersion). Incorporating multiple gaming motivations (from multiple groups) can make the game more engaging — and allows it to appeal to more players.
Your team will select 2-3 motivations (representing at least two different motivation groups) to target for the game it will design.
For example, your team might decide to design a game with Challenge, Strategy, and Completion as its motivations (which represent the motivation groups of Mastery and Achievement).
Every team member should have one of his/her top gaming motivations represented in your team's selected set of motivations. This will help ensure that everyone on the team will be interested in designing and creating the game.
Next your team will use its set of targeted gaming motivations to develop one or more player experience goals.
A player experience goal is a description of the kind of gameplay experience you want players to have.
Screenshot from The Walking Dead game: you play as Lee, who rescues a girl named Clementine during a zombie apocalypse
For example, Telltale Games is a company that created a series of video games based on The Walking Dead comics. These point-and-click adventure games have been critically-acclaimed and very popular. The gaming motivations for these games include: Fantasy, Story, Strategy, and Excitement.
Some of the player experience goals for these games could be described as:
"Players will feel scared and uncertain as they try to survive in a world filled with zombies."
"Players will have to quickly make difficult decisions affecting characters they care about."
Notice several things about these player experience goals:
The player experience goals relate back to the targeted gaming motivations. For example, "scared and uncertain" relates to Excitement, "try to survive" relates to Strategy, "a world filled with zombies" relates to Fantasy, "quickly" relates to Excitement, "difficult decisions" relates to Strategy, "characters they care about" relates to Story.
The player experience goals focus on what the players will do and how they will feel. They do not focus on specific game features such as graphics, number of levels, etc. (The most specific reference in these examples is to "zombies" because these games are based on an existing comics series about zombies.) Instead, the player experience goals are later used to help design the specific game features. For example, these games use dark colors and dim lighting to help make the game feel scary. The games feature interactive conversations between the player and the other characters, so the player develops empathy for them. The games use a timer to force the player to make nearly every response or decision quickly. And so on...
The player experience goals help narrow the focus for thinking of game design ideas — but they are also broad enough to allow a range of possible game designs.
Your team will develop at least one player experience goal based on your team's set of targeted gaming motivations.
A persona is a model of a target user for a product or service, such as an app or a game. A persona summarizes the target user’s background, motivations, goals, and needs. The persona helps you design a solution to meet the target user’s expectations.
A persona is presented as a one-page description of an individual person, even though the persona actually represents a group of users with similar characteristics.
A persona is normally based on data collected from multiple users through observations, interviews, surveys, etc.
The persona for your video game will be created slightly differently. You will start with the gaming motivations and player experience goal(s) that your team has already decided to target for your game's design.
Then you'll create a realistic description of a player that has those motivations and goals. The player's description will include relevant background information (such as age, etc.) as well as information about their general needs and expectations related to video games.
Ideally, you should base your persona on data gathered from actual people representing your target players. In this case, you can use the data gathered from your team interview (and then add other information as necessary).
Your team will use the target player persona to help inform your decision-making as you design, build, and test your game. For example, when your team is debating whether to add a certain feature to the game, you could ask yourselves whether the feature would make the game a better experience for the target player persona.
Compare the top 5 gaming motivations of each member of your team. (Refer back to your team interview notes.)
Decide on 2-3 gaming motivations (representing at least 2 different motivation groups) that your team will be used to design its game.
Each team member should have at least one of his/her top 5 gaming motivations represented in your team's selection.
The gaming motivations are: Destruction, Excitement, Competition, Community, Challenge, Strategy, Completion, Power, Fantasy, Story, Design, Discovery.
The motivation groups are: Action, Social, Mastery, Achievement, Immersion, Creativity. Each group contains two of the motivations listed previously.
Keep in mind that motivations from the Social group may not work for a single player game (which is one of the design constraints in your project challenge).
Based on your team's set of 2-3 gaming motivations, develop at least one player experience goal for the game your team will design.
Use this template to create a persona representing a target player that your team will design its game for.
Start by listing the gaming motivations and player experience goal(s) that your team's game will target.
Develop a background and other relevant information for the target player. Include a photo, first name, and player type (e.g., casual gamer, hard-core gamer, etc.).
Even though the persona is fictional, the information should be realistic and consistent. The persona should seem like a real person, not a joke or a stereotype.
There are many factors that can affect the success of a project team, but two general factors are: 1. Selecting the right team 2. Having the right team approach
Selecting the right team means having a balanced team — a team that has enough in common but is also diverse enough.
Something that your team will want to have in common is at least one gaming motivation. If your team members don't have any top gaming motivations in common, it will be challenging to design a game that everyone is interested in creating. Teams with a common motivation or mission usually produce better results.
Your team should be diverse in terms of each person's interests, skills, background, and ideas. If everyone on the team wants to create the art for the game but nobody wants to program the game, you've got a problem. If everyone on your team thinks exactly alike, you'll be able to decide things easily, but your team might not consider all the possibilities. Diverse teams are like different pieces of a puzzle that fit together to form a complete picture. Diverse teams usually produce better results.
Having the right team approach means being able to effectively communicate and collaborate with each other.
An important first step in communication and collaboration is simply getting to know your team members better. This can help lead to empathy, respect, and trust — which are a foundation for better communication and collaboration.
Another important step is having a shared understanding of everyone's responsibilities. Create a team agreement that identifies the rules and roles that you've all agreed to follow.
Maintaining effective communication and collaboration is an ongoing process throughout a project. It's not always easy, but adhering to your team's core rules will help guide you through any issues that might arise. Teams with mutual respect and clear responsibilities usually produce better results.
Identify your team preferences, and then form into project teams based on the process determined by your teacher, which will take into account (among other things):
Number of People per Team
Overlap in Top Gaming Motivations
Complementary Interests in Team Roles
One person from the team should create a shared workspace for your team project that each team member can access. For example, create a Team Drive or a shared folder on Google Drive.
All electronic files related to the project should be saved in the shared workspace. Be sure your files are clearly named and logically organized, so it's easy to find information when you need it.
All physical documents related to the project should ideally be stored in the classroom (e.g., in a large envelope or folder). Your teacher will identify a location and procedure for storing and accessing these materials.
Conduct a team interview to learn more about each of your team members:
As a group, interview one team member at a time.
Assign someone to ask the questions.
Assign someone to record the answers.
Any other team members should be listening.
Complete a team agreement that identifies your team's rules and team members' roles.
In this second practice, you're going to create a top-down game that shows an overhead view of the player. It will be a space shooter game, similar to Asteroids. This game will feature a nice balance of player skill, random chance, and increasing challenge. It will reinforce the basic concepts of using Phaser to create a game, plus show you some advanced features in Phaser.
Our game will be an update to the classic Asteroids that we'll call Asteroids 2084. Here's its premise:
The year is 2084. The Mars colony is running out of water and needs to reach the icy moons orbiting Jupiter. You're a spaceship pilot sent on a scouting mission to clear a path to Jupiter through the asteroid belt. Asteroids threaten to collide with your ship from all directions. Your only option is to evade and destroy the asteroids before they destroy you.
In this practice, all the game assets (i.e., images and sounds) and some of the game code will be provided. Focus on two things: (1) becoming familiar with the new Phaser commands that will be introduced, and (2) applying the Phaser commands you've already seen.
PREVIEW VIDEO: Demo of Asteroids 2084
Prepare a new Phaser Game Template. Be sure to place a copy of phaser.min.js into the same folder that has your HTML, CSS, and JS files.
Download this assets.zip file, and extract the file contents, which will be a folder named assets that has two subfolders containing 12 images and 7 sounds. (To extract the file on a Windows computer, right-click on the downloaded zip file, and select Extract All. On a Mac, double-click on the downloaded zip file.)
Place the assets folder into your game template folder.
Test your Phaser game template by previewing the HTML file online. If everything's ready to go, you should see a solid black box (your blank game canvas) on your webpage.
This second practice game will be coded in 15 steps, with the last step being an extra credit challenge. Some steps will involve coding that's similar to things you did previously. However, most of the steps will also introduce and explain new Phaser features.
As a reminder, all your game coding is done in your JS file (code.js).
The steps are outlined below. The instructions for Steps 1-5 start on the next page.
Be sure that you completed 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.
Use this graph paper 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.
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 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 ChipTone.
In Step 14, you’ll have the option to create an animated sprite using a web app called Piskel.
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.
Your team will start to generate ideas for possible game designs that can provide the gaming motivations and player experience goal(s) listed in your target player persona.
Your team will generate and refine its ideas in a multi-step process: 1. As individuals, brainstorm ideas for multiple game concepts. Then as a team, share your ideas, and brainstorm additional ideas. 2. Your team will refine its ideas to create a game treatment (brief written summary with sample sketches) for each of its top 3 game concepts. 3. Your team will get outside feedback on its top 3 game concepts, and select a final game concept to prototype.
Thinking of ideas for a game concept can be challenging. As we saw earlier, there are many elements to a game's design. Rather than trying to figure out all the design elements of a game at once, it will help to start by thinking of ideas related to the premise and objective for a possible game:
The premise is a one-sentence summary of the context of the game.
The objective is the goal that the player is trying to achieve in the game.
For example, the premise of Crossy Road is that you're a chicken (or other character) trying to avoid obstacles as you cross a busy road. The objective of Crossy Road is to get the farthest distance possible before dying. As you can see, the premise and objective of a game are closely linked to one another.
The premise helps establish the story of the game, while the objective helps establish the mechanics of the game.
Premise → Setting (Game World) → Characters → Story
Objective → Conflict or Challenge → Basic Gameplay
Starting from a premise, you could then think of ideas for the setting, which could lead to ideas for the characters and story. (Though not every game will have characters and a story. Some games don't have a story, and some games don't even have characters.)
Starting from an objective, you could then think of ideas for the conflict or challenge in the game, as well as the basic gameplay (procedures, rules, etc.).
As a reminder, generic types of game objectives include (but are not limited to): capture or destroy, rescue, chase or escape, race, alignment, solution, outwit, exploration, etc.
Of course, you don't have to start with an idea for the premise or the objective. For example, you might first have an idea for a setting, which leads to an idea for a character, which leads to an idea for a conflict that the character has to resolve, which leads you to the character's objective in the game, which helps you establish the premise.
RECOMMENDATION: Simple sketches or diagrams can be a good way to record ideas — and can help spark even more ideas. For example, if you had an idea for a robot as a possible game character, make a simple sketch of a robot. The sketch might help you think of related ideas for other characters, a setting, a story, the objective, etc.
Each team member should individually brainstorm ideas for at least 2 different game concepts. You can record your ideas using this document.
Keep in mind your team's gaming motivations and player experience goal(s). These should be the starting point for your ideas.
Think of ideas related to the story (premise, etc.) and/or the mechanics (objective, etc.) for a possible game.
Don't criticize or reject any ideas yet.
Try to build on your ideas to create more complete game concepts.
Try to include simple sketches or diagrams.
As a team, share your ideas, and brainstorm more ideas together, so that the team has at least 5 different game concepts.
Don't criticize or reject any ideas yet.
Try to build on each other's ideas to create more complete game concepts or to develop alternative game concepts. Be sure to record these new ideas.
Be sure everyone has the opportunity to contribute and be heard.
Before you start developing a digital prototype of your game design, your team needs to create a plan to specify what you need to develop, what tasks are involved, how long each task should take, etc.
During the development of the digital prototype of your game, your Project Manager will track your team's progress and any problems that arise.
When managing a project, the quality and value of the completed work is constrained by the project's scope, schedule, and resources.
Scope represents the amount of work to be completed, such as the features and requirements for a product.
Schedule represents the amount of time available to complete the work.
Resources represent the amount of people (and money) available to complete the work.
A change in any one of these factors affects the other factors, as well as the quality and value of the completed work.
Quality can be measured internally by your team. Does the completed work meet your criteria and expectations?
Value has to be measured externally with your target users. Does the completed work provide value to the users by meeting their needs and expectations?
For example, if you increase the scope of a project (add more work), then you most likely need to increase the schedule (add more time), increase the resources (add more people or money), or increase both — in order to successfully complete the project and maintain the desired quality and value.
For this project — as is true for other technology projects that follow a Lean or Agile development cycle — you have a fixed schedule (limited amount of time) and fixed resources (limited number of people).
Therefore, given these limits, your team needs to estimate the scope of work that's possible to complete, based on the quality you want to produce and the value you want to provide.
Your team's game design may have many features and requirements. However, it might not be possible to include all of these in your digital prototype based on the limited schedule and resources you have available. Therefore, your team needs to decide which features and requirements to actually include in this initial version of your game.
Many designers refer to this process of estimating the scope for an initial product as defining the Minimum Valuable Product (MVP).
Be aware that Minimum Valuable Product does not mean providing the lowest possible value or providing an incomplete product. Instead, it means defining the mininum set (i.e., the core set) of functions and features for a product that still provides value to your target users.
The MVP still needs to be useful, usable (easy-to-understand and easy-to-use), and provide a desirable user experience. The MVP simply has a limited set of functions and features — but what it does include should still be valuable to the target users.
If your target users find your MVP to be valuable, then they will use it (and buy it) — and you can add other functions and features (to provide even more value) in the next version of the product.
On the other hand, if your target users do not find your MVP to be valuable, then you have an opportunity to improve it before you've invested too much time and resources.
So your team will need to review its game design document and decide what's the MVP version of your game that's possible to create with the limited amount of time and people you have available.
One of the responsibilities of the Project Manager is to track the team's progress to ensure tasks are completed on time.
A project schedule is created and used to list each task, its estimated duration, its due date, who it is assigned to, its start date, its current status, etc.
A common method to record the current status of a task is to estimate what percentage of the task is completed, such as: 0% (not started), 20%, 40%, 60%, 80%, 100% (completed).
The entire team is responsible for following the schedule and providing information to keep it updated. The Project Manager is responsible for ensuring the team stays on schedule — and for helping the team get back on schedule if tasks fall behind.
If a task is behind schedule, the Project Manager leads a team discussion to figure out the best way to get the task back on track, such as:
assigning additional people to help with the task
adjusting the task due date to add more time
reducing or changing the scope of the task
deferring an issue affecting the task
etc.
Another responsibility of the Project Manager is to track and resolve any problems that arise during the project.
An issues log is used to list each issue, its priority, who is assigned to work on it, what actions have been taken, its current status, etc.
A simple method to record the current status of an issue is to identify it as: Open, Closed, or Deferred.
An open issue still needs to be resolved.
A closed issue has been fixed and resolved.
A deferred issue has been placed on hold (to be fixed in the future, if possible).
If an issue arises, the Project Manager leads a team discussion to figure out what actions to take to try to resolve the issue, such as:
identify steps to try that might fix issue
gather additional information that may help understand or fix issue
get help from other people that may know how to fix issue
defer the issue (if it has minimal impact on product value, if it will take too much time to fix, etc.)
etc.
Each team member should preview his or her upcoming assignments:
Project Manager should review the rest of this assignment
Programming Lead should preview assignment 4-2 Code Game in Iterative Stages
Art Lead should preview assignment 4-3 Create Art and Sound for Game
Research Lead should preview assignment 4-4 Create Marketing Website for Game
Define your team's Minimum Valuable Product.
Identify and prioritize the core set of features in your game design required to create an initial version of your game that meets your targeted gaming motivations and player experience goals.
You can list or describe the MVP at the end of your game design document.
Your team will use this spreadsheet as your Schedule Tracker and Issues Log (which are listed on separate tabs). The Schedule Tracker has been partly completed — a set of generic tasks has been entered that should be sufficient for your project. Your team needs to enter some additional information into the Schedule Tracker:
Your team will have several weeks of class time to complete Phase 4. Your teacher will identify the specific deadline. Enter this deadline at the top of the schedule.
Estimate the duration (in class periods) to complete each task. (Some tasks already have estimated durations entered that should be sufficient.)
Based on the estimated durations and sequence of tasks, set initial due dates for each task. (Keep in mind that some tasks cannot be started until a listed precursor task is completed first.)
Assign each task to one or more team members. Remember that different people will work on different tasks at the same time (though some tasks cannot be started until its precursor task is completed). Some tasks might need more than one person (if you have extra people available).
During development (assignments 4-2, 4-3, 4-4), the Project Manager should track the team's progress and any problems that arise:
Make sure tasks are on track and completed on time by updating the Schedule Tracker (with start dates, current status, completed dates)
Make sure any issues that arise are recorded and resolved in a timely basis by updating the Issues Log
It is recommended to briefly check in with the team either on a daily basis or multiple times per week (such as Monday, Wednesday, Friday)
Make sure completed work has the intended features, quality, and value required for your MVP
Assist other team members with tasks as needed to keep the project on track
Now your team will use its game design document to create a paper prototype of your game. In the next assignment, your paper prototype will be playtested to help evaluate your game's design.
Even though a paper prototype cannot simulate all the aspects of a video game, you will hopefully discover that creating and playtesting a paper prototype should help:
clarify your team's conceptual model of the game
verify which game design elements are working well
identify which game design elements may need improvement
In the long run, the paper prototype should help you save time and effort because it allows you to improve your game design before you start coding the game and creating digital artwork.
DEMO VIDEOS:
Blank paper
Graph paper with large grid squares (helpful if drawing tile-based game world)
Pencils and eraser
Colored pencils or markers
Ruler (for drawing lines or drawing to scale)
Scissors
Clear adhesive tape
"Sticks" (for attaching to characters and game objects) — such as: small wooden sticks, strips of paperboard, etc.
Large envelope or folder (to store your paper prototype)
The paper prototype should include (as separate paper parts):
game world
user interface information displayed to player (such as: score, status, etc.)
player's character
non-player characters (opponents, allies, etc.)
key game objects (resources, obstacles, etc.)
Sometimes physical objects — such as coins, etc. — can be used to represent game objects.
If the game world extends beyond the game display, then tape together multiple sheets of paper (horizontally and/or vertically) to draw the game world.
Part of the game world for a side-scrolling platform game
You don't necessarily have to draw the entire level yet (or include all its details) — but create enough of the game world to be able to sufficiently test the gameplay.
Games typically have a user interface that displays certain information to the player (such as: score, health, time, lives, etc.). This information is often shown along the top and/or bottom edges of the game display.
If your game will only display information along one edge of the screen, you can use a strip of paper to draw your user interface.
If your game will display information along multiple edges (such as top and bottom, etc.), you can cut out the center of a sheet of paper to create a "window" — which will allow you to draw your user interface along whichever edges you need.
User interface window showing score, health, and remaining time along top edge and a lives counter along bottom edge.
The strip or window representing your user interface can then be placed on top of your game world during playtesting.
Games with extended worlds typically have the game display scroll to follow the player's movements. To simulate this, place your user interface on top of your game world, and then move the user interface as the player moves (while keeping the game world fixed in place) — or you can slowly pull the game world in the opposite direction that the player is moving (while keeping the user interface fixed in place).
Characters and other key game objects should be drawn on paper and cut out as separate parts.
Characters or other objects that are supposed to move can be attached to sticks to make it easier to move and use them during playtesting.
Even if game objects won't move in the real game, it is helpful to create them as separate parts — rather than drawing them on the game world — because it allows you to easily:
remove objects during playtesting (to represent them being collected or used)
replace objects during playtesting (to represent them changing or animating)
change the locations of objects in the game (to see how it affects the gameplay)
change the quantity of objects in the game (to see how it affects the gameplay)
You can "animate" characters or other objects by creating multiple versions of them (representing different actions or animation frames).
If helpful, you can also animate user interface elements. For example, a health bar could be animated by cutting out a small window for the bar, placing a colored strip of paper behind the window, and then moving the paper strip left or right to represent the health decreasing or increasing.
Your team should create a paper prototype of your game concept based on your game design document.
Focus on prototyping the core gameplay — at this point, don't worry about prototyping a title screen, menu screen, etc.
Your team's Art Lead will lead the creation of the art and sound for your video game. You'll need to gather or create each of the visual and audio assets needed for your game.
Visual assets might include:
Background images (either static images or scrolling tilesprites)
Spritesheets for animated characters
Images (or spritesheets) for game objects and user interface elements (icons, etc.)
Tileset (if using a to create your level)
Audio assets might include:
Sound effects
Background music
Voice-over tracks
It may take more time than you expect (or have available) to gather and create all the assets, so it will be important to know exactly what you need and to prioritize certain assets over others (in case you cannot find or create all the desired assets in time).
In addition, the Art Lead will need to provide your Programming Lead with a scale map of the game's level and a scale layout of the game's user interface. If necessary, use your concept sketches and paper prototype to help create these scale drawings.
The Art Lead will also need to work with your Programming Lead to:
add the visual and audio assets into the game
verify the layout and appearance of the game level and user interface (and make any necessary changes)
add any necessary visual effects into the game (such as: parallax, particles, tweens, etc.)
If not already done so, provide your team's Programming Lead with:
scale map of the game's level (game world) that shows each object, its position, and its size (if needed, include a map key to identify the objects)
scale layout of the user interface (within game display) that shows what information is displayed (text, icons, etc.), its position, and its size
Use graph paper to create the scale drawings:
Determine what assets are needed for your game, their priority, and whether to find or create them.
Refer to the "Artistic Design Elements (Aesthetics)" section in your game design document.
Make separate lists of all the visual assets and all the audio assets needed for the game.
Within each list, determine the priority (importance) of each asset, so you can decide the order in which you should find or create them.
For each asset, determine whether you'll try to find it — or will you create it.
Find or create the assets.
You might need multiple team members finding or creating assets.
Work with your team's Programming Lead to have the assets added into the game.
Be sure the asset file names make it easy to identify each asset. If necessary, rename the files to be more clear.
Upload the asset files into the assets/images subfolder or assets/sounds subfolder of your team's game folder.
For each spritesheet, the Programming Lead will need to know the frame width and height, which frames to use for each animation sequence, what speed (frames per second) to play the animation, whether the animation should loop, and when to play each animation.
For each audio file, the Programming Lead will need to know when the sound should be played (and whether it should loop). After adding the sounds, you'll have to test them in the game to verify what volume (between 0-1) to use for each sound.
Work with the Programming Lead to verify the layout and appearance of the game level and user interface — and help make any necessary changes.
Work with the Programming Lead to add any necessary visual effects into the game (such as: tilesprite scrolling, background parallax, particle effects, tweens, camera shake, camera flash, camera fade, tint colors, slow motion, etc.).
For each visual effect, the Programming Lead will need to know what the effect should look like — as well as when, where, and how long the effect should occur.
Your team needs to evaluate the digital prototype of your game by having users playtest the game to get feedback on what is effective and what could be improved.
The evaluation will focus on testing the functionality, usability, and user experience of your game. This evaluation can help provide evidence of how effective your game is at appealing to your target player.
Save a copy of this in your team's project folder.
IMPORTANT: Be sure to edit survey item #4 to replace the phrase "GAMING MOTIVATIONS" with your game's targeted gaming motivations (such as "Excitement and Strategy").
Recruit a total of 3-5 participants to conduct separate in-person playtesting evaluations.
Let the participant know it will take about 5-10 minutes.
Ideally, the participants should be similar to your target player persona.
Avoid using participants that evaluated your marketing website, since their familiarity with the website will affect how they view and evaluate the game.
However, if you do want to have the same set of participants evaluate both the game and the marketing website (in order to see how the game and website are consistent with each other), then recruit 6-10 participants (i.e., twice as many): Have half the participants evaluate the game first and then the website. Have the other half evaluate the website first and then the game. This will allow you to compare the responses from the two groups and see how the order of the evaluations affected the responses.
At the start of each evaluation, briefly explain to the participant that your team wants to get feedback on a prototype of a new video game. Be sure to mention that:
the game is being tested, not the participant
the game is a work-in-progress, so you want constructive feedback on what’s working well and what could be improved
the participant can ask questions during the evaluation, but you might not be able to answer certain questions until after the evaluation is completed
Tell the participant to “” as they play the game for several minutes.
If necessary, you may need to first provide brief instructions on the controls that the player will use to play the game (i.e., which keys to press for specific actions, etc.).
If time allows, the player can play the game more than once.
Observe as the participant plays the game, and be sure to record notes on any useful feedback.
If the participant has questions about the game, don't explain the game to them. Instead, try redirecting the participant back to the game by responding with your own question (such as: What do you think? What do you expect should happen? etc.). If necessary, let the participant know that you can answer their question after the evaluation is completed.
After several minutes of playtesting, have the participant complete the online evaluation survey.
Be sure to thank the participant for their time and feedback. If needed, you can respond to any follow-up questions the participant has.
Repeat Steps 3-7 with each participant.
Most entrepeneurs or companies create a website to help promote and market new products. Sometimes this is done as a webpage on a crowdfunding website (such as , , etc.) — other times, this is done as a webpage or mini-site on a company website.
Your team's Research Lead will create a one-page marketing website for your video game. This website will be made public to allow more people to learn about your game and play your digital prototype.
Your team's website should briefly explain or show:
Game Title
Marketing Tagline for Game
Game's Premise and Setting
Game's Characters and Story (if applicable)
Game's Objective and Conflict/Challenge
Basic Gameplay and Gameplay Progression
Link to Digital Prototype (so visitors can play game)
Names and Roles of Team Members
Be sure to incorporate visuals into your website, possibly including:
Concept Sketches of Game World
Concept Sketches of Characters
Photos or Video of Paper Prototype
Completed Game Art, such as Character Sprites, etc.
Screenshots of Completed Game
Demo Video of Gameplay
Be sure to design the content and style of your website to appeal to your target player persona. Think about ways that your website can communicate (directly or indirectly) the gaming motivations and player experience goals provided by your game.
Gather and edit the content (text, images, etc.) for your team’s marketing website.
Your team's game design document should already contain much of the text you need. If necessary, edit or revise this text for the website.
Be sure to structure your content (with section headings, etc.) to make it easy for people to follow or scan the content.
Be sure your content is clear, concise, engaging, and professional.
Determine the layout and style for your team's one-page website.
For example, create a wireframe showing the layout of the content, and decide on the visual style for the content (fonts, colors, etc.).
Be sure your layout and style is clear and engaging — and reinforces your game's theme and visual design.
If necessary, include temporary placeholders for images or videos that aren't ready yet (screenshots, sprites, demo video, etc.). Be sure to replace them later with the final visuals, in order to complete the website.
Have other members of your team review the website for possible improvements to make.
In a later assignment, your team will evaluate the website by testing it with users.
Your team's Programming Lead will lead the programming of your video game. You'll need to code the game in iterative stages — by adding, testing, and refining one feature at a time. You'll start with the core game mechanics, and work stepwise to bring your game design to "life" in code.
Your team's Art Lead will need to provide the Programming Lead with a scale map of the game's level and a scale layout of the game's user interface.
The Programming Lead will work with your team's Art Lead to:
add the visual and audio assets into the game (such as: animated sprites, sound effects, etc.)
verify the layout and appearance of the game level and user interface (and make any necessary changes)
add any necessary visual effects into the game (such as: parallax, particles, tweens, etc.)
Your team's Research Lead can assist with testing the game during coding as another "pair of eyes" to check for any bugs or possible improvements.
Before coding a complex computer program, developers (aka coders or programmers) plan out the code in some way — just as you might create an outline to plan out an essay or article before writing it. The plan or outline for a computer program often consists of pseudocode or a flowchart that describe how the program should work.
It is best to first focus on outlining your core gameplay — which will become the code in your game's update()
function (as well as code in your custom functions for collisions, etc.).
Keep in mind that a program can usually be coded or structured in different ways — and still work correctly. However, sometimes there are certain steps that have to occur in a specific order to make logical sense — otherwise, the program won't work correctly.
Pseudocode is an informal step-by-step description of what a computer program should do. Pseudocode is often written in plain language (i.e., English — or whatever language you normally use). Some developers use a simplified form of "code-like" language to write their pseudocode. Other developers combine plain language plus "code-like" language in their pseudocode.
The advantage of writing pseudocode is it makes it easier for you to figure out how your program will work — before you have to write any actual code. Pseudocode is shorter, simpler to understand, and doesn't require all the details to make sense — whereas actual computer code is longer, more abstract, and requires precise logic and syntax to work correctly.
A flowchart is a diagram representing the steps and decisions in a process, such as a computer program (or part of a program). Each step in the process is represented by a shape with a brief text description. Different shapes are used to represent: process steps, input or output of data, decision points, etc. The shapes are connected by arrows to represent the path (or flow) of the process. The path can split into different branches (at decision points), and different branches can also merge back together.
A flowchart has all the advantages of pseudocode, plus the flowchart visualizes how your program will work. It may be easier to verify your program's logic (and catch any mistakes) using a flowchart.
However, a flowchart may take more time to create. It may be faster and easier to first write pseudocode (and then turn that into a flowchart).
Code the game in iterative stages by adding, testing, and refining one feature at a time. It will make it easier to troubleshoot your code if something doesn't work as expected.
Remember that your Phaser game code has three main functions named preload()
, create()
, and update()
— which are used for different purposes. You will progressively add Phaser commands inside these different functions. You will also need to create custom functions for collision events, etc.
For example, to add the player sprite to the game, you'll need to:
declare a global variable for the player, such as: player
load an image or spritesheet for the player in the preload()
function
add player
as a sprite in the create()
function
set values for certain properties (such as: gravity, etc.) of player
in the create()
function
After adding the code for a feature, test it out in your game to see whether you need to revise the code (and retest it), so the feature works how you expect.
For example, if the newly-added player sprite isn't located in the correct position in the game world, adjust the sprite's position in the code, and refresh the preview of the game to see your changes. Of course, later, you might need to change some of these properties again — or to add other properties or commands — but get the basics of this feature working before coding the next feature.
RECOMMENDED: Save back-up versions of your code.js
file at least once per day — in case you encounter major issues in your code and want to revert back to an earlier version. For example, make a copy of the file, and rename the copy by adding the date (MM-DD-YY) to its filename, such as: code-10-03-17.js
OPTIONAL: Some people find it helpful to code and test new features in a separate copy of the game code (sort of like a "sandbox environment") before adding the new features into their "official" game code.
Create a pseudocode outline of the core gameplay.
OPTIONAL: Use your pseudocode to create a flowchart of the core gameplay.
Obtain the following from your team's Art Lead:
scale map of the game's level (game world) that shows each object, its position, and its size (if needed, map should include a key to identify the objects)
scale layout of the user interface (within game display) that shows what information is displayed (text, icons, etc.), its position, and its size
Find or create "placeholder" images and sprites that you'll use temporarily until the Art Lead provides the final visual assets later. (You can also use placeholder sound effects until you obtain the final audio assets later.)
Ideally, the placeholder images or sprites should be the same size (width and height) as the final assets are supposed to be (or as close to the same size as possible).
Later, once you have the final asset files, the only change you might need to make is to update the filenames of the assets loaded in your preload()
function.
For example, you can temporarily load an existing spritesheet (from another game) for the player's character.
In fact, all images and sprites are technically rectangles, so you can even just create different colored rectangle images (of the correct sizes) to use as your temporary assets.
Code your game in iterative stages by adding, testing, and refining one feature at a time.
Load your placeholder assets in the preload()
function.
When building your game world in the create()
function, remember that all 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. Adjust their order as necessary for your game (e.g., so the player sprite isn't hidden behind a background image, etc.).
Use your pseudocode outline to help create your code for your update()
function (and for certain custom functions).
While coding, be sure to continually test your game to identify any bugs or improvements. The Research Lead can also help test the game, so you have multiple reviewers.
The Art Lead will help verify the layout and appearance of the game level and user interface — and help make any necessary changes.
The Art Lead will also help with adding any necessary visual effects by identifying what each effect should look like — as well as when, where, and how long each effect should occur.
When the Art Lead has provided the final visual and audio assets, be sure to revise the game code as needed to use the final asset files.
In a later assignment, your team will have external playtesters evaluate the game.
Your team needs to analyze the feedback and responses from the evaluations of your game and marketing website to determine what is working well and what could be improved.
If time allows, your team will implement improvements as feasible.
Repeat the process to create a separate summary of evaluation findings for your marketing website.
Based on the time available, use your evaluation findings to implement improvements (as feasible) to your team's game and/or marketing website.
Your team needs to evaluate its marketing website by testing it with users to get feedback on what is effective and what could be improved.
The evaluation will focus on testing the utility, usability, and user experience of your website. This evaluation can help provide evidence of how effective your website is at marketing your game.
Save a copy of this in your team's project folder.
Recruit a total of 3-5 participants to conduct separate in-person evaluations of your team's website.
Let the participant know it will take about 5-10 minutes.
Ideally, the participants should be similar to your target player persona.
Avoid using participants that evaluated your game, since their familiarity with the game will affect how they view and evaluate the website.
However, if you do want to have the same set of participants evaluate both the game and the marketing website (in order to see how the game and website are consistent with each other), then recruit 6-10 participants (i.e., twice as many): Have half the participants evaluate the game first and then the website. Have the other half evaluate the website first and then the game. This will allow you to compare the responses from the two groups and see how the order of the evaluations affected the responses.
At the start of each evaluation, briefly explain to the participant that your team wants to get feedback on a marketing website for a new video game. Be sure to mention that:
the website is being tested, not the participant
the website is the focus of the evaluation, rather than the game
the website is a work-in-progress, so you want constructive feedback on what’s working well and what could be improved
the participant can ask questions during the evaluation, but you might not be able to answer certain questions until after the evaluation is completed
Tell the participant to “” as they use the website for a few minutes to learn about the game by finding answers to these questions:
What does the game involve?
What kind of players does the game seem targeted towards?
Observe as the participant uses the website, and be sure to record notes on any useful feedback.
If the participant has questions about the game or website, don't explain the game or website to them. Instead, try redirecting the participant back to the website by responding with your own question (such as: What does the website say? Where would you expect to find that on the website? etc.). If necessary, let the participant know that you can answer their question after the evaluation is completed.
After a few minutes, have the participant complete the online evaluation survey.
Be sure to thank the participant for their time and feedback. If needed, you can respond to any follow-up questions the participant has.
Repeat Steps 3-7 with each participant.
Everyone should keep in mind the .
Review the Project References for and for help
Construct the website using a tool such as (available within Google Drive – if needed, Google has a guide) – or code the website directly using HTML and CSS.
EXAMPLE:
In fact, many developers include some or all of their pseudocode in their actual code by writing each line of pseudocode as a . For example, in JavaScript, you can make a comment by typing two forward slashes //
at the beginning of the line. Developers then add the actual code below each comment line. The pseudocode comments help the developer as he or she is creating the code, and it also helps explain the code to anyone that reviews (or revises) the code later.
VIDEO:
EXAMPLE:
You can create a flowchart using web apps such as (available within Google Drive), , , etc. Of course, you can also draw a flowchart by hand.
TUTORIAL:
You can use an image editor (such as ) to create rectangles of the correct sizes, and fill each rectangle with a different color (to represent different characters or objects).
Here is a .
Review the feedback notes and survey responses from all the participants that evaluated your game. Analyze this evaluation data to summarize the strengths of your game and any issues that might require improvements. Record the summary of your evaluation findings in .
Your team will participate in a poster presentation to demonstrate and explain your project to a public audience. The poster presentation will be like a game convention, where you are pitching your game design and demonstrating your digital prototype.
Your team needs to create a poster that helps describe your game and how your team researched, designed, prototyped, and evaluated it. The poster will be a visual reference to supplement the in-person explanation and demonstration that your team gives during the presentation event.
The poster should be clear, concise, engaging, and professional.
Your team's poster should briefly describe or show:
Game Title
Targeted Gaming Motivations and Player Experience Goals
Summary of Game Concept
Game's Premise and Setting
Game's Characters and Story (if applicable)
Game's Objective and Conflict/Challenge
Basic Gameplay and Gameplay Progression
Summary of Process used to Research, Design, Prototype, and Evaluate Game
Names and Roles of Team Members
Be sure to incorporate images into your poster, possibly including:
Target Player Persona
Concept Sketches for Game
Photos of Paper Prototype
Completed Game Art, such as Character Sprites, etc.
Screenshots of Game
Examples of Game Code
The standard size for a large poster is 36 inches wide by 24 inches tall. Your teacher will assist with getting the posters printed.
Gather the content (text and images) for your team’s project poster.
You should be able to use or edit content from your team's game design document and/or marketing website (but don't necessarily create a poster replica of the website).
Think about ways to use the content of your poster to support or supplement your verbal presentation.
The poster won't necessarily include everything about your project — but the poster should have enough information to make sense if someone were to only look at your poster.
Be sure the content is clear, concise, engaging, and professional.
Determine the layout and style for your team's poster.
Sketch one or more possible layouts for the poster content. You can use a standard sheet of paper to sketch a simplified, small-scale mockup.
Decide on the overall style for the poster (such as: fonts, colors, etc.).
Be sure the layout and style of the content will be clear, engaging, and professional.
Create a digital version of your poster.
You can use this Google Drawing template which is preset to 36 inches wide by 24 inches tall
Be sure you're logged in to your Google Account. From the File menu, select Make a copy. Save the copied file to your shared team folder in Google Drive.
Zoom in or out as needed to add and edit text, images, etc., but do not change the canvas size. (Otherwise, your digital poster proportions might not match the printed poster size).
Be sure your poster doesn't have too much text (people might not read it all) or too little text (people might not understand the poster). For example, certain images may need a caption to be clear.
Be sure you have strong contrast between text color and its background color.
If the background is dark, the text should be light.
If the background is light, the text should be dark.
Be sure the font types, font styles, and font sizes used on the poster will allow people to easily read the text from several feet away.
Fancy fonts may be harder to read. Limit fancy fonts to short text (such as titles or headings). Use simple fonts for longer text.
Small font sizes (e.g., less than 36) may be harder to read from several feet away.
Have a physical poster printed.
Save or download a copy of your poster as a PDF for printing.
Be sure the PDF has a filename that clearly identifies your class period and team.
Provide the PDF to your teacher for printing.
Reflecting back to think about what you've experienced is an important part of learning and improving. Reflection is useful throughout an experience, but it is especially helpful at the end because you have the benefit of reflecting on the entire experience from start to finish.
Write brief responses to the prompts in this personal reflection document.
Your teacher may have other reflection activities for you to complete.
Games have certain design elements that help define them as being games — whether it is a board game, a card game, a sport, a video game, etc. If you're going to create a game, you'll need to make design decisions about all the key elements of your game.
For example, we all recognize checkers, soccer, and Space Invaders as being examples of games. What do these have in common that defines them as being games?
We would probably agree that one element that games have is players — people that agree to participate in the game. Checkers has two players. Soccer features two teams of players. Space Invaders is a single-player game. Every game has one or more players as part of its design.
Game designers have come up with different ways to define the elements of games. In this assignment, you will define your own set of game design elements by constructing an affinity diagram.
An affinity diagram is a way to take a large set of information (such as ideas, etc.) and sort it into a smaller set of groups that reveal patterns and relationships.
Constructing an affinity diagram involves three basic steps: 1. Record each idea separately. It helps to use index cards or sticky notes, if possible. Otherwise, make a list. 2. Sort related ideas into groups. You can have as many groups — or as few groups — as you need, based on the patterns in the ideas. 3. Label each group to identify the pattern or relationship they represent.
After you construct the affinity diagram, you'll compare your results with a specific list of game design elements that we'll be using for this project.
Work with a partner or small group to construct an affinity diagram of the elements that define games. Record your ideas, sort them into groups, and label the groups.
Discuss your findings as a class. How similar are they?
Compare your findings to this reference list of game design elements. How similar are they?
Use the reference list to analyze the design elements of an existing game by completing this template. As an example, here is an analysis of the game design elements of Space Invaders.
In the previous assignment, your team evaluated your game treatments and selected one game concept that your team will prototype for its project.
Next your team will prototype its game in a series of stages:
Conceptual Prototype → Physical Prototype → Digital Prototype
The conceptual prototype will be a game design document that describes your game in detail and includes concept sketches. This represents your team's mental model of the game.
A game design document is a more detailed version of a game treatment. Besides a summary of your game concept, the game design document has specifics about your concept's game design elements:
Structural Elements — which determine your game's mechanics
Dramatic Elements — which determine your game's story
Artistic Elements — which determine your game's aesthetics
Once your team has a first draft of its game design document, your team will create a physical prototype — a paper version of your game that you'll use to demonstrate and test the game design.
Even though a paper version of a video game may seem unusual, you can actually learn a lot from the paper prototype — before you do any coding or create any digital artwork.
Based on the playtesting of your paper prototype, your team will refine your game's design (and update the game design document). This will put your team in a better position to create your digital prototype — a working computer version of the game.
Your team should use this template to create a first draft of your game design document.
Start by transferring the information from your game treatment. Your team may need (or want) to revise some of this information based on the feedback received in the previous assignment.
Brainstorm, discuss, decide, and document the game design elements for your team's concept.
The goal is to have a sufficiently clear sense of your team's game's design, so you're ready to create a paper prototype of it for testing and feedback.
The audience attending the public presentation will walk around and spend a few minutes with different teams to learn about their project. You will most likely have to present and demonstrate your project multiple times to various small groups of people.
Prepare and practice a verbal presentation that describes the problem your team targeted and explains/demonstrates the solution you created.
Have a short "pitch" (less than 3 minutes) that you can use to engage and educate audience members.
For audience members that are interested in learning more, be prepared to explain your game in more detail and how you researched, designed, developed, and evaluated it.
Think about ways to use your poster, game, and verbal presentation to supplement each other.
Each team member should be prepared to help present as needed.
For the day of the event, be sure you have the following:
Your team's project poster
Internet-connected computer (to demonstrate your game and show your marketing website)
OPTIONAL: Any other materials that your team may want to use in its presentation (such as concept art, paper prototype, etc.)
During the presentation:
Be friendly, confident, and engaging. Treat every audience member as a potential judge (because some of them might be judges).
Be sure to give audience members an opportunity to play your game.
Be prepared to respond to questions from the audience members.
Be respectful of your audience's time — they also want to see other team's projects during the event.