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...
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.
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.
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?
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.
During this project, the entire class represents a video game company that will be divided into teams. Each team will design and develop its own video game concept. The teams will help improve each other’s games through feedback and playtesting.
At the conclusion of the project, all teams will present their video games at a public game convention.
2D game world
Single player game (or multi-player game using single computer)
Single level game (or single level of multi-level game)
Innovative: Game is unique (could be unique remix of existing game concepts).
Engaging: Game is fun and motivating to play (based on targeted gaming motivations).
Functional: Game is functional and has no major problems (such as loopholes in rules that provide unintended advantage, dead ends that prevent player from progressing, etc.).
Usable: Game provides clear feedback to player and is easy for new player to understand (such as learning game's controls, rules, objectives, etc.).
Balanced: Game mechanics are fine-tuned to produce desired gameplay experience (such as balancing success vs. failure, risk vs. reward, skill vs. chance, etc.).
Each team member will be a Game Designer, contributing to the ideas, decisions, and work related to the design of their team's game.
In addition, each team member will have at least one other specialized role:
Programming Lead: leads programming of game
Art Lead: leads creation of art and sound for game
Research Lead: leads playtesting and marketing of game
Project Manager: leads team to ensure work is high-quality and completed on time
The recommended team size is 2-4 people. Your teacher will determine how many people each team must have:
Teams of 2 people need to have separate people acting as Programming Lead and Art Lead. Each person will also take on one of the other remaining roles. It is recommended to have the Programming Lead also act as the Project Manager and to have the Art Lead also act as the Research Lead.
Teams of 3 people need to have separate people acting as Programming Lead, Art Lead, and Research Lead. One person will also act as the Project Manager. It is recommended to have the Research Lead also act as the Project Manager.
Teams of 4 people will have separate people acting in each of the four roles.
Each team member is responsible for contributing meaningfully at all times to the team project. Each team member must be willing and able to help work on any task at any time when needed. One of the responsibilities of the Project Manager is to ensure the team is working and collaborating effectively.
IMPORTANT: Each lead person is responsible for planning and delegating the tasks for their speciality area. However, this does not mean that only one person completes that type of work. At certain times, multiple team members will be working together on the same task, while at other times, they may be working in parallel on different tasks.
Video Game that is published online and ready to play
Marketing Website for game, which includes a gameplay demo video and a link to play game
Poster for final presentation that highlights key features of game and key steps in creating game
Presentation at public game convention to demonstrate game and explain how it was designed, built, and tested
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 . 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.
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.
An 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.
Compare your findings to this . How similar are they?
Use the reference list to analyze the design elements of an existing game by completing . As an example, here is an analysis of the .
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.
Browser-based game created using
PREVIEW VIDEO:
Prepare a new . Be sure to place a copy of phaser.min.js into the same folder that has your HTML, CSS, and JS files.
Download this 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.)
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 1-5 on the previous page.
IMPORTANT: Take the time to read each step, and try to understand how the code works.
Let's make the game respond if the spaceship collides with any of the asteroids.
Phaser's Arcade Physics system has two methods (functions) to detect when two game objects "touch" each other:
collide()
checks to see if the two objects are colliding and keeps them separated (so they don't overlap) — if one or both of the objects is moving, they can transfer momentum between each other (if you set a body.bounce
value for the objects, they'll bounce off each other)
overlap()
simply checks to see if the two objects are overlapping (but the objects will pass right through each other)
The two objects being checked can be individual objects and/or groups of objects, such as: Sprite vs. Sprite, Sprite vs. Group, Group vs. Group. For groups, each member of the group is checked individually in sequence.
Add this Phaser command in your update()
function (before the if-else statements for the player movement):
This command tells the Arcade Physics system to check for collisions between the player
sprite and any sprite in the asteroidGroup
.
Refresh your HTML preview to verify that the spaceship can collide with the asteroids (and it will change their velocity).
You probably noticed that the spaceship and asteroids act like they collide before they touch. This is because the player sprite and each asteroid sprite are actually rectangles that have transparent areas. The transparent areas of the sprites are colliding, so it seems like they collide too soon.
Luckily, Arcade Physics has two ways that we can change the collision area of a sprite's body
to more closely match its visual appearance:
body.setCircle()
allows you to change the sprite's collision area to be a circle of a specified radius (which you can offset from the top-left corner of the sprite)
body.setSize()
allows you to change the sprite's collision area to be a rectangle with a specified width and height (which you can offset from the top-left corner of the sprite)
Since the asteroids are basically circles, let's set their collision areas to be circles.
The asteroid sprite frames are 40 pixels in width and 40 pixels in height. If viewed in an image editor, you would see that the asteroid is about 30 pixels across in diameter with about 5 pixels of transparent area on all sides:
The pink circle drawn above represents the circular collision area that we want each asteroid to use (instead of the full image).
Add this Phaser command inside the for
loop that adds the asteroids to the asteroid group (after the code that sets the anchor for each asteroid):
The setCircle(15, 5, 5)
sets the body collision area to be a circle with a radius of 15 pixels (30 pixels in diameter) that is offset from the left edge of the frame by 5 pixels in the x-direction and offset from the top edge by 5 pixels in the y-direction. This offset will center the circle within the frame.
Let's do something similar with the spaceship, which has frames that are 64 pixels in width and 64 pixels in height. Visually, the spaceship is not a rectangle, and it's not a circle — it's more like a triangle. However, triangle is not an option for the body collision shape. Let's just use a circle because it's close enough (besides the ship rotates in a circular motion):
The pink circle drawn above represents the circular collision area that we'll use for the spaceship. It's not a perfect match, but it's better than the using the full image for collisions.
Add a Phaser command to set the body collision area of the player
sprite to be a circle with a radius of 20 pixels that is offset by 12 pixels from the left of the frame and 12 pixels from the top of the frame. Add the command in the create()
function (after the command that enables physics on the player
sprite).
Refresh your HTML preview to verify that the spaceship and asteroids are now visually closer when they collide with each other.
What should really happen in our game when the spaceship collides with an asteroid is that the spaceship should be damaged or destroyed and the asteroid should be destroyed.
Modify your existing collide()
statement in the update()
function, so it looks like this:
This command tells the game to run a custom function named collideAsteroid
when the physics system detects a collision between the player
sprite and any sprite in the asteroidGroup
. (null, this
are two additional parameters that you'll always include). The two specific sprites involved in the collision will be passed into the custom function as parameters (variables).
Now you have to actually create a custom function named collideAsteroid
, and add code inside to do something when this type of collision occurs.
Add this code to the very end of your code.js file (after the update()
function):
This creates the custom function, though it has no code yet inside its curly braces { }
.
Notice that we included player, asteroid
inside the parentheses after the function's name. These represent the parameters — variables that are passed into the custom function when it is called. The parameters represent the specific sprites involved in the collision, in the same order they are listed in the game.physics.arcade.collide()
function.
The parameter names act as local variables inside the custom function, so the names of the parameters do not have to exactly match the sprite names in the collide()
function. However, it will be much easier to understand what's happening in the custom function if the names are either identical or similar. Inside the custom function, whatever you do to the parameter variables will affect the sprites they represent.
Within the custom function collideAsteroid()
, the parameter player
represents the player
sprite (easy to understand because both use the same name). The parameter asteroid
represents the specific asteroid sprite from the asteroidGroup
that collided with the player
sprite. (We could use ship
as the parameter name to represent the player
sprite and rock
as the parameter name to represent the sprite from the asteroidGroup
— and the custom function would work exactly the same. However, the code might seem less clear to us.)
Alright, enough of that — let's destroy the asteroid involved in the collision.
Add this Phaser command inside the collideAsteroid()
function by pasting it between the curly braces { }
:
The kill()
command removes a sprite from the game. Phaser actually keeps the sprite object saved in memory, in case you decide to reuse the sprite later. Phaser has commands to revive()
or reset()
a "dead" sprite to add it back into the game.
Refresh your HTML preview to verify that each asteroid will disappear if it collides with the spaceship.
Now let's add a sound effect that will play one time whenever a collision occurs:
Use boomSound
as the name of the global variable for the sound.
Use assets/sounds/boom.wav for the sound file. Assign a unique asset key name to the sound, such as boom
.
Add boomSound
to the game with a volume of 0.3
.
Add a Phaser command inside the collideAsteroid()
function to play boomSound
.
Refresh your HTML preview to verify that the explosion sound plays whenever an asteroid collides with the spaceship.
Add a Phaser command inside the collideAsteroid()
function to kill the player
sprite.
Let's also add a visual effect as feedback to make the collision feel more realistic.
Add this Phaser command inside the collideAsteroid()
function:
This command will cause the game screen to "shake" briefly when a collision occurs:
The first number 0.02
represents the intensity of the camera shaking as a percentage of the camera size (0.02 = 2%).
The second number 250
represents the duration of the shaking in milliseconds (1000 milliseconds = 1 second).
Refresh your HTML preview to verify that the game display "shakes" and the spaceship disappears when it collides with an asteroid.
Let's add an explosion effect when the player
sprite is killed. The explosion will be an animated sprite that we'll hide from view until needed.
Use explosion
as the name of the global variable for the sprite.
Use assets/images/explosion.png for the spritesheet image. It contains 16 animation frames that are each 128 pixels in width and 128 pixels in height. Assign a unique asset key name to the spritesheet, such as explosion
.
In your create()
function (after the for
loop that added the asteroids), add the explosion
sprite to position 100, 100
of the game (this will just be a temporary position), and set the sprite's anchor to its center.
Add an animation to the explosion
sprite named explode
that will play all 16 frames in order (list an array from 0-15) at a frame rate of 30, with loop set to false
.
Refresh your HTML preview to verify that the first frame of the explosion sprite is visible in the upper left of the game screen.
We're actually going to hide the explosion
sprite until we need to display it (and then we'll move it to the player
sprite's position when it's killed).
Add this Phaser command to your create()
function (right after the command that added the animation for the explosion
sprite):
Refresh your HTML preview to verify that the explosion sprite is no longer visible.
When the player
sprite is killed, we will move the explosion
sprite to the same position where the player
was located, make the explosion
sprite visible, play the explode
animation, and then remove the explosion
sprite again.
Add this Phaser code in your create()
function (after the code that added the player
animation, but before the code that added the asteriodGroup
):
This code adds an event when the player
sprite is killed. This event will perform the code listed inside the function's curly braces { }
:
reset()
makes the explosion
sprite visible again and moves it to the x-y coordinates listed inside the parentheses. In this case, we want to make it appear at the player
sprite's position.
The command to play the animation includes an extra argument at the end:
explode
is the animation key name
30
is the frame rate
false
means do not loop the animation (play it only once)
true
means kill (remove) the sprite after the animation has ended
Refresh your HTML preview to verify that the explosion animation will appear (and then disappear after playing) when and where the spaceship collides with an asteroid.
Let's give your player
a way to fight back. It's time to add a weapon.
Phaser's Arcade Physics system includes a Weapon object with properties and methods related to firing bullets. The weapon is really just a group of bullet objects.
The "bullets" for a weapon can be any image (or spritesheet) that you want: bullets, lasers, arrows, fireballs, etc. If you want the weapon to fire smiley face emojis, you can do that.
Adding a weapon to the game involves 3 steps:
Declare a global variable for the weapon.
Load an image (or spritesheet) for the bullets into memory in the preload()
function.
Add the weapon to the game in the create()
function.
The weapon can then be fired in the update()
function.
Let's get ready to add your weapon:
Add a global variable named laser
for the weapon.
Preload assets/images/laser.png for the bullet image. Assign a unique asset key name to the image, such as bullet
.
Remember that the order in which visual objects are added in the create()
function determines which objects appear in front of (or behind) other objects. We want the weapon's bullets to look like they are being fired from beneath the spaceship, so we need to add the weapon before the player
sprite is added in the code.
Add this Phaser code in your create()
function (before the code that adds the player
sprite):
Let's examine this code:
The first line adds the weapon. 10
represents the number of bullets to create in the bullet group. The bullets will be recycled as they are fired, so this sets an upper limit on the maximum number that could be on-screen at any one time. 'bullet'
is the asset key name that it will use for each bullet.
The second line will kill (remove) a bullet if it leaves the game display boundaries. Phaser has multiple options for bulletKillType.
The third line sets the bullet speed in pixels per second.
The fourth line sets a limit on the firing rate. In this case, a bullet can only be fired every 250 milliseconds (even if the player holds down the fire key).
The last line changes the bullet's collision area to be a rectangle with a width of 24 pixels and height of 12 pixels offset 6 pixels from the left edge of the bullet image and offset 6 pixels from the top edge of the bullet image.
There are many other Phaser.Weapon properties that you could use in other games, if needed.
We want the laser
weapon to be associated with the player
sprite. Phaser has a method for the weapon to track (follow) another sprite, so the bullets will be fired from that sprite's position.
Add this Phaser command in your create()
function (after the code that added the player onKilled
event):
This command will ensure the laser
weapon tracks the position of the spaceship:
player
represents the name of the sprite that the weapon will track
0, 0
represents an x-y offset from the sprite's anchor position. Using 0, 0
means the weapon will fire from the player
sprite's center.
true
means that the weapon should also track the sprite's rotation. We need this for our game since the ship rotates, but other games may not need this (so they could list false
or nothing at all).
Now we just need a way to fire the laser
weapon. Remember that way back in Step 2, you assigned the spacebar as an input named fireKey
.
Add an if
statement in your update()
function (after the if-else statements for the player movement) to check when the fireKey
is being pressed.
Add this Phaser command to your code, so it will be performed when your new if
statement is true:
Refresh your HTML preview to verify that your spaceship fires the laser weapon when the spacebar is pressed.
You'll see that the laser "bullets" don't do anything yet — we'll have to add a collision between the laser bullets and the asteroids.
You'll also discover that the laser can still be fired even after the player
sprite has been killed.
Modify your existing if
statement for the fireKey
, so it will check whether the fireKey
is being pressed AND the player exists.
Remember that &&
is used to represent AND
Command to check if player exists (returns true or false): player.exists
Refresh your HTML preview to verify that the laser weapon can only be fired when the spaceship exists.
Let's add a sound effect that will play whenever the weapon is fired:
Use fireSound
as the name of the global variable for the sound.
Preload assets/sounds/fire.wav for the sound file. Assign a unique asset key name to the sound.
Add fireSound
to the game with a volume of 0.1
. Add the sound after the code that set the laser
to track the player
sprite.
Add this Phaser code inside your create()
function (after the code that added fireSound
):
This code will play the fireSound
every time the laser
weapon is fired. The reason for playing the sound using this code is the weapon has a fireRate
that limits how frequently it can fire. If we played the sound whenever the fireKey
is being pressed, the sound would play too frequently (and would play even when a new bullet is not actually fired). So the code above will keep the sound effect synced with the actual weapon firing.
Refresh your HTML preview to verify that the sound effect plays whenever the laser weapon is fired.
In your update()
function (directly after your existing collide()
function), add a new Arcade Physics collide()
function to check for collisions between the laser bullets and the asteroids:
Check for collisions between laser.bullets
and asteroidGroup
. List these two sprite groups in this order.
When a collision is detected, it should run a custom function called shootAsteroid
.
Add a new custom function named shootAsteroid()
(after your collideAsteroid()
function). Inside the parentheses ()
after the function name, list bullet
and asteroid
as parameters (in that order).
Inside the curly braces { }
of the shootAsteroid()
function, add Phaser commands to:
kill the asteroid
kill the bullet
play the boom sound effect
Refresh your HTML preview to verify that the asteroids are destroyed when they are hit by the laser.
We have an explosion animation that plays when the player
sprite is killed, so let's add a particle effect to show the asteroid breaking up into pieces when it's destroyed.
Phaser's Arcade Physics system includes a Particle Emitter object with properties and methods related to emitting particles. An emitter is just a group of particle objects (similar to how a weapon is just a group of bullet objects).
Particle emitters are useful for one-time events (such as explosions, etc.) or continuous effects (such as rain, snow, flames, etc.).
Adding a particle emitter to the game involves 3 steps:
Declare a global variable for the particle emitter.
Load an image (or spritesheet) for the particles into memory in the preload()
function.
Add the particle emitter to the game in the create()
function.
The particle emitter can then be activated in the create()
function or update()
function, depending on what you need for your game. The particle emitter can be a single point or can be an area of a specified width and height.
Let's get ready to add your particle emitter:
Add a global variable named asteroidParticles
for the particle emitter.
Preload assets/images/asteroid-particle.png for the particle image. Assign a unique asset key name to the image, such as particle
. This particle image is just a scaled down version (20 pixels by 20 pixels) of the first frame from the asteroid spritesheet:
Add this Phaser code in your create()
function (after the for
loop that added the asteroids, but before the code that adds the explosion
sprite):
Let's examine this code:
The first line adds the particle emitter to the game. 0, 0
represent the x and y coordinates where the particles will be emitted from. (Later, we'll change the x and y coordinates to match the x and y coordinates of the asteroid being killed.) 50
represents the maximum number of particles to create in the emitter group. We won't be using all 50 for a single particle explosion — we just want to have enough available in the game's memory, in case we need multiple particle explosions on-screen at the same time.
The second line adds the particle objects to the emitter group using the provided asset key name.
The third line sets the gravity
property of the particles to zero (since we don't want any gravity for our particular game). By default, particles will have a gravity value of 100, unless you change it.
The fourth line will make the particles fade out of view from an alpha of 1
(opaque) to 0
(transparent) over 1000
milliseconds (1 second).
When an asteroid collides with the spaceship or with a laser bullet, we want to create an "explosion" of particles at the asteroid's position.
Add this Phaser code in your collideAsteroid()
function (right before the asteroid.kill()
command):
Add the same code in your shootAsteroid()
function (right before the asteroid.kill()
command).
Let's examine this code:
The first two lines set the x and y position of the asteroidParticles
emitter to be the same as the x and y position of the asteroid involved in the collision.
The third line creates an explosion effect from the particle emitter. 1000
represents the lifespan of the particles in milliseconds (1000 ms = 1 second), and 5
represents the number of particles to emit.
The particles will be emitted in random directions with slightly different speeds, so the explode()
effect looks slightly different each time.
Remember in the create()
function that we set our particles to fade out over 1000 milliseconds. Since we also set our particles to have a lifespan of 1000 milliseconds during the explode()
effect, they will be removed as soon as they fade out.
If we didn't fade out the particles, they would disappear abruptly after 1000 milliseconds, which would look odd. The fading makes the particle explosion seem more natural.
Refresh your HTML preview to verify that a particle explosion occurs when the asteroids are destroyed.
Our game is not going to last very long if there are only 10 asteroids to shoot. As asteroids are destroyed, the game should periodically spawn (generate) new asteroids to replace them, so the game can keep going.
As part of our game's design, we'll keep it so there are never more than 10 asteroids at any one time. (Of course, we didn't have to design our game that way.)
Here are a few different options for when to spawn new asteroids:
Immediately spawn a new asteroid whenever an existing asteroid is destroyed
Use a timer to spawn a new asteroid after an existing asteroid is destroyed (such as: making new asteroid appear 5 seconds after one is destroyed)
Use a random number to decide when to spawn a new asteroid after an existing asteroid has been destroyed (i.e., timing of new asteroid will be random)
Let's go ahead and use a random number for our asteroid spawning. It will add some variation and unpredictability to the asteroid spawning.
Add this Phaser code inside your update()
function (after the code that makes the asteroids wrap around the game world):
There is some missing code that you will need to insert, but let's first explain how the provided code works:
Each time a loop of the update()
function occurs, the game will generate a random number (which will be a decimal value between 0 and 1). If the the random number is less than 0.02
, then it will check for a dead asteroid. This means that each loop of the update()
function has a 2% chance of checking for a dead asteroid. Even though 2% is a very low chance, the game loop runs many times per second, so it doesn't take that much time for a dead asteroid check to occur — however, the amount of time between each dead asteroid check will be random, which is what we wanted.
When the random number is less than 0.02
, the code will check for the first dead (killed) sprite in the asteroidGroup
and assign that dead sprite to a local variable called asteroid
. If there aren't any dead sprites at the moment, then asteroid
will be assigned a value of false
.
The next check is whether asteroid
is true
— meaning was there a dead sprite? If so, then it performs the rest of the code (which will reset the asteroid
sprite, so it is "alive" again).
Notice the tween
command at the end. A tween is a command that allows you to change one or more properties of an object over a specified period of time. Tweens are very useful for adding different types of automated effects in your game. In this case, the tween will make the new asteroid fade into view by changing it from transparent to opaque over 0.5 seconds:
The tween is added to the asteroid
sprite, and it is a to
tween.
Inside the curly braces { }
, it lists all the properties of the sprite that will be tweened. In this example, we're just going to change the alpha
property (which represents the sprite's transparency) from its current value (which we set to 0
in the previous line of code) to a new value of 1
(which is opaque).
500
represents the time period (in milliseconds) for the tween. So it will take 0.5 seconds for the alpha
property to change from its current value to its new value.
The change in the property will follow a pattern called Phaser.Easing.Cubic.Out
(which basically means it will change faster at first but then slow down — making this particular tween seem more "natural"). Phaser has different types of easing patterns that can be used for tweens.
true
tells Phaser to start the tween automatically. (Otherwise, you can use false
to set up a tween without running it, and then use a start()
command to run the tween.)
Now it's time for you to add the missing code inside the curly braces of the if (asteroid)
statement:
Add a Phaser command to reset()
the asteroid
at a random X-position and random Y-position in the game world. (Hint: Look back at Step 6 to see how you reset the explosion sprite, and look at Step 5 to see how you can pick a random X and Y position in the game world)
Add Phaser code to give the asteroid a random speed and direction. (Hint: How did you do this in Step 5?)
Because we are "recycling" a dead asteroid sprite from the asteroidGroup
, the "new" asteroid will still have the same properties as the original sprite — unless we change those properties.
So we did give the asteroid a new random starting position and a new random speed and direction.
However, notice that we did not have to add or change several other existing properties for the asteroid:
We didn't need to set the anchor position for the asteroid. It will still use its same anchor position that was set when it was first added to the asteroidGroup
in the create()
function.
We also didn't need to change the asteroid collision area to be a circle (because it was already set to a circle when it was first created).
We also didn't have to add animations to the asteroid because it still has them from before. The sprite will also continue to play the same animation (spin-clock
or spin-counter
) that it was already playing before.
Refresh your HTML preview to verify that "new" asteroids will randomly spawn and fade in after other asteroids are destroyed.
Now your game will keep recycling the same 10 sprites in the asteroidGroup
, so the gameplay can continue for as long as the player can stay alive.
Now that our game will keep spawning new asteroids as the player shoots them, let's have the game become more challenging as the player progresses.
Every time the player shoots an asteroid, let's increase the asteroid maxSpeed
by a small amount. This won't change the speed of any existing asteroids. However, it will affect the possible speed of new asteroids.
A higher maxSpeed
means each new asteroid has the potential to be slightly faster than the previous asteroids. Since the speed of each new asteroid is calculated as a random value (up to the maxSpeed
), some of the new asteroids will still be slow — but over time (as the player keeps shooting asteroids), some of the new asteroids will be much faster — increasing the challenge of the game in response to the player's skill. (Our game's attempt at promoting flow.)
In your shootAsteroid()
function, add a line of code to increase the value of maxSpeed
by 1
:
Refresh your HTML preview to verify that some of the new asteroids seem to be faster as the game progresses.
The change in the maximum possible speed is small, so it would require destroying at least 50 or more asteroids for it to start to become obvious that some of the new asteroids are faster — but don't worry, the asteroids will definitely get faster as the game progresses.
If you want to really speed things up, you could temporarily change the line of code to increase the maxSpeed
by 10 — and then test it out to verify that it does affect the speed of new asteroids.
Let's give the player some score points for every asteroid destroyed by a laser.
So we'll need a variable to keep track off the player's score, and we'll need to display the score as text in the game display.
As a reminder, adding text to the game involves 2 steps:
Declare a global variable for the text.
Add the text to the game in the create()
function.
Once the text has been added, you can use the variable's text
property to change what the text displays. This is typically done in your update()
function or in a custom function.
Here's what you need to do:
Declare a global variable called score
and assign it an initial value of 0
Declare a global variable called scoreText
In your create()
function (after the code that adds the keyboard inputs), add scoreText
as a text object at position 20, 20
. Set the text to display: 'Score: ' + score
(which will display the text in the quotes followed by the current value of the score
variable). Use the same font styling that you used in Practice 1 (Arial, 20px, bold, #ffffff)
You do not need to set the text's anchor or add a text shadow.
If you need hints, look back at Step 7 of Practice 1.
Refresh your HTML preview to verify that the score (with a value of zero) appears in the upper left of the game.
Now we need to increase the score every time the player shoots an asteroid.
Add Phaser commands in your shootAsteroid
function to do the following (in order):
Increase the value of score
by 250
Change the text
property of scoreText
by assigning it a value of: 'Score: ' + score
If you need hints, look back at Step 9 of Practice 1.
Refresh your HTML preview to verify that the score increases by 250 every time you shoot an asteroid.
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
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.
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 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):
RESOURCE: W3Schools has a JavaScript tutorial on For Loops
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.
RESOURCE: W3Schools has a JavaScript tutorial on Variable Scope (Global vs. Local)
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 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.
UPDATE IN PROGRESS: This project guidebook will be replaced with a revised version during summer 2019.
People have been creating and playing games on computers, ever since the earliest computers were built in the 1940s. However, early computers did not have video monitors, so the earliest computer games weren't technically video games.
In 1958, one of the first video games — called "Tennis for Two" — was created using an oscilloscope (a device normally used to view electrical signals). Special paddle inputs were built to play the game.
VIDEO: Tennis for Two - one of the original video games
Up until the 1970s, computers were expensive, large-scale machines used primarily by governments, universities, and corporations — so very few people actually played any early computer games.
Then in the 1970s, the development of microprocessor chips allowed computers to become much smaller, less expensive, and easier to manufacture. As a result, coin-operated video arcade games (such as Pong, etc.) were introduced into amusement arcades, restaurants, and other public places — quickly becoming a popular form of entertainment. Soon after, the first personal computers (such as the Apple II, etc.) and gaming consoles (such as the Atari 2600, etc.) became available to consumers, finally bringing video games into people's homes — and starting an obsession that continues today.
Space Invaders was introduced as a video arcade game in 1978, quickly becoming extremely popular. In 1980, the first licensed home version (shown above) was made for the Atari 2600 gaming console, which quadrupled sales of the Atari — and made Space Invaders the first "killer app" for gaming consoles. Official versions and unofficial clones of the game were created for nearly every computer and gaming console in the early 1980s.
According to the 2017 video game industry report by the Entertainment Software Association, 65% of U.S. households have at least one person that plays video games regularly, and almost half of households have a dedicated gaming console. U.S. video game players are more diverse in terms of age and gender than you might think. The average video game player is 35 years old, and over 40% of players are female.
In 2016, U.S. consumers spent over $24 billion just on video game content. In fact, over 65,000 U.S. employees work for video game companies, earning an average of $97,000 per year.
Clearly, many people love to play video games. What is it about these games that people love so much? What makes certain games appealing to us but not others?
Could you create a video game that people would want to play?
Copyright © 2016-2019 Michael Frontz and Jim Lyst, Indiana University School of Informatics and Computing at IUPUI
This material is part of the Computing by Design high school computer science curriculum developed for the Informatics Diversity-Enhanced Workforce (iDEW) program, an award-winning community partnership in central Indiana that is broadening student participation in computing and helping students develop the skills essential for success in the 21st century workplace. The iDEW program is managed by the Indiana University School of Informatics and Computing at IUPUI.
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. You are free to use, share, or adapt this material for noncommercial purposes as long as you provide proper attribution and distribute any copies or adaptations under this same license.
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.
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 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.
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 12 gamer motivations (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.
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.
VIDEO: This video talks about flow in video games and how game designers can encourage flow.
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.
Follow the instructions and links in this assignment to playtest two games to compare their use of flow conditions.
Discuss your findings as a class.
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 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.
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.
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.
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:
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 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.
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 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 :
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 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.
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
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.
Be sure that you completed Coding Steps 1-5 on the previous page.
IMPORTANT: Take the time to read each step, and try to understand how the code works.
This game will feature a group of cats as an "enemy". Cats are normally great companions, but these cats don't want to be your friend. If the player touches a cat, the player's health will be reduced.
Declare a global variable named:
catGroup
(will contain all the cat objects)
In your preload()
function, load the spritesheet for the cats:
Load assets/images/cat.png as a spritesheet. Use 'cat'
as the asset key. The image contains 4 animation frames in a single row. The entire image is 128 pixels in width and 32 pixels in height. Use this information to determine the width and height of each frame.
In your create()
function, add code to do the following (after the code that adds the coins, but before the code that adds the player sprite):
Add catGroup
to the game
Enable physics for all members of catGroup
Rather than placing the cats at specific locations, we're just going to space the cats evenly across the top of the game world, give them a random velocity, and let them drop down onto the platforms.
An easy way to do this is to just use a for
loop (similar to how you added the coins but without using JSON data for the positions).
Add this code to your create()
function (after the command that enabled physics for catGroup
):
This for
loop will create 25 cats in the catGroup
. There is some missing code that you'll add in a bit.
The x-position for each cat will be calculated as: i * 200 + 100
. This will place the cats 200 pixels apart, with the first cat positioned at 100:
The x-position for the first cat (i = 0
) will be 100 because 0 * 200 + 100 = 100
The x-position for the next cat (i = 1
) will be 300 because 1 * 200 + 100 = 300
The x-position for the next cat (i = 2
) will be 500 because 2 * 200 + 100 = 500
The x-position for the next cat (i = 3
) will be 700 because 3 * 200 + 100 = 700
and so on...
The y-position for each cat will be 0
(the top of the game display).
Refresh your HTML preview to verify that the 25 cats appear evenly spaced apart along the top of the game.
Now you'll add the missing code inside the for
loop to give each cat some physics properties and an animation.
Inside your for
loop (after the command that creates the cat in the catGroup
), add code to do the following for each cat
:
Set the anchor for cat
to be its center (0.5, 0.5
)
Set its body.gravity.y
to 300
Set its body.bounce.x
to 1
Set its body.collideWorldBounds
to true
Add an animation named 'left'
that will play frames 0-1 (in order) at 10 frames per second in a loop
Add an animation named 'right'
that will play frames 2-3 (in order) at 10 frames per second in a loop
Add this code to give each cat a random velocity (and a 50% chance of having its direction reversed):
Next, you need to make sure the cats collide with the platforms and walls, similar to what you did for the player and coins.
In your update()
function (after the collide()
statements for the player
), add code to do the following:
Make catGroup
and platformGroup
collide with each other
Make catGroup
and wallGroup
collide with each other
You'll also add some code to make sure each cat plays the correct animation based on its direction of movement (which will change as the cats bounce off the walls and game boundaries).
Add this code in your update()
function (after the code that added the background parallax):
This function will check the velocity of each cat in the catGroup
. If the cat's velocity is less than zero, that means the cat is moving to the left. Otherwise, the cat is moving to the right.
Refresh your HTML preview to verify that the cats fall down. The cats should move at different speeds and should collide with the platforms and walls. If a cat collides with a wall or a game boundary, the cat should reverse direction. The cats should play the correct animation as they move and change direction.
For this game, we're going to add a simple AI (artificial intelligence) for the cats: if a cat is on a platform, then it will patrol back and forth on the platform (instead of running off the edge).
We'll add this behavior using a custom function that will run whenever a cat is on top of a platform (i.e., colliding with the platform).
Modify your existing collide()
command for the catGroup
and platformGroup
:
When a collision occurs, it should call a custom function named patrolPlatform
.
Add this code after your update()
function to create the custom function :
The patrolPlatform()
function checks for two possible conditions:
The enemy
is moving to the right (velocity.x > 0
) AND the right edge of the enemy
sprite is hanging over the right edge of the platform
sprite
OR
The enemy
is moving to the left (velocity.x < 0
) AND the left edge of the enemy
sprite is hanging over the left edge of the platform
sprite
If either of those conditions is true, then the function simply reverses the direction of the enemy
by multiplying its velocity.x
by -1
. For example, if the velocity.x
was -125 (moving to the left), it would become 125 (moving to the right).
Refresh your HTML preview to verify that any cats landing on a platform will patrol back and forth on the platform. Even cats on the ground (which is also a platform) should patrol back and forth.
If the player's character touches a cat, the player will have its health reduced slightly. So let's first add a health bar for the player.
Declare a global variable named:
healthBar
(green bar that will be scaled to represent health value)
In your preload()
function, load these images for the health bar:
Load assets/images/bar-red.png with 'red-bar'
as its asset key
Load assets/images/bar-green.png with 'green-bar'
as its asset key
Load assets/images/bar-outline.png with 'bar-outline'
as its asset key
In your create()
function (after the code that adds the player
sprite), add commands to do the following:
Set the health
property for the player
to 100
Set the maxHealth
property for the player
to 100
In your create()
function (after the code that adds scoreText
), add code to do the following:
Declare a local variable named healthText
that adds text to the game with these properties:
To declare a local variable, just list var
in front of the variable name
The text should be positioned at: 325, 20
The text should display: 'Health'
The text should use this style: { fontSize: '20px', fill: '#222222' }
Make healthText
stay fixed to the camera (so it doesn't move when the game world scrolls)
Declare local variables named barBackground
and barOutline
, like this:
var barBackground, barOutline;
Add an image assigned to barBackground
at position 400, 20
using the 'red-bar'
asset
Make barBackground
stay fixed to the camera
Add an image assigned to healthBar
at position 400, 20
using the 'green-bar'
asset
Make healthBar
stay fixed to the camera
Add an image assigned to barOutline
at position 400, 20
using the 'bar-outline'
asset
Make barOutline
stay fixed to the camera
Refresh your HTML preview to verify that the health bar appears at the top center of the game. The bar should be green with a dark gray outline. (Remember that the red bar background is currently hidden because it is covered by the green bar.)
Let's add a sound for the cats that will play whenever the player touches a cat.
Declare a global variable named:
catSound
In your preload()
function, load the audio file for the sound:
Load assets/sounds/meow.wav with 'cat-sound'
as its asset key
In your create()
function, add the sound to the game:
Add catSound
to the game as audio using the 'cat-sound'
asset with a volume of 0.2
Now let's detect when the player touches a cat. Besides reducing the player's health, we'll play the cat sound and also make the cat jump away from the player.
Rather than using a collide()
method to detect when the player touches a cat, we'll use an overlap()
method. They both detect when objects touch. However, collide()
also allows moving objects to transfer momentum, so if we used a collide()
method, then the cats would speed up every time they touch the player.
In your update()
function (after the collide()
method between the catGroup
and platformGroup
), add the following:
Add a overlap()
method between player
and catGroup
(list in this order) that will call a custom function named touchCat
After your update()
function, add the following:
Add a new custom function named touchCat()
. Inside the parentheses ( )
, list player, cat
as parameters.
Inside the curly braces { }
of your touchCat()
function, add code to do the following:
Reverse the direction of the cat
by multiplying its body.velocity.x
by -1
Make the cat
jump by setting its body.velocity.y
to -150
Move the cat away from the player by changing its x-position with this code:
Play the catSound
Reduce the player
health by 5
using the damage()
method
Scale the width of the healthBar
to be: player.health / player.maxHealth
(keep the scale for the height as 1
).
If you need help with the code for scaling the health bar, look back at Step 11 of Practice 2.
Refresh your HTML preview to verify that the player's health bar is reduced when the player touches one of the cats. The cat should reverse direction and jump away from the player. You should hear the cat sound play when this happens.
If the player runs out of health, Phaser will automatically kill()
the sprite. When that happens, let's reset the player's character back at the beginning of the level.
In your create()
function (after the code that adds the player
), add code to do the following:
Add an onKilled
event for the player
that will run a function.
If you need help with the code for adding an onKilled
event, look back at Step 6 of Practice 2.
Inside the curly braces { }
of the onKilled
event function, add commands to do the following:
Reset the player
back to position 25, 300
with a health value of 100
using this command:
Scale the width of the healthBar
Refresh your HTML preview to verify that when the character's health runs out, the player is reset back to the start of the level with full health.
Next we're going to add some "power-up" objects to the game that will temporarily boost the player's running and jumping speeds. We'll use a star as the power-up object. We'll set a timer to control how long the power-up works.
Declare a global variable named:
powerUpGroup
(will contain all the power-up objects)
powerUpActive
and assign it an initial value of false
In your preload()
function, load this image for the power-up:
Load assets/images/star.png with 'star'
as its asset key
In your create()
function (after the code that adds the coinGroup
), add commands to do the following:
Add powerUpGroup
to the game as a group
Enable physics for the members of powerUpGroup
Create a member of powerUpGroup
at position 1000, 200
using the 'star'
asset
Create a member of powerUpGroup
at position 3000, 400
using the 'star'
asset
Set all the members of powerUpGroup
to have their 'anchor.set'
property set to a value of 0.5
Refresh your HTML preview to verify that the star objects appear in the game at correct positions. They should be floating in mid-air (since you didn't add any gravity to them).
When the player collects a star, we'll display a message on the screen while the power boost is active (and then hide the message after the power-up timer has run out).
So let's add a blank text object to the game that we can use later to display a message to the player. We'll also use this same text object for a different message in Step 8.
Declare a global variable named:
messageText
(will be used to display text)
In your create()
function, add code to the following:
Add messageText
to the game as text with these properties:
Position the text at: 500, 100
Have the text display nothing for now (use empty quotes): ''
Style the text using: { fontSize: '48px' }
Set the text anchor position to be its center: (0.5)
Set a shadow for the text using these properties: (2, 2, '#000000', 2)
Make the text stay fixed to the camera
Hide the text by setting its visible
property to false
Now let's detect when the player touches a star. This is when we'll start the power boost. We'll display a message on the screen, change the player's character to a green color, and start a 10-second timer. While the power-up is active, we'll increase the player's running and jumping speeds. When the timer runs out, we'll change everything back to normal.
In your update()
function (after the collide()
method between the player
and coinGroup
), add the following:
Add a collide()
method between player
and powerUpGroup
(list in this order) that will call a custom function named collectPowerUp
After your update()
function, add the following:
Add a new custom function named collectPowerUp()
. Inside the parentheses ( )
, list player, powerUp
as parameters.
Inside the curly braces { }
of your collectPowerUp()
function, add code to do the following:
Remove the powerUp
object (the star) using the kill()
method
Change the value of powerUpActive
to true
Set the text
property of messageText
to display 'Power Boost'
Set the fill
color for messageText
to be green using: '#00ff00'
Make messageText
visible
Change the player
sprite to a green color by setting its tint
property to 0x00ff00
Use this command to start a timer event that will run a custom function named stopPowerUp
after 10 seconds have elapsed:
Before we forget, let's add this custom function that will stop the power-up.
Add another new custom function named stopPowerUp()
. Inside the curly braces { }
of this function, add code to do the following:
Change powerUpActive
to false
Hide messageText
by changing its visible
property
Remove the green color from the player
by setting its tint
to 0xffffff
(represents no tint)
We haven't added the code to change the player's speed yet, but let's test out what we've added so far.
Refresh your HTML preview to verify that the "Power Boost" message appears when the player collects a power-up star. The player should turn a green tint. The player's speed will still be normal (because we haven't added that code yet). After 10 seconds, the message should disappear and the player should turn back to normal.
Now we're ready to add the code to increase the player's running and jumping speeds while the power-up is active.
We'll use the value of powerUpActive
to detect when the power-up is supposed to be active. If powerUpActive
is true
, we'll use a larger value for the velocity that makes the player run or jump. Let's increase the velocity by 50% (e.g., if the normal running speed is 200
, we'll boost to 300
).
So we'll modify the existing code in the update()
function that detect the player's input. Inside those if-else statements , we'll add local variables to represent the normal values for runSpeed
and jumpSpeed
. If powerUpActive
is true
, we'll increase the values of these variables.
Modify your existing if-else statements in the update()
function that check the player input, so the code looks like this:
Refresh your HTML preview to verify that the player's running and jumping speeds are boosted while the power-up is active.
Next let's add to the challenge of the game by adding a countdown timer for the player to complete the level. Instead of displaying the time as text, we'll add a timer bar that decreases to show the amount of time remaining. Let's give the player 2 minutes (120 seconds) to complete the level.
Declare a global variable named:
timeBar
(yellow bar that will be scaled to represent amount of time remnaining)
timeUp
and assign it an initial value of false
timeLimit
and assign it an initial value of 120
(which represents the number of seconds)
In your preload()
function, load these images for the health bar:
Load assets/images/bar-black.png with 'black-bar'
as its asset key
Load assets/images/bar-yellow.png with 'yellow-bar'
as its asset key
In your create()
function (after the code that adds barBackground
, healthBar
, and barOutline
), add code to do the following:
Declare a local variable named timeText
that adds text to the game with these properties:
To declare a local variable, just list var
in front of the variable name
The text should be positioned at: 720, 20
The text should display: 'Time'
The text should use this style: { fontSize: '20px', fill: '#222222' }
Make timeText
stay fixed to the camera
Add an image assigned to barBackground
at position 780, 20
using the 'black-bar'
asset
Make 'barBackground
stay fixed to the camera
Add an image assigned to timeBar
at position 780, 20
using the 'yellow-bar'
asset
Make 'timeBar
stay fixed to the camera
Add an image assigned to barOutline
at position 780, 20
using the 'bar-outline'
asset
Make 'barOutline
stay fixed to the camera
Refresh your HTML preview to verify that the time bar appears at the top center of the game. The bar should be yellow with a dark gray outline. (The black bar background is currently hidden because it is covered by the yellow bar.)
Now let's add a custom function to determine how much time has elapsed in the game and use that to update the time bar to show how much time is remaining.
Luckily, Phaser has a built-in method called game.time.totalElapsedSeconds()
to calculate how much time (in seconds) has elapsed since the game started.
So we can just subtract the game's elapsed time from the timeLimit
to determine how much time is left over. We can then use this result to scale the timeBar
.
In your update()
function, add this as the first line of code inside the curly braces (before any of the collide()
methods):
This will run a custom function named displayTimeLeft()
as the first step every time the update()
loop occurs.
After your update()
function, add this code to create the custom function:
Notice that we had to detect if the value of timeLeft
was less than zero. If so, we set to be exactly zero. Otherwise, if we didn't do that, the timeBar
would scale to show negative timeLeft
(the yellow bar would actually start expanding outside of its outline). We also use this to detect when to set the value of timeUp
to true
.
Also notice that you scale the timeBar
similar to how you scale the healthBar
. For the timeBar
, you divide the timeLeft
by the timeLimit
to get the proportion of time remaining. For example, if only 30 seconds remain out of a 120-second time limit, then the timeBar
should be scaled to 0.25 of its original width (30 / 120 = 0.25).
Refresh your HTML preview to verify that the yellow time bar decreases in width to show the time remaining in the game until it reaches zero (time bar will be completely black).
Now let's add a "game over" function that will run when the time limit for the game is over. We can use the value of timeUp
to detect when this has occurred.
When timeUp
is true
, we want to display a game over message to the player, and make the player
sprite disappear (so the game is effectively over).
Modify the first line of code inside your update()
function, so it looks like this instead:
Now if timeUp
is true
, it will run the custom function named gameOver()
. Otherwise (when timeUp
is false
), it will run the custom function displayTimeLeft()
.
Next you need to add the gameOver()
function.
After your update()
function, add a new custom function named gameOver()
. Inside the curly braces { }
of this function, add code to do the following:
Set the text
property of messageText
to display 'Time Up'
Set the fill
color for messageText
to be red using: '#ff0000'
Make messageText
visible
Make the player
sprite disappear by setting its exists
property to false
Notice that instead of using the kill()
method to remove the player
sprite, we simply set its exists
property to false
. The reason for this is because we previously added an onKilled
event for the player
(when its health runs out) that resets the sprite back to the beginning of the game with full health. However, when the time limit runs out, we want the game to be completely over, so that's why we used the exists
property instead of the kill()
method. Both of them remove the player, but we want to avoid triggering the onKilled
event in this situation.
Refresh your HTML preview to verify that "Time Up" message appears and the player disappears when time runs out.
What you’ve made so far is a partial game. Now it's up to you to design and build the rest of the level.
In this step, you’ll create of a map of your design for the complete level. In Steps 10-15, you’ll implement your design and playtest it.
A recommended scale for the graph is to have each square represent either 20 or 25 pixels (choose one). Use the same scale for both the horizontal and vertical axes of the graph.
You will probably need to split up your level map into multiple sections (either on the same graph sheet or across multiple sheets).
Mark and label your horizontal and vertical scales for your game world on the graph. Unless you've changed it, your game world is 5000 pixels in width and 600 pixels in height.
The x-positions start with 0 as the left edge of the game world, and they increase as you move towards the right edge.
The y-positions start with 0 as the top edge of the game world, and they increase as you move towards the bottom edge.
OPTIONAL: If desired, you can reduce the width of the game world from 5000 pixels to a smaller size (but make it at least 3000 pixels wide). The existing platforms are placed within the first 1500 pixels of the game world.
Include the size and locations of the existing platforms, walls, and other objects (coins, etc.) in your level map. (If desired, you can change the size and/or locations of these existing objects.)
For the platforms and walls, the position at which they were created represents their top-left corner (since we didn't change the default anchor position for these objects).
As a reminder, all the platforms are 25 pixels in height. The width of the platforms range from 50 pixels to 500 pixels (depending on which platform asset is used).
As a reminder, all the walls are 25 pixels in width. The height of the walls range from 50 pixels to 250 pixels (depending on which wall asset is used).
You do not necessarily need to include the locations of moving objects (cats, etc.) unless it is helpful to you. For example, if you want the player to encounter a "boss" enemy at a certain location, it may help to include that on the map.
Be sure to include the size and locations of the new objects that you plan to add in Steps 10-12.
Partial scale map of level (first 2500 pixels of width) showing starting position of player, size and location of existing platforms and walls, locations of coins (after falling), and location of power-up (star). Next step would be to add new objects, and create rest of map.
RECOMMENDED: Preview the instructions in Steps 10-15 to see more details about what you’ll need to do to complete this practice. Here’s a brief summary:
In Step 10, you’ll add more platforms (and walls) throughout the rest of the level. You need to add at least 10 more platforms. You can modify the size and/or location of any existing platforms or walls (but you don’t have to).
In Step 11, you’ll add a new obstacle that hinders the player in some way, such as a spike, new enemy, etc. You can add an individual obstacle or a group of obstacles.
In Step 12, you’ll add a new resource that helps the player in some way, such as a diamond, health pack, weapon, etc. You can add an individual resource or a group of resources. You'll also add more coins for the player to collect.
In Step 15, you’ll have someone else playtest your game to provide feedback.
Add additional platforms (and walls) throughout the rest of the level:
Add at least 10 more platforms to the game. You decide the size and location of the additional platforms (and any additional walls).
OPTIONAL: You can change the size and/or location of any existing platforms or walls.
OPTIONAL: If desired, you can reduce the width of the game world from 5000 pixels to a smaller size (at least 3000 pixels). Refer back to Step 3 to see how to make this change in your code.
OPTIONAL: If desired, you can also reduce the width of the game display from 1000 pixels back to 800 pixels. Refer back to Step 3 for help with the code. However, if you do this, be sure to also:
Change the width of #my-game
in your CSS file to 800px
Adjust the locations of the health bar and time bar to fit within the narrower game display.
IMPORTANT: After adding the rest of the platforms (and walls), playtest your game to ensure the player
can navigate through the platforms and walls to complete the level. If it's not possible to navigate the level, you'll need to do one or both of the following (and then retest your game):
Modify the size and/or location of specific platforms (or walls).
AND / OR
Modify certain properties of the player
, such as its running speed, jumping speed, gravity, etc.
Everyone should keep in mind the .
(helpful if drawing tile-based game world)
Use to create a scale map of the complete game level showing the size and location of all the objects (including new objects you’ll add in Steps 10-12), such as platforms, walls, coins, etc.
In Step 13, you’ll add more sound effects to the game. You’ll use existing sounds from Practice 1 to play a sound effect when the player collects a coin and when the player collects a power-up. Optionally, you create your own sound effects using a web app called .
In Step 14, you’ll have the option to create an animated sprite using a web app called .
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 online evaluation survey 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 “Think Aloud” 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.
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 online evaluation survey 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 “Think Aloud” 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.
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.
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.
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.
The references in this section can help with:
introducing you to Phaser (what is it, where to download, etc.)
creating a Phaser game template (HTML, CSS, and JS files with starter code)
finding and creating visual assets (animated sprites, etc.)
finding and creating audio assets (sound effects, etc.)
coding common game features using Phaser (adding objects, detecting collisions, etc.)
In addition, there are many other Phaser references, examples, and tutorials available online. If you're not sure where to look, try this.
You can create a group of sprites. You can have different groups for different types of sprites. The members of a particular group should be related in some way, such as a group containing enemies, a group containing platforms, etc. The members of a group typically have many similarities — but they can also have differences.
The main advantage of using a group is that is makes it easier to perform checks or actions on each member of the group.
Another advantage is that your code can recycle "dead" members of a group (which helps save memory and make your game run faster).
Add New Group
Create New Member in Group
Set Property for Specific Member of Group
Set Property for All Members of Group
Create Multiple Members Using For Loop
Position Members at Regular Intervals
Position Members at Random Locations
Position Members at Specific Locations
Call Function for Each Member of Group
Call Function for Each Alive (or Dead) Member of Group
Count Members of Group
Get Specific Member of Group
Recycle Dead Members of Group
Freeze or Hide Group (and Undo)
Kill All Members of Group
Created Nested Group Containing Other Groups
Sort Members of Group for Pseudo Depth Effect
You can add a new group to your game by assigning it to a variable. Use a global variable if you will need to refer to the group or its members in your update()
function or in a custom function.
For example, to add a group to a global variable named platformGroup
:
This command would be placed in your create()
function.
Once a group has been added, you can then create a new member in the group, and add the member to the game world at a specific position.
Adding new members to a group is typically done in the game's create()
function. However, it can also be done in the update()
function if you need to add new members as the game proceeds.
For example, to create a new member in a group named platformGroup
:
200, 500
represent the x
and y
coordinates where the member will be positioned in the game world (based on the member's anchor, which is its top-left corner by default). Change these values to the position you need.
'platform-100'
is the asset key of the image or spritesheet to use for this member. Change this to the asset you want to use.
If you need to create more members in the group, just repeat the command, and change the x, y
position and/or asset key for each new member:
The members of a group are sprites, so each member of a group can have its sprite properties set, can have physics properties enabled and set, and can have animations added.
If you need to change the value of a property for a specific member of the group (but not the other members), first create the new member by assigning it to a local variable. Then change the property for that specific member by referring to its variable name.
bottomPlatform
represents a local variable name for the new member. Change this to a variable name that makes sense to you.
The second line of code in the example changes a property (the scale
property) only for this specific member of the group. Change this to list the property and value that you want to set. If necessary, you can modify multiple properties for this specific member of the group.
You can use the setAll()
method to change the value of a property for all members of 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.
For example, this command will set a property for all members of a group named platformGroup
:
true
represents the value for the property. Some properties use true
or false
for their value. Some properties use a number for their value. Some properties use a string (text) for their value (if so, list the string within quotes). List the value you want to use for your property.
You can use a for
loop to create multiple new members in a group. If you do this, you have several options for positioning the new members:
position the members at regular intervals (such as: every 200 pixels, etc.)
position the members at random locations
position the members at specific locations using JSON data array
In order to position the new members at regular intervals in the game world, you need to create a calculation that will space the members horizontally (in the x
direction) and/or vertically (in the y
direction). Typically, these calculations use the i
variable that the for
loop uses to count iterations.
For example, this code would create 10 new members in the coinGroup
located at regular intervals in the game world:
The for
loop shown above will create 10 new members in the group.
var i = 0
creates a local variable named i
that starts at zero and will be used as the "counter" for the number of times to iterate (repeat) the loop.
i < 10
sets a condition to decide when the loop should stop iterating (repeating). In this case, the number 10
controls the number of members that will be created. Change this to the number that you need to create.
i++
changes the local variable at the end of each iteration by increasing its value by one (i++
is shorthand for i = i + 1
).
The x
position of each new member will be calculated using i * 200 + 100
.
200
represents how far apart (in pixels) the members will be positioned.
100
represents the position of the first member.
As the value of
The first new member (when i = 0
) will have an x
position of 100 (0 200 + 100 = 100). The next member (when i = 1
) will have an x
position of 300 (1 200 + 100 = 300). The next member (when i = 2
) will have an x
position of 500 (2 * 200 + 100 = 500) - and so on.
The y
position of each new member will simply be set as 0
(top of game world). Presumably, each coin
will have a gravity value set that will make it fall down.
Modify the code to use your own calculations (and/or numbers) for the x
and y
positions.
You can also just position each new member at a random location.
For example, this code would create 10 new members in the coinGroup
at random locations in the game world:
You can also create a JSON data array that contains specific x
and y
positions for the new members to be created. Then you would use a for
loop to iterate through the JSON data array to create the new members, and add them into the game world at their specific positions.
For example, here is a JSON data array that list positions that will be used to create new members in a group:
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. If you were using variables that have strings (text) for their values, then list the values within quotes.
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.
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.
Each item in an array is identified using an index number, which is based on the order in which the items are listed. The first item in an array is always numbered as index 0
. Since there are 10 items listed in the coinData
array, they are numbered in order as 0-9. 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 array to use the JSON data to create the new members in coinGroup
.
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]
. You can add optional code to set other properties for each member (e.g., set its anchor, add animations, etc.).
You can use the forEach()
method to call a function for each member of a group. Inside the function, you can list whatever commands you need to perform for each member. The forEach()
method will iterate through each member of the group, performing the commands you listed.
For example, this would call an inline function for each member of a group named enemyGroup
:
enemy
represents a local variable name that will be used within the function to refer to a member of the group. Change this to a variable name that makes sense for the group.
Within the curly braces { }
, list one or more commands to be performed for the member. In this example, the code sets the anchor for the member, adds an animation to the member, and starts playing the member's animation. Change these to whatever commands you need.
The forEach()
method can be used in your create()
method to set properties of each member before the game starts.
You can also use the forEach()
method in your update()
function (or in a custom function) to perform commands on each member during gameplay.
You can use the forEachAlive()
or forEachDead()
methods to call a function only for the "alive" (or "dead") members of the group. Inside the function, you can list whatever commands you need to perform for each alive (or dead) member.
For example, this would call an inline function only for each "alive" member of a group named enemyGroup
:
For example, this would call an inline function only for each "dead" member of a group named enemyGroup
:
These methods would typically be used during gameplay in your update()
function (or in a custom function).
If your game needs to know how many members are in a specific group, there are several options available to get a count.
For example, if the name of your group were enemyGroup
:
enemyGroup.total
would be the total number of members in the group, including both "alive" and "dead" members
enemyGroup.countLiving()
will return the number of "alive" members in the group
enemyGroup.countDead()
will return the number of "dead" members in the group
If your game needs to get a specific member of a group (in order to do something with that member), there are several methods available to select a member.
For example, if the name of your group were enemyGroup
:
enemyGroup.getFirstAlive()
will return the first "alive" member of the group that it finds.
enemyGroup.getFirstDead()
will return the first "dead" member of the group that it finds.
enemyGroup.getRandomExists()
will return a random existing member of the group.
enemyGroup.getClosestTo(player)
will return the member of the group that is closest in distance to the player
sprite. Change player
to whichever sprite or display object that you want.
enemyGroup.getFurthestFrom(player)
return the member of the group that is farthest in distance from the player
sprite. Change player
to whichever sprite or display object that you want.
In order to use of the methods above, you will want to create a local variable to store the result (the member) returned by the function. Then you will be able to add commands to do something with that specific member.
For example, the following code will get a random member of the enemyGroup
, assign the member to a local variable named enemy
, and the make that enemy
move towards the player
:
You can recycle "dead" members of a group by adding them back to the game (making them "alive" again). By recycling dead members (instead of constantly creating new members), your game will use less memory and run faster.
For example, your game might be designed to add back members of an enemy group that have been removed using the kill()
method. Since the members are sprites, you can add a "dead" member back into the game using the revive()
or reset()
method.
To do this, you would need to find the first dead member of the group, and assign this member to a local variable. Then you can revive or reset the member and change any of its properties (position, velocity, etc.).
For example, add this code to your update()
function to immediately recycle a dead member of a group named enemyGroup
:
This code tries to find the first dead member of the group, and assigns this member to a local variable called enemy
(change to a name that makes sense for your group).
If a dead member was found, then it will run the code inside the if
statement — this is where you would either revive or reset the member to make it alive again. You can add other code to change the member's properties or perform other actions.
If there weren't any dead members (i.e., every member of the group is already alive), then the local variable will be assigned a value of null
and the code inside the if
statement will not be performed.
If you want to add a brief random interval before recycling a dead member of the group, use this slightly modified code:
This modified code will only check for a dead member to recycle if it generates a random number below a certain value (0.01
in this case). Increasing this value will make it more likely that recycling will occur during any specific game loop — the intervals between recycling will still be random, but recycling will occur more frequently on average. (Decreasing the value has the opposite effect.)
If you game needs to freeze or hide all members of a group at once, you can use change the exists
property or the visible
property.
NOTE: The way that the exists
property affects a group is different from how it works for an individual sprite.
You can change a group's exists
property prevent its member sprites from being affected by any physics interactions. However, the member sprites will still be visible in the game. Basically, it will cause the member sprites to become "frozen" at their current position and become non-interactive.
For example, to "freeze" a group named enemyGroup
:
To unfreeze the group (and make it interactive again):
You can change a group's visible
property to make all of its member sprites invisible. However, the member sprites will still be involved in certain physics interactions (moving, falling, colliding, etc.) because the sprites still exist (they're just invisible).
For certain games, invisible sprites could be interesting (e.g., creating invisible enemies that the player must avoid, etc.).
For example, to make a group named enemyGroup
invisible:
To show the group again:
You can also change both properties at the same time.
For example, to freeze and hide a group named enemyGroup
:
To unfreeze and show the group again:
If your game needs to kill all members of a group at once, there is a method specifically to do this:
If necessary, you can create a group that contains other groups (as well as individual sprites) nested within it. There may be certain situations where using a nested group is helpful.
You can simply use the add()
method to add another group (or a sprite) into a group.
For example:
Some 2D games attempt to create a "pseudo-3D" visual effect by using side-view sprites combined with top-down movement (meaning sprites can move up, down, left, and right — and have no gravity applied) plus depth sorting (based on y
position).
Sprites that are towards the top of the game display (lower y
values) are supposed to be farther back while sprites that are towards the bottom (higher y
values) are supposed to be closer. The game sorts the visual layering of the sprites based on their y
positions to make "closer" sprites appear in front of "farther" sprites.
To be clear, using this "depth" effect will not make your game look like a true 3D game. However, the sprites will have a psuedo-depth effect relative to each other, which can add a slight bit of a "third dimension" to your game.
For example, to do a depth sort for the members of a group named 'treeGroup`:
If the members of the group do not move, then you could just perform the sort one time by placing it in the create()
function.
However, if you the members of the group are moving and changing their y
positions, then you would want to perform the sort every game loop by placing it at the end of your update()
function. For example, you might need to do this for a group of enemies.
If you want apply a depth sort to the player's sprite plus other sprites and groups, then you should create a nested group that contains all the sprites and groups that should be sorted by depth.
For example, add a new group named depthGroup
in your create()
function, and then add the other sprites and groups into the depthGroup
:
Then at the end of your update()
function (after any sprites or group members may have changed position during a game loop), do a depth sort on the depthGroup
:
You can use any keys on the keyboard as inputs for player actions. You can also use mouse or touch input for your game. Phaser even has support for gamepads.
Phaser treats touch input as a pointer — i.e., similar to a mouse (the position of the touch is treated like the position of a mouse cursor, a tap is treated like a mouse click, a double-tap is treated like a double-click, etc.).
Phaser does not have built-in properties or methods to directly detect touch gestures such as: swipe left, swipe right, pinch to zoom, rotate, etc. However, Phaser allows you to have up to 10 "pointer" inputs (i.e., for up to ten fingers), and Phaser Pointer objects do have certain properties and methods that can be used to indirectly detect certain touch gestures (such as: swipe direction, etc.).
NOTE: This reference section focuses on keyboard and mouse input — and will not cover details for gamepads or complex touch input (beyond position or taps).
Add Arrow Keys as Inputs
Add Specific Key as Input
Check If Key Currently Pressed (or Not)
Check If Key Just Pressed (or Just Released)
Check Multiple Properties for Same Key
Check If Multiple Keys Pressed at Same Time
Get Duration of Key Press
Get Current Position of Pointer (Mouse or Touch)
Check If Pointer Is Down (Click or Tap)
Check Left and Right Mouse Buttons Separately
Check for Double Click (or Double Tap)
Check Duration Pointer Was Held Down
Move Sprite Towards Pointer
Find Distance from Sprite to Pointer
Find Angle from Sprite to Pointer
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).
Add this command in your create()
function to add the arrow keys as inputs assigned to a global variable named arrowKey
:
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
as follows:
arrowKey.left
arrowKey.right
arrowKey.up
arrowKey.down
Even though this command will add all 4 arrow keys as potential inputs, your game doesn't necessarily have to actually use all of them. Your game might only check 2 or 3 of the arrow keys for actual input.
You can add an individual key as input by referencing its Phaser key code.
For example, add this command in your create()
function to add the spacebar key as an input assigned to a global variable named fireKey
:
SPACEBAR
represents a specific Phaser key code. Change this key code to match the specific key you need.
Phaser key codes are listed in UPPERCASE:
For letter keys, the keycode is simply the letter (such as: A, B, C, etc.).
For number keys, the keycode is the name of the number (such as: ONE, TWO, THREE, etc.).
For arrow keys (if you wanted to add one or more individually), the keycode is the name of its direction (such as: LEFT, RIGHT, UP, DOWN)
For other keys, the keycode is a name or description (such as: SPACEBAR, ENTER, SHIFT, TAB, CONTROL, etc.).
Keys have two properties that can be used to detect whether or not a specific key is currently being pressed by the player:
isDown
will be true
if the key is currently pressed down — this property will remain true as long as the player continues to hold the key down
isUp
will be true
if the key is currently up (i.e., not being pressed) — this property will remain true as long as the player doesn't press the key
At any given moment, a key is either being pressed or it's not. So one of these two properties has to be true
(and the other property has to be false
). For example, if the key is being pressed, isDown
is true
(which means that isUp
has to be false
).
TIP: The isDown
property is the most commonly-used way to detect input on a key.
For example, here's code that checks for input on global variables named arrowKey
and spacebar
:
This code would be placed in the update()
function (or in a custom function that is called by the update()
function every game loop).
Notice that an if-else statement is used to check input on both the right and left arrow keys. This is done because these particular keys are supposed to be mutually exclusive — the game can't move the player both left and right at the exact same time, so else
is used to help detect three possible conditions: 1. the player is pressing the right arrow key 2. else the player is pressing the left arrow key 3. else the player is not pressing either of them
In the event that the player is actually pressing the right and left arrow keys at the same time (which will probably happen at some point during gameplay), the game will act as if the player were only pressing the right arrow key. This is because the if-else statement first checks for input on the right arrow key (and will skip checking the other conditions once a true
condition is detected).
Notice that a separate if
statement is used to check input on the spacebar. By checking this key with its own if
statement, the game allows the player to press the spacebar at the same time as one of the arrow keys.
Also notice that we didn't include an else
statement for the spacebar — though you could if you needed to for your game.
If you have other keys to check, you would add more if
statements to separately check the input on each key.
If different keys are supposed to be mutually exclusive (i.e., do opposite things) in your game, then you would combine their input checks into the same if-else statement.
In some games, you may want to detect exactly when the player first presses a specific key — or when the player first releases the key that was being pressed.
Remember that your core gameplay occurs in your update()
function, which loops many times per second. Phaser can detect the exact game loop during which a key was first pressed (or first released) by the player.
Keys have two properties that can be used to detect whether the key was just pressed or just released in the current game loop:
justDown
will be true
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 be true
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
For example, here is code to check input on a global variable named spacebar
:
This code would be placed in the update()
function (or in a custom function that is called by the update()
function every game loop).
What would be the difference between checking spacebar.isDown
versus spacebar.justDown
?
For example, let's say that the spacebar is used by the player to fire a weapon:
If your game checks for spacebar.isDown
, the player can simply hold down the spacebar to keep firing the weapon continuously (limited only by the weapon's fire rate).
If your game checks for spacebar.justDown
, the weapon will only fire one time if the player holds down the spacebar. In order to fire the weapon again, the player will have to release the spacebar and press it again.
Either input option is perfectly fine — it simply depends on what kind of input style you want your game to use.
You can also check multiple properties for the same key as part of an if-else statement to do different things based on whether the key was just pressed — versus the key is still being pressed — versus the key was just released — versus the key is still up:
This code would be placed in the update()
function (or in a custom function that is called by the update()
function every game loop).
You don't necessarily have to check all 4 properties for the same key. However, if you are checking multiple properties (2 or more) for the same key, then list them in the order shown above (otherwise, your game logic may not work as you expect) — just remove the property check that you don't need.
If necessary for your game, you can also check whether the player is pressing multiple keys at the same time. For example, maybe your game requires a special combination of keys to be pressed for certain actions to occur.
An if statement can check for multiple keys being pressed together simply by combining multiple conditions using the &&
logical operator (which represents AND).
For example, imagine your game has a power boost feature that allows the player to run at a faster speed when the boostKey
is pressed at the same time as an arrowKey
. The code for checking the player input related to movement might look like this:
This code would be placed in the update()
function (or in a custom function that is called by the update()
function every game loop).
Once a key has been released, you can use the duration
property to get the amount of time (in milliseconds) that the key was held down before being released.
This might be useful for a game where the force or magnitude of a player's action depends on how long a key was held down.
If your game needs to check a key's duration
property, then you would only want to do this after you first detect that the key was just released.
For example, add this code in your update()
function (or in a custom function that is called by the update()
function every game loop):
Inside the if
statement, the value of spacebar.duration
will be the amount of time (in milliseconds) that the spacebar
key was held down before being released. (Reminder: 1000 ms = 1 second)
You can get the current position of the pointer (mouse or touch) using:
game.input.activePointer.x
game.input.activePointer.y
Use these positions to do something in your update()
function or in a custom function.
You can check for a mouse click or touch event (tap) using the isDown
property (similar to checking for a key being pressed):
For example, add this code in your update()
function (or in a custom function that is called by the update()
function every game loop):
If your game will only use the left button of the mouse, then just use the code in the previous section to check if the pointer is down.
However, if your game will use both the left and right buttons on the mouse for different actions, then you can check them separately:
This code would be placed in the update()
function (or in a custom function that is called by the update()
function every game loop).
You can check for a double-click (mouse) or double-tap (touch) by adding an onTap
signal that will call a custom function whenever a tap (or click) occurs. The custom function will be able to detect whether it was a double tap or single tap.
First, add this command to your create()
function:
Whenever a tap (or click) occurs during the game, a custom function named checkTap
will be run. (If desired, you can change the name of the custom function.)
Next, add the custom function after your update()
function:
You can check the duration that the pointer (mouse or touch) was held down by adding an onUp
signal that will call a custom function whenever the pointer is released. The custom function will be able to use the duration
property to get the amount of time (in milliseconds) that the pointer was held down before being released.
This might be useful for a game where the force or magnitude of a player's action depends on how long the pointer was held down.
First, add this command to your create()
function:
Whenever the pointer is released up, a custom function named checkDuration
will be run. (If desired, you can change the name of the custom function.)
Next, add the custom function after your update()
function:
Inside the custom function, the value of pointer.duration
will be the amount of time (in milliseconds) that the pointer was held down (clicked or tapped) before being released. (Reminder: 1000 ms = 1 second)
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 tilemap 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.
Review the Project References for Visual Assets and Audio Assets for help
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'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.
EXAMPLE: Pseudocode for Space Invaders gameplay
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.
In fact, many developers include some or all of their pseudocode in their actual code by writing each line of pseudocode as a comment. 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: Using Pseudocode to Create Actual Code
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.
EXAMPLE: Flowchart of Space Invaders gameplay
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).
You can create a flowchart using web apps such as Google Drawings (available within Google Drive), Draw.io, LucidChart, etc. Of course, you can also draw a flowchart by hand.
TUTORIAL: What is 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.
You can use an image editor (such as Pixlr Editor) to create rectangles of the correct sizes, and fill each rectangle with a different color (to represent different characters or objects).
Code your game in iterative stages by adding, testing, and refining one feature at a time.
Here is a possible order for coding your game features.
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.
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 Kickstarter, Indiegogo, 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.
Construct the website using a tool such as Google Sites (available within Google Drive – if needed, Google has a Get Started with Sites guide) – or code the website directly using HTML and CSS.
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.
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
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.
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 this document.
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.
Phaser is a JavaScript library that makes it easier to code a game — similar to how jQuery is a JavaScript library that makes it easier to modify HTML and CSS on a webpage.
Phaser makes it easy to add your game's graphics to build a game world, add animations, add player inputs, add physics to your game world (velocity, gravity, friction, etc.), detect collisions between game objects, add particle explosions, play sounds, and so much more.
Phaser creates 2D games. However, if you're new to creating games, it's better to start with 2D.
All you need to do is load the Phaser JS file into your webpage using a <script>
tag, and then start using Phaser commands in your game code JS file.
The Phaser JS file contains code defining different types of JavaScript objects. An object is a special type of variable that can contain a set of properties (variables) and methods (functions).
In fact, a property (a variable) within an object can be another object — in other words, an object can contain other objects inside it.
Phaser objects have been specifically designed to have properties and methods that are useful for coding video games.
Every Phaser command is a reference to a Phaser object property or a Phaser object method. If the command has parentheses, then it is a reference to a method. Otherwise, it is a reference to a property.
RESOURCE: W3Schools has a great explanation of JavaScript Objects
Phaser creates your game by inserting an HTML <canvas>
element into your webpage. The <canvas>
element is a container that allows you to add graphics and text inside it using JavaScript. Things added inside the <canvas>
element can be animated and interactive, which makes it perfect to use for a video game.
RESOURCE: If you're interested in learning more, W3Schools has an HTML Canvas tutorial
You will need a copy of Phaser CE (phaser.min.js) inside your game folder, which will also contain your HTML, CSS, and JS files.
Get the latest version of Phaser CE from Phaser.io. Click the "min.js" icon to download the file.
If you cannot download from Phaser.io, you can download this archived version of Phaser CE (version 2.13.1 — released May 15, 2019) from Google Drive. Click the download icon to save the file to your computer (and then add a copy of the file to your game folder in your code editor).
PHASER 3 VS. PHASER CE: Phaser 3 is the newest version of Phaser (first released in February 2018), which is maintained and updated by the folks at Photon Storm. Phaser CE is an open-source version of the previous Phaser 2, which is maintained and updated by the Phaser developer community.
While the API methods for Phaser 3 are similar to Phaser CE, code written for one will not work in the other without modification. All of the code examples in this project guidebook are based on Phaser CE. Eventually, this guidebook will be revised to use Phaser 3, which does offer improvements. However, in the meantime, be sure to use Phaser CE for this project.
Besides the Phaser coding references available in this GitBook, here are additional online resources for learning more about using Phaser CE:
Phaser CE Code Examples on Phaser.io (Phaser 2 = Phaser CE)
Phaser community tutorials on Phaser.io (older examples prior to 2018 use Phaser 2, but newer examples use Phaser 3)
Phaser Game Examples by Emanuele Feronato (her older examples prior to 2018 use Phaser CE, but newer examples use Phaser 3)
PHASER CE / PHASER 2: You can also find other Phaser resources online. Just keep in mind that Phaser 3 was released in February 2018, so newer resources might use Phaser 3 (which is not compatible with Phaser CE). Phaser resources published prior to 2018 will use Phaser 2 (which is now referred to as Phaser CE).
There are other JS game engines available for creating browser-based games. There are also numerous game engines (such as: Unity, etc.) for making games for other platforms (consoles, PC, iOS, Android, etc.). So why use Phaser?
Some advantages of using Phaser:
It has lots of features for developing full-fledged games
It uses JavaScript (many cross-platform game engines use C++)
It's free and open-source
It's updated frequently to fix bugs and add new features
Its capabilities can be extended with plugins and integrations with other software
It has a large community of users (thus lots of online help available)
There are thousands of games made with Phaser (see what's possible and get inspired)
It's easy enough for beginners but powerful enough for seasoned developers
It's perfect for learning how to prototype and develop games — and can serve as a springboard for those who are more serious about game design and game development
Some limitations of using Phaser:
It is limited to creating browser-based games (though you can convert your web app game into a native mobile app)
It is limited to creating 2D games (though you can create pseudo-3D isometric games)
It is limited to single player games "out of the box" (though you can create multiplayer games with Phaser if you're more adventurous)
Overall, Phaser offers many advantages for learners new to creating video games, which is why it is utilized in this project.
However, this project could be modified to use other available game engines. The game design process will be similar, regardless of the specific game engine used for development.
The game world contains all of your game objects. Your game world can extend beyond the , and the can follow the player's sprite as it moves through your game world.
Set Size of Game World
Positions of Objects in Game World
Change Anchor for Object's Position
Get Current Position of Object
Change Position of Object
Layering of Objects in Game World
Change Order of Object's Layer
Get Center Position of Game World
Get Random Position in Game World
Get Boundaries of Game World
Make Sprite Wrap Around World
By default, Phaser will set your game world to be the same size as your game display. However, you can set the boundaries of your game world to be larger than your game display.
This Phaser command should be placed near the beginning of your create()
function.
The first two numbers 0, 0
represent (in order) the x
and y
coordinates of the top-left corner of your game world. Typically, you'll use 0, 0
for your top-left corner.
The next two numbers 3000, 600
represent (in order) the width and height (in pixels) of your game world. Change these to any values that you want (as long as they are equal to or greater than the size of your game display).
Side-Scrolling Game: Make the width of the game world larger than the width of the game display. Keep the height of the game world the same as the height of the game display.
Vertically-Scrolling Game: Keep the width of the game world the same as the width of the game display. Make the height of the game world larger than the height of the game display.
Top-Down Scrolling Game: Make both the width and height of the game world larger than the width and height of the game display.
Every display object (images, sprites, text, etc.) in the game world has a position defined by its x
and y
coordinates: x, y
The x
and y
coordinates represent pixel positions, relative to the top-left corner of the game world (which is 0, 0
).
The x
coordinates start at 0
(left boundary) and increase as you move from left to right.
The y
coordinates start at 0
(top boundary) and increase as you move from top to bottom.
For example, an object placed at coordinates 250, 100
will be positioned 250 pixels to the right and 100 pixels down, relative to the top-left corner.
The x
and y
coordinates of a display object (image, sprite, text, etc.) represent the position of the object's anchor.
By default, an object's anchor is its top-left corner. However, you can change the anchor for an object (e.g., to make the object's center become its anchor for positioning).
For example, to change the anchor of a sprite named player
:
The first number represents the horizontal position of the anchor, as a number between 0-1 (0 = left edge, 0.5 = horizontal center, 1 = right edge).
The second number represents the vertical position of the anchor, as a number between 0-1 (0 = top edge, 0.5 = vertical center, 1 = bottom edge).
For some objects, it will make sense to leave the anchor as the object's top-left corner (the default).
For other objects (especially sprites), it will make more sense to set an object's anchor to its center (0.5, 0.5)
.
For certain objects, it might even make sense to set the object's anchor to its top-middle (0.5, 0)
or its bottom-middle (0.5, 1)
.
The current x
and y
coordinates of an object's position (based on its anchor) are stored as properties of the object.
For example, to get the current position of a sprite named player
:
player.x
represents its current x
coordinate
player.y
represents its current y
coordinate
You can also set or modify the x
and/or y
coordinates of an object to change its position in the game world.
For example, you could change the position of a sprite named enemy
by assigning it specific x
and y
coordinates:
As another example, you could move a sprite named player
relative to its current position:
In the example above, the player
sprite would be moved 5 pixels to the right and 5 pixels up from its current position.
Increasing the x
coordinate moves an object to the right, while decreasing x
moves the object to the left.
Increasing the y
coordinate moves an object down, while decreasing y
moves the object up.
IMPORTANT: The order in which game objects 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, that image should be added first in the create()
function, so it will be the farthest back layer. Each new display object (image, sprite, text, etc.) that is added in the code will appear in front of the previous layers.
For example, if you added the player's sprite first and then added the background image, the player's sprite would be hidden behind the background. To fix this, change the order of the code, so the background image is added first and then the player's sprite is added.
TIP: You can use the layering of the game objects to help create a sense of depth within your otherwise flat 2D game world. For example, if your game takes place in a forest, you could add images of trees in the background, then add the player's sprite, and then add more trees in the foreground. As the player's sprite moves through the game world, it will pass in front of the background trees but behind the foreground trees — giving the illusion of depth within your virtual forest.
Your Phaser game maintains a display list of the stacking order of every object in your game world. If necessary, you can modify the relative order of an object's layer within this list.
For example, the following could be used to modify the stacking order of a sprite named enemy
:
enemy.moveDown();
would move this sprite farther back by one layer
enemy.moveUp();
would move this sprite closer to the front by one layer
enemy.sendToBack();
would make this sprite the farthest back layer
enemy.bringToTop();
would make this sprite the closest layer
game.world.centerX
is the center x
coordinate
game.world.centerY
is the center y
coordinate
game.world.randomX
is a random x
coordinate (will be different every time you use it)
game.world.randomY
is a random y
coordinate (will be different every time you use it)
game.world.left
is the left x
boundary (typically equals 0
)
game.world.right
is the right x
boundary (typically equals width of game world)
game.world.top
is the top y
boundary (typically equals 0
)
game.world.bottom
is the bottom y
boundary (typically equals height of game world)
You can make a sprite (or a member of a sprite group) wrap around to the opposite side of world if it moves outside of the game world boundaries.
For example, if the sprite moves out of the world across the right boundary, the sprite will reappear at the left boundary of the world (and vice versa). The same is true for the top and bottom boundaries.
This feature is typically used for single-screen games. For example, in Asteroids, the asteroids and the player's ship wrap around the world. However, this feature can be used for any size of game world.
To make an individual sprite wrap around the world:
The code example above must be placed in the update()
function (otherwise, it will not work).
player
represents the sprite's variable name. Change this to the name of your sprite.
32
represents the amount of padding (in pixels) to allow before wrapping the sprite. In this case, the sprite can move 32 pixels outside of the world before the sprite will be wrapped around to the other side. Change this to any value you want (including 0 for no padding).
The advantage of using padding is that it can prevent the sprite from appearing to be visually split — i.e., part of the sprite shown on one side of the world and the rest of the sprite shown on the opposite side of the world.
To prevent this visual split, use a padding value that is at least half the width or height of the sprite (whichever is greater).
To make each member of a sprite group wrap around the world:
Again, the code example above must be placed in the update()
function (otherwise, it will not work).
asteroidGroup
represents the group's variable name. Change this to the name of your sprite group.
asteroid
is a local variable used in the function to represent an individual member of the group. Change this to any local variable name that makes sense.
20
represents the amount of padding to allow before wrapping the sprite. Change this to any value you need.
If you only want the sprite to wrap around the world horizontally between left and right (but not between top and bottom):
If you only want the sprite to wrap around the world vertically between top and bottom (but not between left and right):
You can add text to your game to display feedback or information as part of your game's user interface. Phaser handles text as a display object with properties and methods that allow you to modify the text in various ways.
Add Text (and Set Style)
Add Text Shadow
Change Anchor for Text Position
Change Text Position
Rotate Text to Angle
Make Text Stay Fixed to Camera View
Hide Text (and Unhide)
Change Text Content
Clear Out Text Content
Add Line Breaks in Text Content
Change Text Style
If you have static text (i.e., won't change during game) that just needs to be positioned and have basic style properties set, you can add the text object directly (without assigning it to a variable):
The command to add text would be placed in your create()
function (probably towards the end, so that it will be displayed in front of other objects).
350, 20
represent the x
and y
coordinates where the text will be positioned (based on the text's anchor, which is its top-left corner by default). Change these values to the position you need.
'Health'
represents the text string to display as the text. Replace this with your text.
OPTIONAL: You can style the text by providing a comma-separated list of style properties and values inside curly braces { }
(i.e., a JSON object).
However, if the text will be dynamic (i.e., will change during the game) or needs to be modified beyond basic style properties (such as adding a text shadow, hiding the text, etc.), then you need to declare a variable and assign the text object to the variable:
scoreText
represents a variable name. Change this to the name of your variable.
If you only need to change the text properties within the create()
function, you can assign the text object to a local variable.
Otherwise, if you need to change the text in the update()
function or a custom function, then assign the text object to a global variable.
The default style for text will be Arial 20pt bold in black — but you can modify any of these default properties.
fontSize
sets the size of the font. By default, it will use '20pt'
(20pt is approximately 27px). For more accurate layout, you may want to use px (pixels) to set the size, such as: '30px'
fontWeight
sets the weight of the font, such as 'normal'
or 'bold'
. By default, it will use 'bold'
.
If you've assigned the text object to a variable, you can add a shadow behind the text:
The first value is the horizontal offset (in pixels) of the shadow. Positive values shift the shadow to the right, while negative values shift it to the left. Use a value of 0
for no horizontal offset. (A small value between 0-5 usually works well for offset.)
The second value is the vertical offset (in pixels) of the shadow. Positive values shift the shadow down, while negative values shift it up. Use a value of 0
for no vertical offset. (A small value between 0-5 usually works well for offset.)
The fourth value is the blur (in pixels) of the shadow. Use a value of 0
for a sharp shadow. Use a value up to about 10
for a softer shadow. (A small value between 0-5 usually works well for blur.)
The x
and y
coordinates of a display object (text, image, sprite, etc.) represent the position of the object's anchor.
By default, an object's anchor is its top-left corner. However, you can change the anchor for an object (e.g., to make the object's center become its anchor for positioning).
For example, to change the anchor of a text object named messageText
:
The first number represents the horizontal position of the anchor, as a number between 0-1 (0 = left edge, 0.5 = horizontal center, 1 = right edge).
The second number represents the vertical position of the anchor, as a number between 0-1 (0 = top edge, 0.5 = vertical center, 1 = bottom edge).
For most text, it will be fine to leave the anchor as the top-left corner (the default).
However, for some text, it may make more sense to set its anchor to its center (0.5, 0.5)
— or possibly to its top-middle (0.5, 0)
or bottom-middle (0.5, 1)
.
If you need to change the position of the text, just change its x
and/or y
coordinates.
For example, you could change the position of a text object named messageText
:
You can rotate text (around its anchor point) by changing the value of its angle
property.
For example, you could rotate a text object named gameTitle
:
-10
represents the angle in degrees. Change this to any value from -180 to 180. Negative values rotate the image counterclockwise. Positive values rotate the image clockwise.
Phaser also has a property called rotation
that can be used to get or set the angle of rotation of an text. The only difference is that the rotation
property uses radians for its units, instead of degrees (2π radians = 360°).
You can make an image stay fixed within the game camera view, so the image won't move when the game world scrolls.
Phaser display objects (such as images, text, sprites, etc.) have a property called fixedToCamera
. By default, this property is set to false
— changing this value to true
will make the object stay fixed to the camera view.
For example, you can make a text object named scoreText
stay fixed to the camera view:
If you've assigned the text object to a variable, you can hide (or unhide) the text by changing its visible
property.
For example, to hide the text:
To show the text again, just unhide it:
You can change the content of the text that is displayed by assigning a new text string to its text
property:
You can concatenate (combine) text strings and/or variable values using the +
sign. JavaScript will treat the result as one continuous text string.
For example, if your game has a numerical variable named score
, you could display a text label plus the value of score
:
Notice that a blank space was included in the text string (inside the quotes). Otherwise, there would be no space between the word "Score" and the number when the text is displayed.
You can clear out the text content (make it blank) by simply assigning an empty string to the text
property:
If necessary, you can add line breaks to split up your text into multiple lines.
To add a line break, insert: \n
For example, the following text will be split into 3 lines:
The text would be displayed as:
Game Over You Lost Try Again
If you do use multi-line text, you may want to change the text so each line will be center-aligned:
The options for align
are 'left'
, 'center'
, or 'right'
. By default, the alignment will use 'left'
. This can also be set as one of the style properties when you add the text object.
If you've assigned the text object to a variable, you can later modify any of its style properties. Here's a few examples:
You can add images to help create your game world (backgrounds, etc.) or as part of your game's user interface (icons, etc.). Phaser handles each image as a display object with properties and methods that allow you to modify the image in various ways.
Load Image
Add Image
Change Anchor for Image Position
Change Image Position
Make Image Stay Fixed to Camera View
Hide Image (and Unhide)
Rotate Image to Angle
Scale Image (or Flip Image)
Crop Image Using Rectangle
Tint Image Using Color
Each image file (also referred to as an asset) must be loaded into the game's memory before you can add it to your game world.
The command to load an asset is always placed in your preload()
function.
'logo'
represents an asset key — a key is sort of like a variable name. You decide what string to use for the key, as long as each image asset has a unique key.
'assets/images/game-logo.png'
represents the folder path and filename of the image to load. Change this to match your specific folder path and filename.
Once an image asset has been loaded into memory, you can add the image to your game world.
If you have an image that simply needs to placed at a specific position and won't be modified in any way, you can add the image directly (without assigning it to a variable):
The command to add an image is typically placed in the create()
function:
0, 0
represent the x
and y
coordinates where the image will be positioned in the game world (based on the image's anchor, which is its top-left corner by default). Change these values to the position you need.
'background'
represents the asset key of the image to use. Replace this with your asset key.
However, if you will need to modify the image (change image property, rotate image, etc.), then you need to declare a variable and assign the image to the variable:
gameTitle
represents a variable name. Change this to the name of your variable.
If you only need to modify the image within the create()
function, you can assign the text object to a local variable.
Otherwise, if you need to modify the image in the update()
function or a custom function, then assign the image to a global variable.
The x
and y
coordinates of a display object (text, image, sprite, etc.) represent the position of the object's anchor.
By default, an object's anchor is its top-left corner. However, you can change the anchor for an object (e.g., to make the object's center become its anchor for positioning).
For example, to change the anchor of a image named gameTitle
:
The first number represents the horizontal position of the anchor, as a number between 0-1 (0 = left edge, 0.5 = horizontal center, 1 = right edge).
The second number represents the vertical position of the anchor, as a number between 0-1 (0 = top edge, 0.5 = vertical center, 1 = bottom edge).
For most images, it will be fine to leave the anchor as the top-left corner (the default).
However, for some images, it may make more sense to set its anchor to its center (0.5, 0.5)
— or possibly to its top-middle (0.5, 0)
or bottom-middle (0.5, 1)
.
If you need to change the position of an image, just change its x
and/or y
coordinates.
For example, you could change the position of a image named gameTitle
:
You can make an image stay fixed within the game camera view, so the image won't move when the game world scrolls.
Phaser display objects (such as images, text, sprites, etc.) have a property called fixedToCamera
. By default, this property is set to false
— changing this value to true
will make the object stay fixed to the camera view.
For example, you can make an image named gameTitle
stay fixed to the camera view:
You can hide (or unhide) an image by changing its visible
property.
For example, to hide an image named gameOverPic
:
To show the image, just unhide it:
You can rotate an image (around its anchor point) by changing the value of its angle
property.
For example, you could rotate an image named gameTitle
:
-10
represents the angle in degrees. Change this to any value from -180 to 180. Negative values rotate the image counterclockwise. Positive values rotate the image clockwise.
Phaser also has a property called rotation
that can be used to get or set the angle of rotation of an image. The only difference is that the rotation
property uses radians for its units, instead of degrees (2π radians = 360°).
You can make an image larger or smaller by changing its scale
property. You can also use this property to flip the image (forming a mirror image).
For example, you could scale an image named gameTitle
:
The first number represents the horizontal scale, which affects the image's width. Change this to the value you need for scaling.
The second number represents the vertical scale, which affects the image's height. Change this to the value you need for scaling.
Each direction (horizontal or vertical) can be scaled using the same value (which keeps the same image proportions) — or can be scaled using different values (which changes the proportions).
A scale value less than 1 will make the image smaller in that direction. For example, a value of 0.5 would make the image half of its original size.
A scale value of exactly 1 will keep the image its original size in that direction.
A scale value greater than 1 will make the image larger in that direction. For example, a value of 2 would make the image twice its original size.
Using a negative scale value will flip the image in that direction (forming mirror image). For example, a value of -1 will keep the image its original size but flip it.
You can apply a rectangular crop to an image. Any portion of the image outside the crop rectangle will be hidden from view. The crop is a reversible visual effect — you can change the crop or removed it if desired.
The first line of code creates a rectangle and assigns it to a variable named livesCrop
. Change the variable name for your rectangle.
0, 0
represent the x and y coordinates of the top-left corner of the crop rectangle (relative to the top-left corner of the image that it will crop). Change these values based on where you need to position your crop rectangle on the image.
75, 25
represent the width and height (in pixels) of the crop rectangle. Change these values based on how much of the image you need to show.
The second line of code applies the crop rectangle to an image named livesBar
. Change the names to match the your image and crop rectangle.
If needed, you can change the size and/or position of your crop rectangle and then update to show the new cropped image.
If you need to change the size of the crop rectangle, change the width and/or height of the rectangle.
If you need to change the position of the top-left corner of the crop rectangle, change the x
and/or y
position of the rectangle. (NOTE: This changes what part of the original image is shown, but the cropped image will still be positioned at the same location in the game world.)
Once you've changed the value(s) for your crop rectangle, update the crop.
Unfortunately, there isn't a built-in method to directly "uncrop" an image once it has been cropped. Instead, the solution is to make the crop rectangle the same size as the original image and then apply this full-sized crop (which will show the entire image).
You can tint an image using a specific color. For example, if you have a background of a light blue sky ("daytime"), you could tint it a reddish color to represent sunrise or sunset — or tint it a dark color to represent nighttime.
For example, to tint an image named sky
:
The tint color is added as a semi-transparent layer on top of the image, so the tint color will visually mix with the colors in the image.
For example, adding a yellow tint on top of a blue image will actually make the image look green (yellow + blue = green).
So you may need to try different tint colors to achieve your desired result (and it may not be possible to get the exact result you want, depending on the original colors of the image).
To remove the tint color and restore the image to its original appearance, change the tint color to 0xffffff
(white represents no tint):
You can add a special type of image object called a sprite to your game world. Sprites are used for characters and other game objects that need to move and interact with each other.
Sprites are basically image objects that have additional properties allowing for animations, simulated physics interactions (moving, falling, colliding, etc.), and simulated life (gaining health, losing health, being killed, etc.).
A sprite can be created from a regular image, but an animated sprite will need to use a special type of image called a spritesheet, which is a set of identical-sized smaller images (called frames) that have been combined into one large image (which usually has a transparent background).
Example spritesheet image consisting of 9 frames
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 to create the illusion that it is moving or changing.
Load Spritesheet
Add Sprite
Change Anchor for Sprite Position
Get Current Position of Sprite
Change Position of Sprite
Rotate Sprite to Angle
Add Animations to Sprite
Play Animations
Display Specific Sprite Frame
Hide Sprite (and Unhide)
Remove Sprite (Kill)
Set Sprite Lifespan
Set Sprite Health
Damage or Heal Sprite
Set Maximum Health for Sprite
Recycle Dead Sprite (Revive or Reset)
Run Function When Sprite Killed
Create Health Bar
Create Lives Counter
Each spritesheet image file (also referred to as an asset) must be loaded into the game's memory before you can add it to your game world.
The command to load an asset is always placed in your preload()
function.
'dude'
represents an asset key — a key is sort of like a variable name. You decide what string to use for the key, as long as each spritesheet asset has a unique key.
'assets/images/dude.png'
represents the folder path and filename of the spritesheet image to load. Change this to match your specific folder path and filename.
32, 48
represent the width and height (in pixels) of each frame within the spritesheet. Phaser will use these measurements to divide the spritesheet into a set of individual frames. Change these values to match your frame size.
If you're not sure of the size of each frame in a spritesheet, you can easily calculate it from the size of the entire spritesheet.
Spritesheets have their identical-sized frames combined into a grid (with a certain number of columns and rows).
Determine the width and height (in pixels) of the spritesheet image. One way to do this is to open the spritesheet in an image editor (such as Pixlr Editor, etc.).
Count the number of frames (columns) contained in the top row across the spritesheet's width. Divide the width of the spritesheet by the number of columns. The result should be a whole number and represents the width of each frame.
Count the number of frames (rows) contained in the left column down the spritesheet's height. Divide the height of the spritesheet by the number of rows. The result should be a whole number and represents the height of each frame.
Once a sprite's asset (either a "regular" image or a spritesheet) has been loaded into memory, you can add the sprite to your game world.
Declare a global variable for the sprite, and add the sprite by assigning it to the variable. The command to add a sprite is typically placed in the create()
function.
player
represents a variable name. Change this to the name of your variable.
100, 500
represent the x
and y
coordinates where the sprite will be positioned in the game world (based on the sprite's anchor, which is its top-left corner by default). Change these values to the position you need.
'dude'
represents the asset key of the spritesheet (or image) to use. Replace this with your asset key.
The x
and y
coordinates of a display object (text, image, sprite, etc.) represent the position of the object's anchor.
By default, an object's anchor is its top-left corner. However, you can change the anchor for an object (e.g., to make the object's center become its anchor for positioning).
The command to change the anchor is typically placed in the create()
function (after adding the sprite).
For example, to change the anchor of a sprite named player
:
The first number represents the horizontal position of the anchor, as a number between 0-1 (0 = left edge, 0.5 = horizontal center, 1 = right edge).
The second number represents the vertical position of the anchor, as a number between 0-1 (0 = top edge, 0.5 = vertical center, 1 = bottom edge).
For most sprites, it will make the most sense to set its anchor to its center (0.5, 0.5)
.
However, for some sprites, it might make more sense to set its anchor to its top-middle (0.5, 0)
or bottom-middle (0.5, 1)
— or possibly just leave the anchor as the top-left corner (the default).
The current x
and y
coordinates of an sprite's position (based on its anchor) are stored as properties of the sprite.
For example, to get the current position of a sprite named player
:
player.x
represents its current x
coordinate
player.y
represents its current y
coordinate
If you need to change the position of a sprite, just change its x
and/or y
coordinates.
For example, you could change the position of a sprite named player
:
As another example, you can also move a sprite relative to its current position:
In the example above, the player
sprite would be moved 5 pixels to the right and 5 pixels up from its current position.
Increasing the x
coordinate moves an object to the right, while decreasing x
moves the object to the left.
Increasing the y
coordinate moves an object down, while decreasing y
moves the object up.
You can rotate an image (around its anchor point) by changing the value of its angle
property.
For example, you could rotate a sprite named player
:
-90
represents the angle in degrees. Change this to any value from -180 to 180. Negative values rotate the image counterclockwise. Positive values rotate the image clockwise.
Phaser also has a property called rotation
that can be used to get or set the angle of rotation of a sprite. The only difference is that the rotation
property uses radians for its units, instead of degrees (2π radians = 360°).
A spritesheet typically contains one or more sequences of frames that represent animations.
For example, in the spritesheet shown below, the first four frames represent an animation sequence for "running to the left", the fifth frame represents "standing still", and the last four frames represent an animation sequence for "running to the right."
Sprite frames are referenced by an index number, with the first frame numbered as 0, the second frame numbered as 1, the third frame numbered as 2, etc.
You can add an animation sequence to a sprite by specifying which particular frames should be played during the animation.
The command to add an animation is typically placed in the create()
function (after adding the sprite).
For example, to add an animation to a sprite named player
(using the spritesheet shown above):
'left'
represents an animation key — a key is sort of like a variable name. You decide what string to use for the key, as long as each animation has a unique key.
[0, 1, 2, 3]
is an array listing the index numbers of the specific spritesheet frames to use for the animation. The frames will be played in the order listed in the array. Change the array to list whichever frame numbers you need (in the order you need them to play).
10
represents the frame rate for the animation, in frames per second. Depending on your animation, you can change this number to make it play faster or slower.
true
indicates that this animation should play repeatedly in a loop. If you don't want an animation to loop, then list false
.
This just adds the animation into memory, so it's ready to use. The animation won't actually play until you use a command telling it to start playing.
If your sprite has multiple animations, use additional commands to add the other animation(s):
Animations won't play until you tell them to do so.
For example, to play an animation that was added to a sprite named player
:
'right'
represents the animation key to play. Change this to your animation key.
To switch the sprite to play a different animation, simply change the animation key:
To stop a sprite's animation:
When the animation stops, the sprite will display a random frame from the most recent animation (whichever frame was being displayed at the exact moment the animation stopped).
In some cases, you may want to the sprite to display a specific frame when no animation is being played.
To make a sprite display a specific frame (e.g., when no animation is being played), simply change the frame
property of the sprite.
4
represents the index number of the frame to display. Remember that frames are numbered starting with 0 (so 4 is actually the 5th frame of the spritesheet). Change this value to the frame index number you want.
There are two properties that can be used to hide a sprite:
visible
property
exists
property
You can change a sprite's visible
property to make the sprite invisible. However, the sprite will still be involved in physics interactions (moving, falling, colliding, etc.) because the sprite still exists (it's just invisible).
For certain games, invisible sprites could be interesting (e.g., creating invisible enemies that the player must avoid, etc.).
For example, to make a sprite named enemy
invisible:
To make the sprite visible again:
You can change a sprite's exists
property to make the sprite invisible and also prevent it from being affected by any physics interactions. When you want, you can easily make the sprite exist again.
For example, to make a sprite named powerUp
not exist:
To make the sprite exist again:
You can remove a sprite from the game use the kill()
method. This sets the sprite's visible
, exists
, and alive
properties to false
.
For example, to remove a sprite named coin
:
Killing a sprite removes it from the game, but not from the game's memory. This allows you to recycle "dead" sprites by adding them back into the game using the revive()
or reset()
method. For example, this would allow you to give the player's character additional lives — or to replenish resources or enemies that get removed during the course of the gameplay.
If desired, you can give a sprite a limited lifespan in the game, which will automatically remove (kill) the sprite after it has existed for a certain amount of time.
For example, to set a lifespan for a sprite named powerUp
:
5000
represents duration of the lifespan in milliseconds (1000 ms = 1 second). The lifespan begins as soon as the sprite has been added to the game. When the lifespan expires, the sprite will be automatically removed (killed).
If desired, you can give a sprite a certain amount of health. Your game can have various ways for the sprite to gain health (collecting resource, etc.) or lose health (being attacked, etc.). If the sprite's health reaches zero, the sprite will be automatically removed (killed).
This health property is typically used for sprites representing characters (player, enemies, allies, etc.) — but it can be used for any sprite you want (obstacles that must be destroyed, etc.).
For example, to set the health for a sprite named player
:
100
represents amount of health. Change this to any value that makes sense for your game.
You should set a sprite's initial health value in the create()
function after adding the sprite. If needed, you can also set a new value in the update()
function or custom functions if certain conditions or events occur.
You can increase or decrease a sprite's health using the heal()
or damage()
method. You would most likely use these methods in the update()
function or custom functions if certain conditions or events occur in the game.
For example, to decrease the health for a sprite named player
:
10
represents amount to decrease the sprite's health. Change this to any value that makes sense for your game.
For example, to increase the health for a sprite named player
:
10
represents amount to increase the sprite's health. Change this to any value that makes sense for your game.
You can also set the maximum health for a sprite (so it cannot be healed beyond this value).
For example, to set the maximum health for a sprite named player
:
100
represents the maximum health value possible for the sprite. Change this to any value that makes sense for your game.
You would typically set a sprite's maximum health value in the create()
function after adding the sprite and setting its initial health value. If needed, you can also set a new value for the maximum health in the update()
function or custom functions if certain conditions or events occur.
You can recycle "dead" sprites by adding them back into the game using the revive()
or reset()
method. For example, this would allow you to give the player's character additional lives — or to replenish resources or enemies that get removed during the course of the gameplay.
REVIVE: The revive()
method adds the dead sprite back into the game at the same position where it was killed (though you add commands to change the sprite's position). The sprite will also have the same values for its physics motion (velocity, acceleration, etc.) that it had when it was killed (though you can add commands to change these values).
RESET: The reset()
method adds the dead sprite back into the game at a specific position you indicate. It also resets the sprite's physics motion (velocity, acceleration, etc.) to zero.
You can use revive()
and reset()
for sprites that are supposed to have health (player, etc.) or for sprites that don't have health (resource, etc.).
For example, to revive a dead sprite named powerUp
that isn't supposed to have health:
For example, to revive a dead sprite named player
that should have health:
50
represents the amount of health to give the sprite when adding it back to the game. Change this to any value that makes sense for your game.
For example, to reset a dead sprite named powerUp
that isn't supposed to have health:
400, 200
represent the x
and y
coordinates where the sprite will be added back into the game world (based on the sprite's anchor, which is its top-left corner by default). Change these values to the position you need.
For example, to revive a dead sprite named player
that should have health:
The first two numbers 100, 500
represent the x
and y
coordinates where the sprite will be added back into the game world (based on the sprite's anchor, which is its top-left corner by default). Change these values to the position you need.
The last number 50
represents the amount of health to give the sprite when adding it back to the game. Change this to any value that makes sense for your game.
You can add an onKilled
event to a sprite to run a function whenever that sprite is killed (either because the kill()
method was used on the sprite or because the sprite's health
reached zero).
The onKilled
event should be added in your create()
function (after adding the sprite).
For example, to add an onKilled
event for the player
sprite:
An onKilled
event is commonly added to the player sprite to end the game — or to add the player back into the game (e.g., back to the start or at a checkpoint) depending on how many "lives" the game allows (endless or limited).
For example, this code checks to see if the player has any lives remaining. If so, the player is reset back into the game at the last checkpoint position with full health. If the player is out of lives, then it runs a custom function called gameOver()
:
Some games display a health bar to represent how much health the player's character has remaining compared to the maximum possible. The health bar shrinks or expands as the player's health changes.
You can create your own custom health bar by following these steps:
Create an image of a solid rectangle (whatever color you want) that will be used as the health bar (to represent health remaining). This example will use a green rectangle that is 200px in width and 20px in height:
Create an image of an identical-sized solid rectangle (of a different color) that will be used as a background bar (to represent missing health). This example will use a red rectangle that also is 200px in width and 20px in height:
Before your preload()
function, declare a global variable for the health bar:
In your preload()
function, load the two rectangle images:
In your create()
function, set the values for the initial health and the maximum health for your player's sprite (after adding the sprite):
The background bar (red) and the health bar (green) will be added to the game at the same position. However, the background bar will be added first, so it will be stacked behind the health bar.
In your create()
function, add the background bar by assigning it to a local variable. Then add the health bar at the same position by assigning it to its global variable.
You should also add a text label to the left of the bars by assigning it to a local variable.
If your game world is larger than your game display, then be sure to make the images and text label fixed to the camera.
When the player's health changes, the width of the health bar is supposed to shrink or expand proportionately. If the health bar shrinks, it will reveal some of the background bar behind it. If the health bar expands, it will cover more of the background bar.
We can make the health bar shrink or expand by changing its scale
property. The correct value for the scale can be calculated by dividing the player's health by its maximum health value.
For example, if the player's health is currently 25 and its maximum health value is 100, the current scale for the health bar should be 0.25 (25/100 = 0.25), which would look like this:
Anytime the player's health changes (sprite is damaged, healed, revived, or reset), you need to change the scale for the width of the health bar:
This command sets a scale value for the width and height of the health bar:
player.health / player.maxHealth
is the calculated scale for the width
1
is the scale for the height (1 = 100% height)
HINT: If desired, you could use these steps to create a scalable bar to represent other elements of your game (such as: timer bar, progress bar, etc.).
Some games display a counter to represent how many lives the player has remaining. This counter could simply be shown as a number. However, in some games, the lives counter is shown as a row of icons representing the individual lives remaining. As the number of lives changes, the number of icons displayed changes.
Games that utilize an icon-based lives counter typically set a relatively low limit ( < 10) on the maximum number of lives available. (Otherwise, if the limit is higher, it is probably better to just display an actual number to the player.)
You can create your own custom icon-based lives counter by following these steps:
First determine the maximum number of lives that your game will allow the player to accumulate. Next you need to create an image similar to a spritesheet that shows a row of icons representing the maximum number of lives.
The best way to create an image like this is to find or create an icon representing one life. Determine the width of the icon, and then paste copies of it side-by-side at evenly-spaced widths to create a row of icons. Save this image as your lives counter.
For example, the spaceship icon is 25px in width (including the transparent area on both sides). Copies of the icon were placed side-by-side every 25px, so the final image of 5 icons is 125px in width (5 × 25 = 125).
Before your preload()
function, declare global variables for the current number of player lives and the maximum number of lives, and assign values to both.
You also need to declare global variables to represent the lives counter image, the width of each lives icon, and a rectangle that will be used to crop the lives counter image:
In your preload()
function, load the lives counter image:
In your create()
function, add the lives counter image by assigning it to its global variable. Also add a text label by assigning it to a local variable. If your game world is larger than your game display, be sure to make the image and text label fixed to the camera.
Phaser allows you to apply a rectangular crop to an image. Any portion of the image outside the crop rectangle will be hidden from view. The crop is a reversible visual effect — you can adjust the size of the crop whenever needed.
The crop rectangle for the lives counter should:
be positioned at the top-left corner of the lives counter image
have a width equal to the number of lives multiplied by the width of a lives icon
have the same height as the lives counter image (to show its full height)
In your create()
function (immediately after adding the lives counter image and its text label), add this code:
livesIcon
represents the width of an individual icon in the lives counter image. The icon width is calculated by dividing the width of the entire lives counter image by the maximum number of lives (i.e., the number of icons in the image).
livesCrop
is the rectangle that will be used to crop the lives counter image:
0, 0
will be the position of the crop rectangle relative to the top-left corner of the image being cropped.
lives * livesIcon
will be the width of the crop rectangle.
livesCounter.height
will be the height of the crop rectangle.
The crop()
method applies the crop rectangle to the lives counter image.
Anytime the player gains or loses a life (i.e., anytime the value of lives
changes), you need to re-calculate the width of the crop rectangle, and then update the crop of the lives counter image (to show more or fewer icons):
HINT: If desired, you could use these steps to create an icon-based counter to represent other elements of your game (such as: number of resources remaining, number of achievements earned, etc.).
You can add one or more background images to your game, and if your game world is larger than the game display, you can also make your game world scroll automatically to follow the player. This can be done without using a tilesprite — you can just position whatever image(s) you want throughout your game world, and make the game camera follow the player.
A is used to create a single background image that looks like it is scrolling as the player moves. A tilesprite can be used for a single-screen game or for an extended game world.
However, not just any image will work as a tilesprite. There are two main criteria: pattern and size.
In order to create a smooth scrolling effect, the tilesprite image must have a pattern that matches seamlessly across opposite edges — which could be its horizontal edges (left and right), its vertical edges (top and bottom), or both — depending on which direction(s) your game world is supposed to scroll.
In the image above, notice that the pattern of the sky and clouds along the left edge matches perfectly with the right edge. If you were to place multiple copies of this image side-by-side, the pattern would repeat seamlessly, so this image would be perfect for a horizontally scrolling game.
The other criteria for a tilesprite background image is that it should be the exact size of your game display. If necessary, use an image editor to resize your tilesprite image to match the width and height of your game display. (Alternatively, you could change the size of your game display to match the size of the image.)
Load Image for Tilesprite
Add Tilesprite
Make Tilesprite Stay Fixed to Camera View
Scroll Tilesprite When Player Moves
Create Parallax Effect to Simulate Depth
Just like other images, the image file for your tilesprite must be loaded into the game's memory before you can add it to your game world.
The command to load an asset is always placed in your preload()
function.
'sky'
represents an asset key — a key is sort of like a variable name. You decide what string to use for the key, as long as each image asset has a unique key.
'assets/images/sky-clouds.jpg'
represents the folder path and filename of the image to load. Change this to match your specific folder path and filename.
Once the image asset has been loaded into memory, you can add the tilesprite to your game world.
You will need to declare a global variable for the tilesprite and assign the tilesprite to the variable in your create()
function.
For example, to add a tilesprite to the variable named skyTile
:
0, 0
represent the x
and y
position for the top-left corner of the tilesprite image. This is normally where you want to position tilesprite images.
800, 600
represent the width and height of the tilesprite image. Be sure the size of your tilesprite matches the size of your game display. Change these numbers to match your tilesprite and game display.
'sky'
is the asset key for the tilesprite image.
Remember that the code to add background images, such as tilesprites, should be listed in your create()
function before the code to add other sprites, images, text, etc.
You can also layer multiple images to create a more complex background. This only works if the layer(s) in front have some transparent areas, so you can see part of the other layer(s) behind.
For example, imagine the background of your game will consist of these three tilesprites:
skyTile
will be the farthest back tilesprite layer — an image showing sky and clouds
mountainTile
will be the middle tilesprite layer — an image showing a mountain ridge but with a transparent sky (so you can see part of the skyTile
behind it)
cityTile
will be the closest tilesprite layer — an image showing a silhouette of city buildings but with a transparent sky (so you can see part of the mountainTile
and skyTile
behind it)
Add the tilesprites in the order of their "depth" from back to front:
If your game world is larger than your game display, then you must make the tilesprite stay fixed to the game camera's view.
For example, to make a tilesprite named skyTile
stay fixed to the camera view:
If you have layered multiple tilesprite images to create your background, then be sure to fix each of them to the camera.
In order to create a realistic scrolling effect, the tilesprite should scroll in the opposite direction that the player is moving. For example, if the player's sprite is moving to the right, then the tilesprite should scroll to the left.
You can scroll the tilesprite by changing its tilePosition.x
and/or tilePosition.y
values — depending on whether the tilesprite should scroll horizontally, vertically, or both:
Change the tilesprite's tilePosition.x
to simulate horizontal scrolling:
If the player is moving to the right, make the tilesprite scroll left by decreasing its tilePosition.x
.
If the player is moving to the left, make the tilesprite scroll right by increasing its tilePosition.x
.
Change the tilesprite's tilePosition.y
to simulate vertical scrolling:
If the player is moving down, make the tilesprite scroll up by decreasing its tilePosition.y
.
If the player is moving up, make the tilesprite scroll down by increasing its tilePosition.y
.
The code to scroll the tilesprite should be included in your update()
function (after any player movement has occurred).
If the tilesprite scrolling in your game seems too fast or too slow (relative to the player's movement), adjust the value used to scroll the tilesprite:
If the scrolling seems too fast, use a smaller value.
If the scrolling seems too slow, use a larger value.
There are several options for how to scroll the tilesprite, depending on how your game is set up:
If you have a single-screen game that moves the player by directly changing the player's position (x
and/or y
coordinates), use Option 1 below.
If you have a single-screen game that moves the player by changing its velocity or acceleration, use Option 2 below.
If you have an extended game world with a game camera set to follow the player, use Option 3 below.
If you have a single-screen game (game world is same size as game display) that moves the player's sprite by directly increasing or decreasing its x
or y
coordinates, then move the tilesprite at the same time (but in the opposite direction).
For example, this code checks the arrowKey
inputs used to move the player
sprite and horizontally scrolls a tilesprite named skyTile
:
The example above scrolls the tilesprite by the same amount that the player's position is moved. As necessary, adjust the scrolling value to a number that feels right for your game.
If your game needs to scroll the tilesprite vertically, use similar code to change the y
positions of the player and tilesprite.
NOTE: +=
is the addition assignment operator. It is a shortcut for adding a value to a variable's current value.
player.x += 5
is the same as player.x = player.x + 5
-=
is the subtraction assignment operator. It is a shortcut for subtracting a value from a variable's current value.
player.x -= 5
is the same as player.x = player.x - 5
If you have a single-screen game (game world is same size as game display) that moves the player's sprite by changing its velocity (or acceleration), then move the tilesprite in the opposite direction by subtracting the player's velocity (reduced by a scaling factor).
For example, to horizontally scroll a tilesprite named skyTile
:
Dividing the player velocity by 50
represents a scaling factor, so the scrolling is not too fast. As necessary, adjust this scaling factor to a number that feels right for your game.
If your game needs to scroll the tilesprite vertically, use a similar command:
If your game has an extended game world (larger than the game display) with a game camera set to follow the player, then you can scroll the tilesprite relative to the game camera's position.
For example, to horizontally scroll a tilesprite named skyTile
:
Multiplying the camera position by -0.2
represents a scaling factor, so the scrolling is not too fast. Using a negative value makes the tilesprite scroll in the opposite direction of the camera movement. As necessary, adjust this scaling factor to a number that feels right for your game.
If your game needs to scroll the tilesprite vertically, use a similar command:
In the real world, we experience a visual phenomenon called parallax — as we move, objects in the distance seem to shift more slowly compared to objects that are closer to us. This is one of the ways that our vision system detects depth and distance.
You can simulate parallax in your game by creating a background consisting of multiple tilesprite layers and then scrolling these tilesprites at different rates based on their depth — the farthest back tilesprite layer should scroll the slowest, while the tilesprite layers that are closer should scroll faster.
In your create()
function, create a layered background by adding multiple tilesprites in the order of their "depth" from back to front. Two or three tilesprite layers will be sufficient for a parallax effect.
For an extended game world, be sure to make each tilesprite stay fixed to the camera.
For example, imagine the background of your game consists of these three tilesprites:
skyTile
is the farthest back tilesprite layer — an image showing sky and clouds
mountainTile
is the middle tilesprite layer — an image showing a mountain ridge but with a transparent sky (so you can see part of the skyTile
behind it)
cityTile
is the closest tilesprite layer — an image showing a silhouette of city buildings but with a transparent sky (so you can see part of the mountainTile
and skyTile
behind it)
Create the parallax effect by scrolling the tilesprites at different rates (by using different scaling factors) based on their relative depth.
The furthest back tilesprite layer should scroll the slowest, while the other tilesprite layers should scroll faster as they get closer to the front.
Scroll the tilesprites using one of the three options shown below.
As necessary, adjust the scrolling rates to numbers that feel right for your game.
As necessary, adjust the scaling factors to numbers that feel right for your game.
As necessary, adjust the scaling factors to numbers that feel right for your game.
The game display is the HTML <canvas>
element that Phaser adds to the webpage to display your game. Phaser also refers to this as the Stage.
The game display shows the player's view into your . The game world can be the same size as the game display (e.g., a single-screen game like Space Invaders) — or the game world can extend beyond the game display (e.g., a scrolling game like Crossy Road). For extended game worlds, the position of the determines what is shown in the game display.
Set Size of Game Display
Change Background Color of Game Display
Change Background to Random Color
You set the width and height of your game display when you first create your Phaser.Game object, which is typically done at the beginning of your JavaScript game code — before the preload()
function.
In the code above, the game display will be 800 pixels in width by 600 pixels in height. You can change these numbers for your game. However, you'll want the game display to fit within the available width and height of the player's browser window. For beginning game developers, just choose a width and height that fits your own screen.
REMINDER: Remember that 'my-game'
represents the id name of your game container <div>
in your HTML. If your CSS sets a width or height for #my-game
, then be sure to update those values to match the size of the game display in your JavaScript.
By default, Phaser uses black as the background color for your game display. However, you can change this to any color using a CSS hex string.
In most cases, you would add this command in your create()
function (probably towards the beginning — just to make it easy to find it). For example, if you only need to change the background color once, then do this in the create()
function.
However, you can also use this command in your update()
function or in a custom function. For example, you might do this if you wanted to change the background color based on certain conditions or events in the gameplay.
You can also let Phaser choose a random color for the game display background:
This reference section will help with finding and creating audio assets to use in your Phaser games.
Audio assets might include:
Sound effects
Background music
Voice-over tracks
can be used to create and save your own sound effects as WAV files.
is a game development studio that offers free audio assets for download. Downloaded assets will be WAV, MP3, or OGG format.
can be used to search, preview, and download open-source audio files.
You will need to create a free account in order to download files.
Files will vary in length, format, and size — so check before downloading.
Other websites or apps may allow you to find, create, or edit audio files.
Avoid using copyrighted audio.
Be careful when downloading files from unknown (and potentially malicious) sites.
Audio files can be WAV, OGG, or MP3 format.
Avoid using audio with large file sizes, as it will slow down your game. Most of your audio files should probably be less than 100 KB. Music files will typically be much larger (several MB).
Be sure the audio file names make it easy to identify each sound. If necessary, rename the files. (Reminder: File names should not have blank spaces in them.)
Be sure your final audio files are uploaded into the assets/sounds subfolder of your team's game folder.
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.
This reference section will help with finding and creating visual assets to use in your Phaser games.
Visual assets might include:
Background images
Spritesheets for characters
Images or spritesheets for objects and user interface elements (icons, etc.)
Tileset (if using to create your game levels)
A spritesheet is an image typically used for characters or objects that are animated.
The spritesheet is a set of smaller images called frames that have been combined into one large image. The frames are images of the character or object in a different position (or at a different point in time).
Each frame within a particular spritesheet has the same width and same height.
An animation is represented by a specific sequence of frames within the spritesheet. A spritesheet can contain one or more animation sequences.
An animation is played by displaying its sequence of frames in rapid succession. The animation can be played just once and then stopped — or the animation can play over and over in a continuous loop.
Spritesheet images must use the PNG format. PNG allows for transparent areas within an image.
This example spritesheet contains 9 frames for an animated character.
The first four frames show the animation sequence for running to the left (imagine these four frames playing over and over again in a loop).
The fifth frame shows the character standing still.
The last four frames show the the animation sequence for running to the right.
A tileset is an image used for creating tilemaps of the level(s) in your game.
A tileset is similar to a spritesheet. The tileset is a set of smaller images called tiles that have been combined into one large image. Each tile is an image of a different object or background that can be used to create the game level. The tileset is not the actual level — instead, a tilemap represents the actual layout of the level.
Each tile within a tileset has the same width and same height. Tiles are typically square (such as 32 pixels by 32 pixels, etc.).
Tileset images should use the PNG format. PNG allows for transparent areas within an image.
This example tileset contains 264 tiles (12 rows each containing 22 tiles across). Each tile is 32 pixels by 32 pixels. Some of the tiles have transparent areas.
The tilemap can also be used to identify certain properties of each tile. For example, the tilemap can identify which tiles the player should collide with (versus tiles that the player can walk through).
A tilemap can be created using an array, a CSV file, or a JSON file.
Screenshot of Tiled map editor showing a game level being created using tiles (from a tileset)
Other websites or apps may allow you to find, create, or edit image files.
Avoid using copyrighted images.
Be careful when downloading files from unknown (and potentially malicious) sites.
Image files will typically be JPG, GIF, or PNG format. Both GIF and PNG formats allow transparent areas in images.
Spritesheet images must be PNG format.
Be aware that animated GIF images will not play within a Phaser game — only the first frame of the GIF will be displayed (even if you try to use the GIF as a spritesheet and add an animation). You must use a PNG spritesheet to play animations in Phaser.
Avoid using images with large file sizes, as it could slow down your game. Most of your image files should probably be less than 50 KB. Certain images (such as: backgrounds, tilesets, texture atlases, etc.) might be larger.
If an image does not contain any transparent areas, you may be able to reduce the file size by resaving it as a compressed JPG at a lower quality (such as: 70%).
Be sure the image file names make it easy to identify each image. If necessary, rename the files. (Reminder: File names should not have blank spaces in them.)
Be sure your final image files are uploaded into the assets/images subfolder of your team's game folder.
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. (Piskel does not auto-save.)
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.
Sign in to your Piskel account (or create an account if you haven’t already done so).
Click Create Sprite button at top-right of screen. The sprite editor screen will appear, showing a single blank frame.
The default frame size for a new sprite is 32 pixels in width and 32 pixels in height (unless you've changed your default size).
To change the frame size, click Resize icon at right (looks like an arrow pulling a corner).
In "Resize" section at top, enter the desired width and height (in pixels) for your frame size. (All frames in this new sprite will use this same size.)
If your frame size is not supposed to be a perfect square, then be sure Maintain Aspect Ratio is unchecked. This will allow you to enter different numbers for the width and height.
Click Resize button to finish. Your existing blank frame will change to your new size.
To change the default size for new sprites:
Click Resize icon.
In "Default Size" section at bottom, enter the desired width and height (in pixels) for your default frame size. (All new sprites will use this default size.)
Click Set Default button to save this setting. (However, it will not change the frame size of the sprite that is currently open.)
You can import an existing image or spritesheet into Piskel, and then modify it to create a revised sprite.
Be sure the existing image or spritesheet is saved on your computer (where you can find it).
Sign in to your Piskel account (or create an account if you haven’t already done so).
Click Create Sprite button at top-right of screen. The sprite editor screen will appear, showing a single blank frame.
Click Import icon at right (looks like an open file folder).
Click Browse Images button (under “Import From Picture”).
A file browser window will appear. Navigate to where the existing image or spritesheet is saved on your computer, select the file, and click Open button.
A new pop-up called “Import and Merge” appears.
It defaults to Import as Single Image. If the file is actually just one image (one frame), simply click Next button (and go to Step 8).
If the file is a spritesheet (with multiple frames), click the radio button for Import as Spritesheet. Next enter the width and height (in that order) for the Frame Size. (In most cases, you will leave the "Offset" as zero for both the x and y directions.) Once you enter the frame size, you will see the spritesheet get divided into frames in the image preview, which helps visually confirm you’ve entered the correct numbers. Click Next button.
In the next screen, click Replace button to have your existing sprite (which is just a blank frame) replaced by the imported spritesheet. Piskel will then ask you to confirm that you want to replace your existing (blank) sprite. Click OK to proceed.
NOTE: The other option is to Merge the imported sprite with existing frames that you've already created.
The imported file should now appear in the sprite editor.
If you imported a single image, you should see a single frame.
If you imported a spritesheet, you should see all its frames.
Now you can edit the frame(s) for the sprite. Remember to save your edits as you go because Piskel does not auto-save.
When you’re done making changes, you can export your revised sprite as a PNG spritesheet, and then use it in your game.
You can export your sprite from Piskel, and then use it in your Phaser game.
Click Export icon at right (looks like a picture of mountains with the Sun).
Select tab for PNG, and then change the Spritesheet Layout Options so the number of columns equals the number of frames in the sprite. (The number of rows will automatically become 1 when you do this correctly.)
Click Download button to save the PNG spritesheet file to your computer.
Add the downloaded spritesheet file into the assets/images subfolder of your Phaser game folder.
Sketch out each of the new or modified frames for your animation sequence(s).
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. Kind in mind that all the frames in the spritesheet will end up using this same size.
Try to minimize the amount of unused transparent area in your frames.
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.
Remember to periodically save your sprite as you work.
Download your completed spritesheet by exporting it.
RECOMMENDED: After creating just one frame for a sprite, export the spritesheet, and add it to your game to double-check the frame size. Verify the sprite's size is appropriate relative to the other objects in your game. If the sprite is too small or too large, you can make adjustments in your frame size in Piskel before you create the rest of the frames. If absolutely necessary, you can use your game code to scale sprites (to make them larger or smaller), but it is better to create the sprites at the actual size you want them to be in your game (without having to scale them with Phaser).
You can add sound effects and music to your game. Sounds can be looped, faded, paused, etc.
You can only use the following formats for Phaser audio files:
.wav
.mp3
.ogg
If you find or create sound files that you want to use in your Phaser game, check the file extension to see what format the audio is saved in. If necessary, you may need to (typically .wav
or .mp3
).
RECOMMENDED: Avoid using audio with large file sizes, as it will slow down your game. Most of your audio files should probably be less than 100 KB. Music files will typically be much larger (several MB).
Load Audio
Add Audio
Set Audio to Loop
Play Audio (and Stop)
Pause Audio (and Resume)
Change Volume of Audio
Make Audio Fade
Each audio file (also referred to as an asset) must be loaded into the game's memory before you can add it to your game world.
The command to load an asset is always placed in your preload()
function.
'coin-sound'
represents an asset key — a key is sort of like a variable name. You decide what string to use for the key, as long as each audio asset has a unique key.
'assets/sounds/coin.wav'
represents the folder path and filename of the audio to load. Change this to match your specific folder path and filename.
Once an audio asset has been loaded into memory, you can add the audio to your game so it will be ready to play.
Declare a global variable for the sound, and add the sprite by assigning it to the variable. The command to add audio is placed in the create()
function.
'coin-sound'
represents the asset key of the audio to use. Replace this with your asset key.
0.3
represents the volume to use when playing this sound, which can be a value between 0 to 1 (with 1 being the file's maximum volume). Adjust this value to the volume you want.
Be aware that the sound won't actually play until you use a command telling it to start playing.
TIP: The volume to set for each sound depends on how loud the recording of the sound is — and on how loud you want it to be relative to the other sounds in the game. As you keep adding new sounds to your game, you may need to adjust their volumes relative to one another.
By default, when a sound is played, it will play once and then stop. However, you can set a sound to play over and over again in a continuous loop. You'll still be able to stop the sound if and when needed.
Looping can be useful for background music or for certain sound effects.
You would set a sound to loop by including a command in your create()
function, after adding the audio to the game.
For example, to set a sound named spinSound
to loop:
Once audio has been added to the game, it is ready to play.
For example, to play a sound named spinSound
:
Typically, commands to play sounds are placed in the update()
function or in custom functions.
However, you can also play a sound by including a command in the create()
function. This would be useful for background sounds (music, etc.) that you want playing at the start of the game.
By default, when a sound is played, it will play once and then stop.
However, if you have a long audio track (such as background music, etc.) or you have set a sound to loop, you can stop the audio whenever you need to.
For example, to stop playing a sound named spinSound
:
If you have stopped a sound, then it will start from the beginning of the audio track when you play it again.
You can also pause audio and then resume playing it.
For example, to pause a sound named gameMusic
:
Then to resume playing the sound from its pause point:
If necessary, you can change the volume of a sound during gameplay.
For example, to change the volume of a sound named gameMusic
:
0.2
represents the volume which can be a value between 0 to 1 (with 1 being the file's maximum volume).
You can play a sound and make it fade by changing its volume over a specific duration. This might be useful for background music or for certain sound effects.
Phaser has three methods that can be used for fading audio:
fadeIn()
— the sound starts playing from a volume of 0 and increases to a volume of 1 over a duration you specify
fadeOut()
— the sound starts playing from a volume of 1 and decreases to a volume of 0 over a duration you specify
fadeTo()
— the sound starts playing from its currently set volume and changes to a new volume you specify over a duration you specify
For example, to play a sound named gameMusic
and make it fade in (from volume of 0 to volume of 1):
2000
represents the duration in milliseconds for the fade (1000 ms = 1 second). Change this value to whatever duration you want.
For example, to play a sound named gameMusic
and make it fade out (from volume of 1 to volume of 0):
2000
represents the duration in milliseconds for the fade (1000 ms = 1 second). Change this value to whatever duration you want.
For example, to play a sound named gameMusic
and make it fade from its currently set volume to a new volume:
2000
represents the duration in milliseconds for the fade (1000 ms = 1 second). Change this value to whatever duration you want.
0.2
represents the new volume to fade to, which can be a value between 0 to 1 (with 1 being the file's maximum volume).
The game camera controls the player's view into the . By default, the game camera is set to the same size as your .
You can change the camera's position within the game world, or you can set the camera to automatically follow a specific sprite (such as the player's character). You can also set certain objects (images, text, etc.) to stay "fixed" within the camera view, as part of the game's user interface.
Make Camera Follow Specific Sprite
Focus Camera on Specific Object or Location
Make Object Stay Fixed to Camera View
Make Camera Shake
Make Camera Flash
Make Camera Fade
If your game world is larger than your game display, you can set the game camera to automatically follow a specific sprite (typically the player's character) as it moves through the game world.
In the code above, player
represents the name of the sprite to follow. If necessary, change this to match the variable name of your sprite.
You would typically add this command in your create()
function after adding the sprite to the game.
You also have the option of selecting a follow style with a specific type of "deadzone" — an invisible rectangular area in the middle of the game display where the camera won't move (until and unless the sprite moves outside the rectangle).
Phaser.Camera.FOLLOW_LOCKON
is the default style used if you don't select a style. It has no deadzone and will closely track the movement of the sprite.
Phaser.Camera.FOLLOW_PLATFORMER
is a style that has a tall, narrow deadzone.
Phaser.Camera.FOLLOW_TOPDOWN
is a style that has a square deadzone.
Phaser.Camera.FOLLOW_TOPDOWN_TIGHT
is a style that has a small square deadzone.
To use a different style, modify the command so it lists the follow style after the sprite's name:
You can turn off the following of a sprite using this command:
This would be helpful if you want the camera to follow a different sprite or to focus on a specific object or location.
You can make the camera move to focus on a specific display object (image, sprite, etc.) or location in the game world. This is a one-time camera movement (not a continuous following behavior). This might be helpful as visual feedback to draw the player's attention to an object or event, etc.
To focus the camera on the current location of a specific display object, use this command:
In the code above, explosion
represents the name of the display object (sprite, etc.) to focus on. Change this to match the variable name of your object.
To focus the camera on a specific location using x
and y
coordinates, use this command:
In the code above, 600
is the x
coordinate, and 300
is the y
coordinate. Changes these to match your desired coordinates.
You can make specific objects (images, text, etc.) stay fixed within the camera view, so the objects won't move when the game world scrolls. For example, a game's user interface elements (score, health bar, etc.) typically stay "fixed" to the camera view.
Phaser display objects (such as images, text, sprites, etc.) have a property called fixedToCamera
. By default, this property is set to false
— changing this value to true
will make the object stay fixed within the camera view.
In order to change this property, the display object needs to be assigned to a variable name. For example, if the player's score were displayed using a text object named scoreText
, you would use this command:
Change scoreText
to the variable name of your display object. Add the command in your create()
function after adding the display object. (The x, y
position where the object is added will be the position where it will stay fixed, relative to the top-left corner of the game camera.)
As a visual effect, you can make the game camera shake back and forth briefly (and then return to normal). This can be helpful as visual feedback for a collision, explosion, etc.
0.02
represents the intensity of the shaking (as a percentage of the camera size). Change this value as needed, but you'll probably want to use a low number (less than 0.1).
250
represents the duration of the shaking in milliseconds (1000 ms = 1 second). Change this value to whatever duration you need.
You also have the option of selecting which direction the camera should shake:
Phaser.Camera.SHAKE_BOTH
is the default direction used if you don't select a direction. It will shake the camera both horizontally and vertically.
Phaser.Camera.SHAKE_HORIZONTAL
will only shake the camera from left to right.
Phaser.Camera.SHAKE_VERTICAL
will only shake the camera up and down.
To use a specific direction, modify the command so it also lists true
plus the shake direction:
As a visual effect, you can make the game camera briefly flash a specific color (and then fade back to normal). This can be helpful as visual feedback for a reward, punishment, etc.
500
represents the duration of the flash in milliseconds (1000 ms = 1 second). Change this value to whatever duration you need.
As a visual effect, you can make the game camera fade to a specific color (black, red, white, etc.). It is basically the opposite of a camera flash (except the game camera will remain that color until you reset it). This can be helpful as visual feedback to indicate a character dying, reaching the end of a level, etc.
500
represents the duration of the fade in milliseconds (1000 ms = 1 second). Change this value to whatever duration you need.
At the end of the camera fade, the game display will remain that color (black, red, white, etc.) until you reset it back to normal using this command:
The members will be created as , so be sure to load the image or spritesheet asset for the members in the preload()
function. Each member of a group can use the same asset, or you can use different assets for different members in a group.
'body.immovable'
represents the name of the property to change. The name should be listed within quotes. Group members are sprites, so list the name of the specific you want to change.
This code creates a local variable named coinData
which is an array containing for the members of a group.
If you want to check for dead members to recycle based on a timed interval (such as every 10 seconds), then to your game to do this.
Here's an . In the example, as you move the player (using the arrow keys) through the trees, the player's "depth" relative to specific trees will change.
In addition, the section has coding references for physics methods that involve the Pointer (mouse or touch):
Here is a complete list of .
Phaser has several properties for detecting input on keys. JavaScript are used to check these input properties to see whether they are true
or false
.
Every display object (images, sprites, text, etc.) is added to the game world as a layer (using the property). These object layers are stacked on top of each other, meaning they can overlap (and hide) other objects behind them.
The lists all the style properties that can be set for text. Here's a list of the most common style properties that you might use:
font
sets the font-family. By default, it will use 'Arial'
. The font must be already be installed on the user's computer, so stick with a . If you're interested in using a special font, here's an .
fill
sets the color of the text. By default, it will use 'black'
. Set the value (as a string) using either a or a .
The third value is the color of the shadow. The color can listed either as a or as a . Colors that often work well for shadows are either '#000000'
(black) or 'rgba(0, 0, 0, 0.7)'
(slightly transparent black) — but it depends on the color of the text and the color of the background.
0xff66cc
is a code for the color. Change this to your own color by listing 0x
followed by a .
It will typically be more common that a sprite's position is changed automatically by its — e.g., its velocity will cause the sprite to move, its gravity will cause the sprite to fall, etc.
If you have a group of sprites, there is a method to getFirstDead()
sprite in the group. For more information on this method, see .
For example, here is the image created for the lives counter used in the . It shows 5 spaceship icons, which represents the maximum number of lives in this game.
While we won't cover details here, Phaser has a that can be used to scale your game display to fit the size and orientation (landscape vs. portrait) of the player's device, so your game is playable on a range of device sizes. This is a feature for advanced game developers.
In the code above, '#ffffff'
represents white — replace this with your own .
If necessary, you can convert an audio file from an unsupported format (AAC, AIFF, etc.) into WAV or MP3 format using (file conversion web app).
is a web app that allows you to create and download your own sound effects as WAV files.
A Phaser game level can be built as a grid of different tiles. A is a data file that indicates which particular tile from the tileset should be displayed in each grid square of the game level.
is a free-to-download tile map editor. You import your tileset image into the editor, create the map of your game level as a grid, and then export a JSON tilemap that can be used in your Phaser game.
is a sprite editor (available as web app) that can be used to create, edit, and save your own spritesheets.
is an image editor (available as web app or Google Drive app) that can be used to create, edit, and save images.
is a game development studio that offers free image assets for download as PNG files. Downloaded assets will include individual images, spritesheets (for animated sprites), and tilesets (for creating tilemaps).
is a website used to share, find, and download open-source game art (as well as music and sound effects).
If an image file size is large ( > 250 KB), see if the image size (width and height) is larger than what you actually need. If so, use an image editor (such as: , etc.) to resize the image down to the actual size needed, and resave the file.
is a web app that allows you to create, preview, save, and download your own animated sprites (as spritesheets). You can also import existing images or spritesheets, and modify them.
VIDEO:
It may help to first draw each frame to scale using and colored pencils.
0x00ff00
is a code for the color (green in this example). Change this to your own color by listing 0x
followed by a . For example, white would be 0xffffff
, red would be 0xff0000
, etc.
0x000000
is a code for the color (black in this example). Change this to your own color by listing 0x
followed by a . For example, white would be 0xffffff
, red would be 0xff0000
, etc.
You can use Arcade Physics to add a weapon to your game that fires bullets. The weapon is not represented visually by an actual weapon (such as a gun, etc.). The "weapon" is really just a group of "bullets" ready to be fired.
The "bullets" can be any visual object you want — bullets, laser beams, arrows, fireballs, snowballs, hearts, etc. It simply depends on the image or spritesheet that you use for the bullets.
When you fire a weapon, a bullet appears at the weapon's position and travels outward at an angle and speed determined by the weapon's properties (which you can set). When the bullet hits another object, you can perform specific actions (such as damaging or killing the object, etc.).
RECOMMENDATION: If you only think of a "weapon" as a literal weapon with bullets (such as a gun), you'll be overlooking interesting ways to use a Phaser weapon in your game. Think more broadly about objects that can be thrown, shot, or directed at a target.
In the game Angry Birds, the "bullets" are birds that you aim and shoot using a slingshot.
In a basketball game, the basketball is a "bullet" that is shot towards the goal. You can add gravity and rotation to make your basketball "bullet" act realistically.
Load Image or Spritesheet for Bullets
Add Weapon
Set Weapon Properties
Auto Fire
Fire Angle
Fire Rate
Fire Limit
Bullet Speed
Bullet Kill Type (Remove Bullet)
Bullet Kill Distance
Bullet Kill Lifespan
Bullet Gravity
Bullet Rotate to Match Direction
Make Weapon Track Sprite
Fire Weapon
Call Function When Weapon Fired Successfully
Check for Bullets Hitting Objects
Change Collision Area of Bullets
Display Visual Weapon
Properties and Methods for Arcade Physics Weapon
The bullets for a weapon can use an image or a spritesheet (e.g., for animated bullets).
The image or spritesheet file for your weapon's bullets must be loaded into the game's memory before you can add the weapon to your game.
The command to load an asset is always placed in your preload()
function.
'laser'
represents an asset key — a key is sort of like a variable name. You decide what string to use for the key, as long as each image asset has a unique key.
'assets/images/laser.png'
represents the folder path and filename of the image to load. Change this to match your specific folder path and filename.
Once the asset for the bullets has been loaded into memory, you can add the weapon to your game.
You will need to declare a global variable for the weapon and assign the weapon to the variable in your create()
function. Typically you will want to add the weapon before adding the sprite or group that will use the weapon (so that the weapon bullets will appear from behind the sprite using the weapon).
For example, to add a weapon to the variable named laser
:
5
represents the number of bullets to create for this weapon. Bullets are automatically recycled as they are fired and removed, so this represents the maximum number that could be on-screen in the game at any one time. Change this number to what you need.
'laser'
is the asset key for the bullets. Change this to match your asset key name.
NOTE: There is no visual indication that a weapon has been added to the game. When the weapon is fired, a bullet will appear and begin to travel — but otherwise, there is no actual "weapon" displayed.
This sub-section describes the most common weapon properties that you might use in your game.
These weapon properties are typically set in your create()
function after adding the weapon. However, sometimes certain properties are adjusted during gameplay (such as fire angle, etc.).
If you want a weapon to automatically fire itself (limited only by its fireRate
and fireLimit
), then set its autofire
property to true
.
For example, to set the laser
weapon to autofire:
If you don't set a weapon to autofire, then be sure to add an input that the player can use to manually fire the weapon.
You can use the fireAngle
property to set the angle (in degrees) at which bullets will be fired.
For example, to set the fire angle for the laser
weapon:
Change the angle to any value from -180 to 180. Negative values rotate the fire angle counterclockwise. Positive values rotate the fire angle clockwise.
Here are some commonly used values for the fire angle:
0
represents pointing right
-90
represents pointing up
180
represents pointing left
90
represents pointing down
If the sprite using the weapon will rotate during gameplay, you use the trackSprite()
method to automatically rotate the weapon's fire angle to match the sprite's rotation.
You can use the fireRate
property to set a limit on how frequently the weapon can be fired. The rate represents the minimum amount of time (in milliseconds) between fires.
For example, to set a fire rate for the laser
weapon:
In this example, the laser
can only be fired once every 250 milliseconds, even if the player is pressing the "fire key" faster than that.
You can use the fireLimit
property to set a limit on the total number of bullets that can be fired during the game. Once the limit is reached, the weapon won't fire any more (unless you reset it).
By default, a weapon has no fire limit (i.e., has unlimited bullets), unless you specifically change it.
For example, to set a fire limit for the laser
weapon:
The shots
property keeps track of how many total bullets have been fired from the weapon. When the value of shots
reaches the fireLimit
, the weapon cannot be fired anymore.
For example, this code would calculate the number of shots remaining by subtracting the number of shots fired from the fire limit:
Once the fire limit has been reached, you have to reset the shots
counter back to zero to allow the weapon to be fired again. Once reset, the fire limit is still in effect (so the weapon will eventually "run out" of bullets again).
You can add a function that will be called whenever a weapon's fire limit has been reached. The most common use for this is to reset the bullet count after subtracting from the player's inventory of saved bullets ("ammo packs").
For example, this code will check to see if the player has any more bullets in inventory to "load" into the weapon. If so, it will reset the weapon to fire again.
You can use the bulletSpeed
property to set the speed at which the bullets travel (in pixels per second).
For example, to set the bullet speed for the laser
weapon:
You can set the bulletKillType
property to automatically remove bullets under certain conditions. Here are the most commonly-used values for this property:
Phaser.Weapon.KILL_WORLD_BOUNDS
is the default value unless you change it. It automatically removes a bullet if it leaves the game world boundaries.
Phaser.Weapon.KILL_CAMERA_BOUNDS
automatically removes a bullet if it leaves the game camera boundaries.
Phaser.Weapon.KILL_DISTANCE
automatically removes a bullet after it has traveled a certain distance, which is determined by the bulletDistance
property.
Phaser.Weapon.KILL_LIFESPAN
automatically removes a bullet after it has traveled for a certain amount of time, which is determined by the bulletLifespan
property.
For example, to automatically remove the laser
weapon bullets if they leave the game camera:
NOTE: In addition to setting the bulletKillType
property, you will still need to manually remove bullets (using the kill()
method) when they hit other objects.
If you set the bulletKillType
property to Phaser.Weapon.KILL_DISTANCE
, then you need to set a distance (in pixels) for the bulletKillDistance
property.
For example, to automatically remove the laser
weapon bullets after they have traveled 500 pixels:
If you set the bulletKillType
property to Phaser.Weapon.KILL_LIFESPAN
, then you need to set an amount of time (in milliseconds) for the bulletLifespan
property (1000 ms = 1 second).
For example, to automatically remove the laser
weapon bullets after they have traveled for 2 seconds:
You can set the bulletGravity
property to make the bullets respond to gravity.
By default, weapon bullets will not be affected by gravity and will travel in a straight line (based on its fire angle).
You can set the bullets' gravity value for the horizontal direction (x
) and/or the vertical direction (y
). The value represents the acceleration due to gravity measured in pixels per second squared. By default, a bullet's gravity is set to zero in each direction, unless you specifically change it.
bulletGravity.x
— change this value to add gravity in the horizontal direction. Positive values will pull the bullet to the right, and negative values will pull the bullet to the left.
bulletGravity.y
— change this value to add gravity in the vertical direction. Positive values will pull the bullet downwards, and negative values will pull the bullet upwards.
Adding a bulletGravity.y
works well for "bullets" that are shot or thrown up into the air at an angle, such as arrows, a basketball, etc.
For example, to make the bullets from a bowArrow
weapon fall downward:
You can use the bulletRotateVelocity
property to make the bullets rotate to match the direction of their travel. This can create the effect of a bullet "pointing" to the path it is following (such as: an arrow being fired from a bow, etc.). This works best when you have set a bulletGravity
value.
For example, to make the bullets from a bowArrow
weapon rotate to align with the direction of their travel:
You can make the position of the weapon automatically track the position of the sprite using the weapon.
For example, to make a weapon named laser
track the position of the player
sprite:
player
represents the name of the sprite that the weapon will track. Change this to match your sprite.
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 anchor. Change these numbers as necessary to position the firing point of the weapon.
true
means that the weapon's fire angle should track the sprite's rotation. If the sprite will rotate in the game, you should list this as true
. Otherwise, list false
and adjust the fire angle using your own code.
Include this command in your create()
function after adding the weapon and the sprite to be tracked. Phaser will automatically update the weapon's position during gameplay.
For a group of enemies, it is common to create a single enemy weapon that they all can share. Then during gameplay, specific members of the group are selected to use the weapon. A specific enemy is selected from the group, and then the enemy weapon is moved to that enemy's position and fired. Then the process can be repeated.
If you want to change which sprite the weapon is tracking, then you can use the trackSprite()
method in your update()
function (or in a custom function called by the update()
function).
For example, this code could be included in your update()
function to continuously select the closest enemy to the player and make that enemy fire a weapon:
The fire()
method is used to fire a bullet from the weapon. The bullet speed, direction (fire angle), fire rate, etc. depend on the values set for the weapon's properties.
For example, to fire a weapon named laser
:
You may need to adjust the direction that a bullet will be fired by changing the fire angle based on the direction that the sprite using the weapon is moving (or was last moving).
For example, this code would move the player
right or left and also change the fire angle of the laser
weapon to match the player's direction:
As another example, this code will detect the current horizontal direction that the enemy
is moving and adjust its fire angle to match its direction before firing its weapon:
You can add a function that will be called whenever a weapon's bullet is successfully fired. The most common use for this is to play a sound effect representing the weapon firing.
For example, this will play laserSound
whenever the laser
weapon is fired:
This code would be placed in your create()
function after the code that adds the weapon.
The reason for using the onFire
signal to play the weapon sound effect is that the firing of the weapon is limited by its fireRate
(and potentially also by a fireLimit
).
If your code played the sound whenever the player pressed the "fire key", the sound would play even if a bullet was not</> actually fired (because the weapon was being limited by its fireRate
or fireLimit
). Using the onFire
signal to play the sound will keep the sound effect synced with the actual weapon firing.
You can use the Arcade Physics overlap()
method to detect when a weapon's bullet hits another object. (You can also use the collide()
method if you prefer.)
For the purposes of the overlap()
method, the weapon bullets are treated as a group referred to as weapon.bullets
(replace weapon
with the specific variable name of your weapon).
For example, to detect when one of the player's bullets hits a members of the enemyGroup
:
enemyGroup
represents the sprite or group to check for being hit by the bullets. Change this to match the name of your sprite or group.
laser.bullets
refers to the weapon's bullets. Change laser
to match the name of your weapon.
enemyHit
is the name of a custom function to run when an overlap is detected. List the function name without parentheses. You will have to create this custom function in your game. Change this to any unique function name that makes sense for your game.
null, this
are additional parameters that you need to list.
Next, you will need to create the custom function by adding it after your update()
function:
enemyHit
is the name of the custom function. Change this to match the name you listed in your collide()
command.
enemy, bullet
are the parameter variables that will be passed into the function by your collide()
command. These represent the two specific sprites that were involved in the collision. Change these to names that make sense for your collision.
The parameters are received in the same order as the original groups are listed in your overlap()
command, so the order of the names matters (in order to make your code perform the actions you intend).
The parameter names that you use will be different from the original group names. However, it will be much easier to understand what's happening in the custom function if the names are similar to the original group names.
Inside the custom function, whatever you do to the parameter variables will affect the original sprites they represent.
In this example function, enemy
represents the specific member of the enemyGroup
that was hit, and bullet
represents the specific bullet that hit the enemy
. So if the commands enemy.kill()
and bullet.kill()
were used inside the custom function, it would remove those specific members from the game world.
For example, the complete code for the enemyHit()
function might look like this:
By default, the physics body collision area for a weapon's bullet is treated as a rectangle the same width and height of the image or spritesheet frames used for the bullet.
Sometimes the image or spritesheet for a bullet includes transparent areas around the outer edges. These transparent areas are counted as part of the bullet's collision area, so it can make collisions look or feel unnatural.
If necessary, you can change the size and position of the rectangle used to detect collisions of the bullet's physics body. (This won't change the visual appearance of the bullet.)
For example, to change the collision rectangle for the bullets of a weapon named laser
:
24, 12
represent the width and height (in pixels) of the collision rectangle. Change these numbers to match the size you want to use for your bullet.
6, 6
represent the horizontal offset and vertical offset (in pixels) of the collision rectangle's top-left corner, relative to the top-left corner of the bullet. Change these numbers to position the collision rectangle where you need it to be.
There is no visual indication that a weapon has been added to the game. When the weapon is fired, a bullet will appear — but otherwise, there is no actual "weapon" displayed.
If you want to have a visual weapon displayed, then either include the weapon as part of the image or spritesheet used for the character carrying the weapon (e.g., include a weapon in the frames of the player's spritesheet) — or create a separate sprite for the weapon itself (using its own image or spritesheet).
If you do create a separate sprite for the weapon, then you would probably want to constantly update its position to match the position of the character that is carrying the weapon.
Using a separate sprite for the weapon would allow you to place the weapon in the game world for the player to find and collect — and to allow the player to change weapons (by either changing to a different sprite or by changing to a different sprite frame).
For example, let's assume that you've created a Phaser weapon named laser
which contains the bullets group. Separately, you've created a sprite named laserGun
that displays a visual weapon.
In your update()
function, you could add this code to make the laserGun
sprite follow the player
sprite's position:
If necessary, you could add (or subtract) numbers from the player.x
or player.y
positions to fine-tune the pixel position of the laserGun
relative to the player
.
Then you could set the Phaser weapon named laser
(containing the bullets) to track this sprite:
If you needed to switch the orientation of the laserGun
sprite (e.g., to point left instead of right), then you could either switch to a different sprite frame — or just flip the sprite horizontally by scaling its width to negative one:
The weapon's bullets would still be fired from laser
:
This section explains how to add other miscellaneous game features, such as a start screen, a game over screen, etc.
IMPORTANT: You should add your core game mechanics before worrying about adding any of these miscellaneous features.
Add Start Screen for Game
Add Input Signal to Pause Game (and Resume)
Add Game Over Screen
Add Input Signal to Restart Game
You can add a simple "start screen" to your game. Since your game is a single-state game, this "start screen" will simply display an image and/or text (such as the game title and maybe some information). The "start screen" will be hidden once the game is started (by the player pressing a specific key, etc.).
NOTE: If you create a Phaser game that has multiple states, you would create a separate game state just for the start of the game. Creating a multi-state game is not recommended if you're new to creating games.
You need to decide what to display at the start of the game as your "start screen":
You could display an image that will cover all (or some) of the game screen.
You could display an image of the game's title (or you could just display the title as text).
You could display some text information (such as the game's premise, some basic instructions, etc.).
You could display a combination of the above — or anything else you might want.
As necessary, find or create any image(s) that you need. If you want your "start screen" to completely cover and hide the game (until the game is actually started), then create an image that is the same width and height as your game camera view.
In this example, the "start screen" will simply display an image of the game's title and some text explaining how to start the game. The rest of the game world will be visible behind the "start screen." (Again, if you want to hide the game world at the start, create an image that will completely cover the the game camera view.)
Before your preload()
function, declare global variables for any images or text objects that will be displayed as part of your "start screen". Use any unique variable names that make sense for your game.
In the example code above, gameTitle
will be the variable for an image of the game's title, and gameStartText
will be the variable for some text explaining how the player can start the game.
If you are using any images in your "start screen", be sure to load the images in your preload()
function.
Towards the end of your create()
function (after adding all the other sprites, images, and text for your game world), add the image(s) and/or text that you want to display as your "start screen".
As necessary, change the positions to place the images and/or text where you need.
You need to decide how to prevent the normal gameplay from starting (until the game's start signal has occurred):
You could pause the game.
You could hide the player's character.
You could prevent the game's inputs from performing normal gameplay actions.
You could use a combination of the above — or another method that makes sense.
The simplest approach is to just pause the game until the player has started the game. This will "freeze" everything in the game world.
However, for certain games, you could hide the player's character until the game's start signal occurs. However, the other sprites in the game world (such as enemies, etc.) can still move around before the game's start signal has occurred. For example, in Practice 2, the Asteroids 2084 game simply hides the player's spaceship until the game is started (which occurs when the player presses the spacebar).
To pause the game, add this command in your create()
function (after the code that adds your "start screen" and the signal that will start the game):
If you aren't pausing the game, then you could hide the player's sprite until the game's start signal has occurred.
To hide the player's sprite, add this command in your create()
function (after the code that adds the player
sprite and sets its properties):
You might also need to modify some of the code in your update()
function that checks for player input, so the input will only work if player.exists
is true
. Otherwise, those inputs might perform actions that should only occur once the game has officially started.
For example, if the code in your update()
function to check the fireKey
input looked like this:
Then you might need to modify that code to include the condition that player.exists
is true
, like this:
You need to add a one-time signal that will be used to start the game:
You could start the game when the player clicks the mouse button.
You could start the game when the player presses a specific key.
You could start the game when the player clicks on a specific image or sprite.
You could set a timer to automatically start the game after a certain amount of time.
You could use another signal that makes sense for your game.
When the signal occurs, a custom function will be called that will hide the "start screen" and allow the normal gameplay to begin.
Below are several options for signals. Each option adds a signal that will call a custom function named startGame
(which you'll add to your game code in Step 4). You would add the signal in your create()
function (after adding the images and text for your "start screen").
You can designate a separate input key that will only be used to start the game — or you can use an existing input key that is normally used during gameplay for another action (e.g., to fire a weapon, to make the player jump, etc.).
In either case, before your preload()
function, be sure to declare a global variable for the input key that will be used to start the game, such as:
To add a separate input key that will only be used to start the game:
To re-use an existing input key normally used for another gameplay action (such as firing a weapon, etc.), the code is identical except the input key's variable name is its "normal" name:
You can add a signal to start the game when a specific image or sprite is clicked using the mouse (or tapped using a finger).
Before your preload()
function, be sure to declare a global variable for the image or sprite that will be used to start the game, such as:
In your preload()
function, be sure to load the image (or spritesheet).
In your create()
function, add the image (or sprite), and enable it to act as an input:
To start the game automatically when a timer ends, set the timer for the amount of time that you want your "start screen" to be displayed:
After your update()
function, you'll need to add the custom function named startGame
that will be called by the game's start signal. This custom function will hide the "start screen" and allow the normal gameplay to begin.
Inside the custom function, you will hide each image or text object displayed in your "start screen" by setting its visible
property to false
. Be sure to do this for each image or text object that should be hidden once the game is started.
The custom function will also allow the game to start by either unpausing the game or by unhiding the player sprite (depending on which option you used in Step 2).
If you paused the game in your create()
function:
If you hid the player
sprite in your create()
function:
You can pause a Phaser game by setting the game's paused
property to true
. Setting the property back to false
will resume the game.
When the game is paused, the update()
function is paused, which causes everything in your game world to be frozen in place. However, you can still detect input and run custom functions, which allows you to designate a key to resume the game again.
NOTE: If your game displays a timer using game.time.totalElapsedSeconds()
, be aware that pausing the game does not pause the game's internal clock — so even though your timer display will stop updating while the game is paused, the game time will keep elapsing in the background. Once you resume the game, the timer display will jump ahead to reflect the amount of time that elapsed while the game was paused. To avoid this issue, you'll need to figure out how to modify your timer.
Let's add a key that can be used to pause the game (and also resume the game). Each time the key is pressed, we'll toggle game.paused
between true
or false
.
Select a key that will be used as an input to pause (and resume) the game. This example will use the P
key (for "pause"). We'll add a signal that will call a custom function whenever this key is pressed down.
This example will also display text on the screen to indicate when the game is paused. However, adding this text is optional.
Before your preload()
function, declare global variables for the input key and the text (if you're using the text):
In your create()
function (towards the end), add this code:
If desired, you can leave out pauseText
— or change its properties (such as what the text displays, its position, its fill color, etc.).
Notice that whenever pauseKey
is pressed down, Phaser will run a custom function named togglePause
, which you'll need to add to your game code.
After your update()
function, add the custom function togglePause()
:
If you're not using pauseText
in your game, then remove those lines of code from the custom function.
You can add a simple "game over screen" to your game. Since your game is a single-state game, this "game over screen" will simply display text (but could also display images or other elements). The "game over screen" will be hidden at the start of the game and will only be displayed when the game detects specific conditions that indicate it is over.
In general, a game can end in two possible ways — either the player "wins" (e.g., the player completes the challenge, the player reaches the end of a level, etc.) or the player "loses" (e.g., the player's character dies, the player runs out of available time, etc.).
However, in certain games, there isn't really a way to "win" the game — instead, your game performance is measured in some way (such as: score points, achievements, progress, etc.). For example, every player will eventually "lose" Space Invaders by running out of lives; however, your Space Invaders gameplay is measured by how high your score is at the end of the game.
NOTE: If you create a Phaser game that has multiple states, you could create a separate game state just for the end of the game (or a level). Creating a multi-state game is not recommended if you're new to creating games.
You need to decide what to display at the end of the game as your "game over screen" — if necessary, you may need to display different things if the player "wins" versus "loses" the game:
You could display one or more images (e.g., the game's logo, different images for "win" versus "lose", etc.).
You could display text (e.g., a message such as "You Win" or "Try Again", instructions for how to play again, etc.).
You could display a combination of the above — or anything else you might want.
As necessary, find or create any image(s) that you want to use in your screen(s).
In this example, the "game over screen" will simply display a different image based on whether the player won or lost, as well as some text indicating whether the player won or lost.
Before your preload()
function, declare global variables for any images or text objects that will be displayed as part of your "game over screen". Use any unique variable names that make sense for your game.
In the example code above, gameWinImage
and gameLoseImage
will be the variables for the different images that will be displayed based on how the game ended, and gameOverText
will be the variable for the text indicating whether the player won or lost the game.
If you are using any images in your "game over screen", be sure to load the images in your preload()
function.
Towards the end of your create()
function (after adding all the other sprites, images, and text for your game world), add the image(s) and/or text that you want to display as your "game over screen".
As necessary, change the positions to place the images and/or text where you need.
The "game over screen" needs to be hidden when the game starts.
In your create()
function (immediately after adding the elements for your "game over screen"), hide each element in your "game over screen" by setting its visible
property to false
:
You need to detect the specific conditions that cause the game to be over. If your game can end in multiple ways (such as "winning" or "losing"), then you'll need to detect each of these possible conditions.
For example, a side-scrolling game could be designed to be "won" if the player reaches the end of the level — or "lost" if the player runs out of lives (or time).
When a condition occurs that causes the game to end, you will call a custom function named gameOver()
to display the "game over screen".
Some common examples of "lose" conditions and "win" conditions will be provided here. You can mix and match these with each other — and with your own conditions — based on what's needed for your game design.
If your game could end with the player either "winning" or "losing" the game, then it will help to have a variable to track the game's final result.
Before your preload()
function, declare a global variable to represent whether or not the player has won the game. Assign an initial value of false
to the variable (because the game hasn't been won yet at the start).
Later, if the player does win the game, the variable will be changed to true
.
In some games, the player has only one life: if the player's character dies, the game is over.
In this example code, when the player is killed, the game will call a custom function named gameOver()
:
The example code above would be placed in the create()
function after the code that adds the player
sprite.
In some games, the player can have multiple lives: when the player's character dies, the game continues if the player has any lives remaining — otherwise, the game is over.
In this example code, when the player is killed, the game will check to see if the player has any lives remaining. If so, the player is reset back into the game world with full health. Otherwise (when the player is out of lives), a custom function named gameOver()
is called:
The example code above would be placed in the create()
function after the code that adds the player
sprite.
In some games, there is a limited amount of time for the player to complete the game's level.
You can create a countdown timer for your game that displays the amount of time remaining (based on a time limit that you specify).
In this example code, if time limit is not over, it displays the time remaining. Otherwise (when the time runs out), a custom function named gameOver()
is called:
The example code above would be placed in the update()
function. Be sure to add the other code necessary to create the countdown timer.
In some games, the player wins by reaching the end of the level. A simple way to detect this is to compare the position of the player against a specific position (x
and/or y
) in the game world.
For example, this code will detect when the player
sprite has reached an x
position of 4950 pixels (which might represent the "end" of a game level that is 5000 pixels in width):
The example code above would be placed in the update()
function.
In some games, the player wins by reaching a specific object in the game world (e.g., a treasure, an exit door, another character, etc.). A simple way to detect this is to check for overlap between the player
sprite and the object's sprite.
For example, in your update()
function, check for the overlap of the player and the target object (the lostCat
sprite in this example):
Then after your update()
function, add the custom function called by the overlap method:
In some games, the player wins by defeating a specific enemy, such as a boss enemy.
In this example code, when the bossEnemy
is killed, the game will call a custom function named gameOver()
:
The example code above would be placed in the create()
function after the code that adds the bossEnemy
sprite.
In some games, the player wins by defeating all the enemies in a group.
In this example code, when there are no more living members left in the enemyGroup
, the game will call a custom function named gameOver()
:
The example code above would be placed in the update()
function.
In some games, the player wins by collecting all the objects in a group (such as a group of coins, etc.).
In this example code, when there are no more living members left in the coinGroup
, the game will call a custom function named gameOver()
:
The example code above would be placed in the update()
function.
After your update()
function, you'll need to add the custom function named gameOver()
that will be called when a "game over" condition is detected. The custom function will show the "game over screen" (and might need to cause the normal gameplay to end).
Inside the custom function, you will show each image or text object displayed in your "game over screen" by setting its visible
property to true
. Be sure to do this for each image or text object that should be shown once the game ends. You can also add other code to change the text, play a sound, etc.
NOTE: You may want (or need) to add other code to prevent the normal gameplay from continuing once the game is supposed to be over. Here are two simple options (but there are other possible options):
Pause the gameplay by using: game.paused = true;
Remove the player by using: player.exists = false;
You can add an input signal to allow the player to restart the game (i.e., start a new game) when the current game is over. Often this is used in combination with displaying a "game over screen".
A Phaser game can be restarted by using:
The instructions below will show how to embed the "restart" command within a custom function that is called by a signal within your game.
In your gameOver()
function, you would add a one-time signal that will restart the game:
You could restart the game when the player clicks the mouse button.
You could restart the game when the player presses a specific key.
You could restart the game when the player clicks on a specific image or sprite.
You could set a timer to automatically restart the game after a certain amount of time.
You could use another signal (or combination of signals) that makes sense for your game (e.g., restart when specific enemy defeated, etc.).
When the signal occurs, a custom function will be called that will restart the game.
Below are several options for signals. Each option adds a signal that will call a custom function named restartGame
(which you'll add to your game code in Step 2).
In your gameOver()
function, add the signal to restart the game:
You can designate a separate input key that will only be used to start the game — or you can use an existing input key that is normally used during gameplay for another action (e.g., to fire a weapon, to make the player jump, etc.).
In either case, before your preload()
function, be sure to declare a global variable for the input key that will be used to start the game, such as:
To add a separate input key that will only be used to start or restart the game, first add the input key in your create()
function:
Then add the signal in your gameOver()
function:
To re-use an existing input key normally used for another gameplay action (such as firing a weapon, etc.), first, add the input key in your create()
function:
Then add the signal in your gameOver()
function:
To add a dedicated key that can restart the game at any point during gameplay, use a separate input key that's not used for any other purpose, change the signal from addOnce()
(one-time signal) to add()
(repeatable signal), and add the signal in your create()
function (instead of in the gameOver()
function):
You can add a signal to restart the game when a specific image or sprite is clicked using the mouse (or tapped using a finger).
Before your preload()
function, be sure to declare a global variable for the image or sprite that will be used to start the game, such as:
In your preload()
function, be sure to load the image (or spritesheet).
In your create()
function, add the image (or sprite, enable it to act as an input, and hide the image (until the game is over):
In your gameOver()
function, show the image, and add the signal to it:
To restart the game automatically after a timer delay, set a timer for the amount of time that you want your "game over screen" to be displayed.
In your gameOver()
function, add the timer:
After your update()
function, you'll need to add the custom function named restartGame
that will be called by the signal you added. This custom function will use the game.state.restart()
method to restart the current game.
Restarting the game skips the preload()
function (because all the game assets should already be loaded into memory from the previous game). Instead, it first clears out the current game world. Then it runs the create()
function again one-time (to rebuild a fresh game world). After that, the update()
function begins to runs in a continuous loop for the new gameplay.
IMPORTANT: If you have any custom game variables (e.g., to store the player's score, etc.), those variables will keep the same values they had at the end of the previous game. For some variables, that's perfectly fine. However, for other variables (such as the player's current score, etc.), you may need to reset the values of those variables for the new game before restarting the game. If desired, you can also change the values of certain variables (such as updating the game's high score, increasing the enemy count for the next game, etc.).
Inside the custom function, change or reset the values of certain game variables (as necessary) for the start of a new game. Then restart the game:
You can add certain types of animation effects using a tween. A tween changes a game object's property from one value to another value over a specified amount of time. Phaser figures out all the "in between" values to make the change occur smoothly over the time duration. Tweens can be used for objects such as text, images, sprites, etc.
For example, a tween could be as simple as making the game's title fade in (or fade out). However, a tween can also be more complex. You can use a tween to change multiple properties of an object at the same time, such as moving a sprite while also making it smaller and making it fade out. You can also chain multiple tweens together (for the same object or multiple objects) to have them play in sequence.
Here's an example of a tween animation effect that changes the values of the y
and angle
properties for a set of sprites (each sprite is an image of a letter). The tweens use delays to start playing in a staggered fashion from left to right.
Object Properties Used in Tweens
Add Tween to Change Object Property
To Tween (Current Value → Different Value)
From Tween (Different Value → Current Value)
Deciding Which Type of Tween to Use
Easing Patterns for Tween Effects
Examples of Tweens
Tween Alpha (Make Object Fade In or Fade Out)
Tween Angle (Make Object Rotate)
Tween Scale (Make Object Larger or Smaller)
Tween Position (Move Object Horizontally or Vertically)
Tween Multiple Properties of Same Object at Same Time
Chain Tweens Together in Sequence
Control Playing of Tween (Start, Pause, Resume, Stop)
Detect When Tween Completed
Properties and Methods for Tween Classes and Methods for Phaser Easing Patterns
The most commonly-used object properties used in tweens include:
alpha
(make object fade in or fade out using transparency)
angle
(make object rotate)
scale
(make object larger or smaller)
x
position (make object move left or right)
y
position (make object move up or down)
However, any object property can used in tweens, as long as the property can have a range of numeric values.
You can add a tween in your create()
function, update()
function, or in a custom function. Tweens can be set to start automatically or can be started manually (which requires assigning them to a variable).
There are two types of tweens:
To Tween — changes property from its current value to a new value
From Tween — changes property from a specified value to its current value
In order to apply a tween to a game object (such as: text, image, sprite, etc.), the object must be assigned to a variable (so you can reference the object's variable name in the tween).
If you want to automatically play the tween, you can add the tween directly (without assigning the tween to a variable).
However, if you want to manually control the playing of the tween (start, pause, etc.), you must add the tween by assigning it to a variable.
A "to" tween changes an object's property (or set of properties) from its current value to a different value over a designated amount of time.
Here is the generic format for a "to" tween:
target
represents the variable name of the object whose property will be changed.
{ property: value }
represents the name of the object's property and its new value.
Any property that can have a range of numeric values can be tweened.
The value can be absolute (i.e., a specific number such as 300
) or relative (i.e., a change such as '+300'
or '-300'
). Relative values need to be listed inside quotes.
If needed, you can change multiple properties by separating property-value pairs with a comma: { property: value, property: value }
duration
represents the amount of time (in milliseconds) over which the property's value should change from its current value to the new value. If no value is listed, it will default to 0
(no delay).
easing
represents the name of a mathematical pattern that determines how the property's value will be changed over time. If no value is listed, it will default to linear easing (steady change).
autostart
determines whether the tween will automatically start. List true
to automatically start the tween, or list false
to manually start the tween. Tweens that will be manually started must be assigned to a variable. If no value is listed, it will default to false
(manual start).
delay
represents the delay (in milliseconds) before the tween will start. If no value is listed, it will default to 0
(no delay).
repeat
represents the number of times that the tween should repeat itself. List the number of times to repeat, or list -1
to make tween repeat itself continuously. If no value is listed, the default is 0
(play tween once but don't repeat).
yoyo
determines whether the tween will reverse itself. List true
to make the tween play and then reverse itself (play backwards — returning to the starting value). If no value is listed, the default is false
(play tween once but don't reverse).
Only the target
name and the property: value
pair are required parameters. The other parameters are optional and will use their defaults if omitted.
However, it is recommended to list values for duration
, easing
, autostart
, and delay
. This will make it easier for you to fine-tune the tween animation effect.
A "from" tween changes an object's property (or set of properties) from a different value to its current value over designated amount of time.
Here is the generic format for a "from" tween:
The parameters are the same as a "to" tween (see above for descriptions).
How do you decide whether to use a "to" tween versus a "from" tween?
The short answer: It may not matter. You can most likely use either type of tween to achieve the same effect.
The longer answer: It depends. It might be slightly easier to use one versus the other. Just use whichever one makes more sense to you (or for your game).
Possible reasons to use a "to" tween:
You want the current value of the object's property to be different when the tween effect is completed. (The tween will start from the current value and change to a different value.)
You have a specific ending value in mind for the property.
Possible reasons to use a "from" tween:
You want the current value of the object's property to be the same when the tween effect is completed. (The tween will start from a different value and change to the current value.)
You have a specific starting value in mind for the property.
Again, in most cases, you could probably use either type of tween. Your code will just be slightly different.
Phaser has different "easing patterns" that determine how the values of the property are mathematically changed from its starting value to its ending value.
The default easing pattern is to change the value in a linear pattern (i.e., steady change over time). In many cases, a linear pattern will work just fine for a tween effect.
However, in other cases, you may want to use a different easing pattern to achieve a more realistic (or interesting) animation effect for the tween. For example, a cubic pattern is often used for changes that naturally speed up or slow down.
Here are some of the more commonly-used Phaser easing patterns (not a complete list):
Phaser.Easing.Default
— the same as Phaser.Easing.Linear.None
Phaser.Easing.Linear.None
— value changes at steady rate
Phaser.Easing.Cubic.In
— value changes slowly at first and then speeds up
Phaser.Easing.Cubic.Out
— value changes rapidly at first and then slows down
Phaser.Easing.Cubic.InOut
— value changes slowly at first, then speeds up, and then slows down again
Some of the other classes of Phaser easing patterns include: back easing, bounce easing, circular easing, elastic easing, sinusoidal easing, etc.
VISUAL REFERENCE: Here are graphs that visually represent the different Phaser Easing patterns.
The black line in each graph shows how the property's value will change over time during the tween. (Time progresses from left to right across the graph.)
The red lines represent the starting and ending values for the property. (The bottom red line represents the starting value. The top red line represents the ending value. The graph shows the value increasing over time, but the pattern still applies if the value is decreasing over time — just imagine flipping the graph vertically.)
PowerX easing patterns are no longer included in Phaser.
Exponential easing patterns are not shown, but they would look similar to the quintic easing patterns.
For example, to fade in the gameTitle
from a value of 0
(transparent) to its current value (which is 1
unless you previously set it to something else):
As another example, to fade out the gameTitle
from its current value (assume 1
) to a new value of 0
(completely transparent):
For example, to make the gameTitle
rotate from a value of 360
degrees to its current value (which is 0
unless you previously set it to something else):
This will rotate the gameTitle
360 degrees counter-clockwise. To make it rotate clockwise, use -360
as the "from" value.
Scale is a property that has multiple components:
scale.x
(horizontal scale)
scale.y
(vertical scale)
Because this property has multiple components, you must use object.scale
as the target and { x: value, y: value }
for the property-value pairs.
This also means that you cannot combine scale and another property into the same tween. However, if you want to tween an object's scale and another property at the same time, just create separate tweens that play at the same time.
For example, to scale gameTitle
from a value of 4
to its current value (which is 1
unless you previously set it to something else):
To scale gameTitle
while also making it fade in at the same time, just add separate tweens (one directly after the other):
As another example, to scale the gameTitle
from its current value to a new value of 0
(i.e., make it shrink until it disappears):
An object's position is determined its x
and y
properties. You can move an object by tweening its x
and/or y
positions.
NOTE: Since x
and y
are actually two separate properties (rather than components of the same property), you can change both x
and y
positions with the same tween, if needed.
NOTE: If the object is a sprite that has gravity (gravity.x
or gravity.y
), its gravity will also affect the sprite as you move it with a tween.
For example, to move the gameTitle
to position 300, 20
:
If desired, you can use negative values for position in order to move an object off-screen (or move the object from off-screen to on-screen).
For example, to move the gameTitle
vertically from a position off-screen (y: -100
) down to its current y
position:
Remember that values can be absolute (i.e., a specific number such as 300
) or relative (i.e., a change such as '+300'
or '-300'
). Relative values need to be listed inside quotes.
For example, to move the bossEnemy
horizontally from its current position to a new position 300 pixels to the right:
To move the bossEnemy
horizontally to the left use a negative relative value (such as: '-300'
).
To make the bossEnemy
repeatedly move horizontally to the left by 300 pixels and back again to its original position, set the tween to repeat continuously and to reverse itself in a yoyo:
You can tween multiple properties of the same object at the same time by adding multiple tweens and/or listing multiple property-value pairs in the same tween.
For example, these two tweens for gameTitle
are added back to back and thus will start at the same time:
Together, these two tweens will make the gameTitle
become larger (scale from 0 to 1), fade in (from 0 to 1), rotate clockwise (from -360 to 0), and move down (from 150 pixels above its current y
position). All four of these effects will start at the same time and occur over the same duration of time.
If you assign a tween to a variable and set the tween's autostart parameter to false
, then you can control the playing of the tween. You will be able to start, pause, resume, and/or stop the tween whenever you want. You'll also be able to replay (reuse) a tween if needed.
First, add the tween by assigning it to a variable. Use any unique variable name that make sense for your game.
Then when needed in your game, you can use any of the methods to start, pause, resume, and/or stop the tween.
NOTE: If the tween is being added in one function (such as the create()
function) but will be controlled in a different function (such as the update()
function or a custom function), then the tween should be assigned to a global variable. (Otherwise, if the tween is added and controlled within the same function, a local variable can be used.)
You can chain multiple tweens together, so they play in a sequence — when the first tween ends, the next tween will start, etc.
One of the tweens will represent the start of the chain. The other tweens will represent additional "links" in the chain.
First, add the individual tweens by assigning them to variables. Use any unique variable names that make sense for your game.
Then use the chain()
method to "link" the tweens in order (which represents the order in which they will play).
NOTE: If the chain is being created in one function (such as the create()
function) but will be started in a different function (such as the update()
function or a custom function), then the first tween in the chain should be assigned to a global variable (but the other tweens in the chain can just use local variables if the tweens and chain are created within one function).
NOTE: If you want multiple tweens to play at the same time (in parallel instead of in sequence), then do not chain them together (just add them one after another).
You can add an onComplete
signal to call a custom function when a particular tween has completed.
First, add the tween by assigning it to a global variable. Use any unique variable name that make sense for your game. Then add an onComplete
signal to specify the name of the custom function to call. Use any unique function name that make sense for your game.
Finally, add the custom function after your update()
function. Be sure to use the same name that you listed in the onComplete
signal.
You can use Phaser code to develop enemy behaviors that will give the enemies "artificial intelligence", so they respond appropriately to certain conditions or events during gameplay. Many games actually use very simple rules to create enemy behaviors that seem "intelligent" to the player.
A very simple (but still useful) model of behavior is: STIMULUS → RESPONSE
You should decide on a set of a few simple rules that will guide how your enemies should behave. As necessary, restate each rule as a more specific "stimulus-response" statement (which will act like pseudocode). What are the specific conditions (stimulus) that will lead to a specific enemy behavior (response)? The final step is try to translate this into actual code. Hint: Phaser Arcade Physics has properties and methods that are useful when coding enemy behaviors.
Here's an example of how to translate a general rule for enemy behavior into "stimulus-response" pseudocode and then into Phaser code:
Rule for Enemy Behavior
If the player gets close enough to an enemy, the enemy will follow the player.
Restated as "Stimulus-Response" Pseudocode
If the enemy and the player are within a certain distance of each other AND the enemy is moving away from the player (or the enemy is not currently moving), make the enemy move towards the player.
Translated into Possible Phaser Code
This reference section will supply some examples of common enemy behaviors used in games. However, you may be able to develop other behaviors using your own code (or by finding other code examples elsewhere online).
Make Enemies Bounce Off Obstacles, Each Other, or the Player
Make Enemies Patrol Back and Forth on Platforms
Make Enemies Patrol Back and Forth using Step Counter
Make Enemies Move Towards Player
Make Enemies Fire Weapon at Player
You can make enemies bounce off obstacles, each other, and/or the player. To do this, you need to set the bounce
property for the enemies, and then check for collisions (or overlap) between the enemies and the other sprites.
For example, if the enemies move horizontally (left or right), then set their body.bounce.x
property to a value of 1
when adding each enemy in your create()
function:
Using a value of 1
will ensure the enemy will not lose any velocity when it collides with an object and reverses direction. (If you want the enemy to slow down after each collision, then use a value less than 1.)
In your create()
function, be sure to set each member of the obstacle group to "immovable":
In your update()
function, add a collide()
method between the enemy group and the obstacle group:
In your update()
function, add a collide()
method between the members of the enemy group:
In your update()
function, add an overlap()
method between the player and the enemy group to run a custom function (called touchEnemy
in this example):
Then add the custom function after your update()
function. Inside the custom function, reverse the enemy's direction by multiplying its velocity by -1
.
You could add other code inside the custom function, such as damaging the player's health, etc.
You can make a group of enemies patrol back and forth on platforms. If an enemy is about to move off the edge of a platform, then the enemy should reverse its direction. When the enemy reaches the opposite platform edge, the enemy will reverse again — patrolling back and forth on the platform without falling off.
In order to accomplish this, you need to have your collide()
method between the enemies and platforms run a custom function (which will check whether or not each enemy needs to reverse its direction).
In your update()
function, your collide()
method might look like this:
Then you'll need to add the custom function patrolPlatform()
after your update()
function.
The custom function will check the enemy's current direction of movement (right vs. left) and also check whether the enemy has started to move over the edge of the platform (by comparing the edge positions of the enemy and platform). If the enemy is moving off the edge of the platform, the enemy's direction will be reversed (by multiplying its velocity by -1).
You can make a group of enemies patrol back and forth (whether they are on a platform or not) simply by counting the number of "steps" they take and reversing each enemy when they reach a certain number (the step limit).
Before your preload()
function, declare a global variable named stepLimit
, and assign it a value representing the number of "steps" that each enemy can take before reversing direction. Each step actually represents one game loop (as a general rule of thumb, about 50-60 game loops occur in every 1 second of game time).
When adding each enemy in your create()
function, give the enemy a starting velocity. You can either give each enemy the same velocity or give each enemy a random velocity within a specified range.
For example, this command would give each enemy a random horizontal velocity between 125 and 175 and also randomly select either a positive velocity (moving to right) or negative velocity (moving to left):
When adding each enemy, you also need to give them a new property which we'll call currentStep
. This is not a built-in Phaser property. However, you can add new properties to an existing JavaScript object (including a Phaser object) just by assigning a value to the property. If the object doesn't already have a property with this name, then JavaScript will automatically add the property to the object.
Rather than starting each enemy at a step count of zero, let's make each enemy's starting stepCount
somewhere between 0 and the stepLimit
:
Giving each enemy a random starting point for its stepCount
will help prevent the enemies from all reversing direction at the exact same moment in the game. (If you do want the enemies to move in unison with each other, instead just set them to the same stepCount
at the start.)
Then in your update()
function, you need to increase each enemy's stepCount
with each game loop (i.e., each time the update()
function runs). If an enemy reaches the stepLimit
, then reverse the enemy's direction by multiplying its velocity by -1
and reset its step counter:
You can make the enemies in your game move towards the player by detecting where the player is located relative to an enemy and then changing the direction of the enemy's movement, so the enemy will move towards the player.
Provided below are examples for a side-view game and for a top-down game.
Let's assume you have a side-view game with platforms where the enemies can move either left or right.
Let's also assume that an enemy will only change direction to move towards the player if the player is within a certain distance AND the player and enemy are possibly on the same platform (i.e., their "bottom" positions are the same).
For each enemy, you would first need to check whether its bottom position matches the player AND the player is within a specified distance. If that's true, then next you need to detect whether the player is currently located to the left or the right of the enemy (by comparing their x
positions) AND whether the enemy is moving away from the player (by checking whether its velocity is positive or negative, which tells you direction). Then when necessary, you'd change the enemy's direction to move towards the player.
Here is example code that could be included in your update()
function:
You could revise the code above however you need for your game. For example:
You could change the distance from 400 pixels to something else.
You could add other code to change the enemy animation, make the enemy fire a weapon, etc.
If you don't want to worry about the player and enemy being on the same platform (or your game doesn't have platforms), you could remove the condition that compares the bottom
positions.
If you don't want to worry about a minimum distance between the player and enemy, you could remove the condition that checks the distanceBetween()
— or you could even remove the entire outer if
statement.
If your enemies are stationary and should only start to move when the player gets close, then remove the condition that checks whether the enemy.body.velocity.x
is greater than or less than zero. (Then be sure to directly set the enemy velocity instead of multiplying it by -1.)
If your game allows enemies to move up and down, then compare the y
positions of the player and enemy, in order to decide whether the enemy should move up or down.
etc.
Let's assume you have a top-down game where the player and enemy can rotate and move in any direction (i.e., at any angle).
Let's also assume that an enemy will only change direction to move towards the player if the player is within a certain distance.
For each enemy, you would first need to check whether the player is within a specified distance. If that's true, then next you can rotate the enemy to face towards the player (but that's optional, depending on your game). Then you'd move the enemy towards the player.
Here is example code that could be included in your update()
function:
You could revise the code above however you need for your game. For example:
You could change the distance from 400 pixels to something else.
You could change the enemy velocity from 150 pixels/second to something else.
You could add other code to make the enemy fire a weapon, etc.
The enemies in a group can share a single enemy weapon, rather than needing to create a separate weapon for each enemy.
In your create()
function, add a weapon for the enemies to share, set any necessary weapon properties (such as: fireRate
, bulletSpeed
, etc.), and add a function to play a sound effect when the weapon fires:
In your update()
function, you need to select a specific enemy to use the weapon, move the weapon to that enemy's position, adjust the weapon's fire angle (if necessary), and then fire the weapon:
The code shown above is "generic" code. See below for more details on how to select an enemy, set the fire angle, etc.
You need to decide on a rule for which enemy will fire the weapon: a random enemy, the closest enemy, all enemies within a certain distance, etc. Once the specific enemy has been selected, you give the weapon to that enemy using the trackSprite()
method.
To select a random enemy from the group, and give it the weapon:
To select the closest enemy to the player, and give it the weapon:
To select an enemy within a certain distance of the player, and give it the weapon:
NOTE: If you use the forEachAlive()
function to select nearby enemies to fire the weapon, be aware that the enemy weapon's fireRate
(as well as the number of bullets in the enemy's weapon) will limit how many frequently the weapon is actually fired. The result is that even though multiple enemies might be close to the player, typically just one enemy at a time will fire the weapon (and that same enemy tends to keep firing until it is no longer close to the player).
For certain games, the enemies always fire in the same direction. For example, in Space Invaders, the aliens always fire straight down. If this is how your game works, then you can set the enemy weapon fire angle in the create()
function — and you won't need to change it during gameplay.
However, in other games, the enemies need to adjust the enemy weapon fire angle during gameplay, in order to fire at the player.
Let's assume you have a side-view game where the enemies can fire the weapon either left or right in the direction of the player. You would need to determine whether the player is currently located to the left or the right of the enemy. You could do this simply by comparing their x
positions. Then you would set the enemy weapon fire angle to the point in the direction of the player.
To make the enemy weapon fire to the left:
To make the enemy weapon fire to the right:
If needed, you can set the enemy weapon to other angles (e.g., to fire up or down, to fire at a non-perpendicular angle, etc.)
If the enemy has been rotated to face towards the player, then make the enemy weapon fire in the same direction of the enemy's angle:
Otherwise if your game does not rotate the enemy to face the player, then get the angle between the enemy and player, and make the enemy weapon fire at this angle:
If you wanted to add small margin of error to the angle (so the enemy doesn't always have perfect aim), add this extra line of code after setting the "perfect" angle (but before firing the weapon):
5
represents the maximum amount of error (in degrees). In this case, it would add a random error between -5 to +5 degrees (including the possibility of 0 degrees — i.e., no error). Change this number to the maximum amount of error you want.
You can use Arcade Physics to add a particle emitter that can release particles for visual effects. Particle emitters can be used for one-time effects (such as explosions, etc.) or continuous effects (such as rain, snow, etc.).
The "particle emitter" is really just a group of "particles" ready to be emitted (similar to how a Phaser weapon is just a group of bullet objects). The particles can be emitted from a single point or from an extended area.
By default, particles are emitted in random directions with slightly different speeds and rotations. As the particles fan out, they are pulled downward by gravity until they are removed after two seconds. However, you can customize the emitter to change how the particles are emitted (speed, rotation, gravity, lifespan, etc.).
Load Image or Spritesheet for Particles
Add Emitter
Add Particles to Emitter
Set Particle Emitter Properties
Change Emitter Position
Set Emitter Size
Make Emitter Stay Fixed to Camera
Set Particle Speed
Set Particle Rotation
Set Particle Gravity
Set Particle Lifespan
Set Particle Transparency (Fade In or Out)
Set Particle Scale (Change Size)
Emit Single Particle
Emit One-Time Burst of Particles
Emit Continuous Flow of Particles
Make Particles Collide
The particles for a particle emitter can use an image or a spritesheet (e.g., for animated particles).
The image or spritesheet file for your particles must be loaded into the game's memory before you can add the particle emitter to your game.
The command to load an asset is always placed in your preload()
function.
'particle'
represents an asset key — a key is sort of like a variable name. You decide what string to use for the key, as long as each image asset has a unique key.
'assets/images/asteroid-piece.png'
represents the folder path and filename of the image to load. Change this to match your specific folder path and filename.
Once the asset for the particles has been loaded into memory, you can add the particle emitter to your game.
You will need to declare a global variable for the particle emitter and assign the particle emitter to the variable in your create()
function.
For example, to add a particle emitter to a variable named asteroidEmitter
:
0, 0
represent the center x
and y
position where the particles will be emitted from. For one-time effects (such as explosions, etc.), you will typically change the position later during gameplay. For continuous effects (such as rain, etc.), you might already know the specific position to use. Later, you can also change the size of the emitter to release particles from an extended area (useful for effects like rain, etc.). If necessary, change this position for your game.
50
represents the number of particles for this particle emitter. Particles are automatically recycled as they are emitted and removed, so this represents the maximum number that could be on-screen in the game at any one time. Change this number to what you need.
Even if the emitter is only intended for one-time bursts of particles, you may need to create enough particles for multiple bursts being on-screen at the same time, depending on on how frequently the bursts may occur.
As another example, to add a rainEmitter
that will be positioned at the top-center of the game display (and have 400 "rain" particles):
NOTE: There is no visual indication that a particle emitter has been added to the game — until you instruct it to release particles.
Once the particle emitter has been added, you need to actually add the particles to the emitter by using the image or spritesheet asset for the particles.
For example, to add the particles to the asteroidEmitter
emitter:
'particle'
is the asset key for the particles. Change this to match your asset key name.
NOTE: There is no visual indication that the particles have been added to the emitter — until you instruct it to release particles.
This sub-section describes the most common particle emitter properties that you might change in your game.
These particle emitter properties are typically set in your create()
function after adding the particle emitter. However, sometimes certain properties are adjusted during gameplay (such as position, etc.).
You can change the emitter position during gameplay by setting new values for the emitter's x
and y
coordinates. This can be done in the update()
function or in a custom function.
Changing the position of an emitter is typically done when the emitter is used for one-time bursts of particles (e.g., creating a particle explosion when an object is destroyed, etc.).
For example, to set the emitter to a specific position using numbers:
You can also set the emitter position by referencing the position of another object, such as a sprite.
For example, an emitter named asteroidEmitter
is used to create a one-time burst of asteroid particles whenever a specific asteroid
is destroyed. The emitter is set to the asteroid's position before removing the asteroid and creating the particle explosion:
By default, a particle emitter will emit particles from a single point defined by its x
and y
coordinates.
However, you can set the size of the emitter to emit particles from an extended area. For example, you could emit "rain" particles from across the entire width of the game display.
To set the size of the emitter, just specify values for its width and height (in pixels).
For example, to set rainEmitter
to the width of the game display (but a height of just 1 pixel):
If you have an extended game world, you can make the particle emitter stay fixed to the camera view. This is typically done for emitters that produce continuous weather effects (rain, snow, etc.).
First, set the emitter's fixedToCamera
property to true
. Next, set the center x
position of the emitter using the cameraOffset.x
property.
NOTE: For some reason, the original emitter.x
center position gets set to 0
when emitter.fixedToCamera
is set to true
. Setting cameraOffset.x
seems to be the only way to shift the emitter position. If needed, you can also set a value for cameraOffset.y
.
For example, to set rainEmitter
to be fixed to the camera and centered horizontally within the game display (assume that its y
value was set to 0
when the emitter was added):
By default, particles are emitted outward at random speeds (though relatively slow) and random directions. If needed, you can specify a range of speeds for the x
direction and/or y
direction. Each particle will receive a random speed within your specified range.
This can be used not only to change how fast or slow the particles move — but also which direction they move (e.g., you could make "rain" particles only move downward, etc.)
To set a range of speeds for the x
direction (horizontal), provide values for the minimum and maximum values (in pixels per second):
min
and max
can be any values you want. Negative values for the x
speed make particles move to the left, and positive values make particles move to the right.
To set a range of speeds for the y
direction (vertical), provide values for the the minimum and maximum values (in pixels per second): :
min
and max
can be any values you want. Negative values for the y
speed make particles move upwards, and positive values make particles move downwards.
For example, here are particle speed ranges for a rainEmitter
:
The x
speed range has been set to allow the rain particles to move left (negative) or right (positive) but only at a very slow speed (5 or less).
The y
speed range has been set to only allow the rain particles to move down (positive values) but at fairly fast speed (between 300-500 pixels per second).
By default, particles are emitted with a slow random rotation speed (angular velocity). If needed, you can specify a range for the angular velocity that controls the rotation. Each particle will receive a random rotation speed within your specified range. You can also turn off the rotation if desired.
To set a range of speeds for the rotation (angular velocity), provide values for the the minimum and maximum values (in degrees per second):
min
and max
can be any values you want. Negative values for rotation make particles rotate counter-clockwise, and positive values make particles rotate clockwise.
For certain particle emitters, you may not want the particles to rotate. For example, you may want "rain" particles to simply fall down without rotating.
To prevent particles from rotating, set the rotation range to zero:
By default, particles have a small amount of gravity in the vertical direction that pulls them downward. If desired, you can change the gravity values for the x
and/or y
directions. You can also turn off the particle's gravity.
This can be used make the particles respond more realistically to your game's simulated environment — but it also could be used as another way (besides particle speed range) to direct particles (e.g., by pulling them in a specific direction).
For certain games (e.g., game using top-down perspective, game set in outer space, etc.), you might not want the particles to be affected by gravity.
By default, the emitter's gravity.x
value is set to 0
(no gravity).
To set a different gravity value for the x
direction (horizontal), provide a value (in pixels per second squared):
50
represents the gravity value (in pixels per second squared). Change this to the value you need for your particle effect. Negative values for gravity.x
pull particles to the left, and positive values pull particles to the right.
By default, the emitter's gravity.x
value is set to 100
.
To set a different gravity value for the y
direction (vertical), provide a value (in pixels per second squared):
200
represents the gravity value (in pixels per second squared). Change this to the value you need for your particle effect. Negative values for gravity.y
pull particles upwards, and positive values pull particles downwards.
To turn off the gravity effect for particles, just set both gravity values to zero:
By default, each particle will be automatically removed 2 seconds after being emitted. However, you can change the particle lifespan.
NOTE: You will typically set the particle lifespan when using the explode()
or flow()
methods to emit particles. So you don't necessarily need to directly set the lifespan
property unless necessary for other reasons.
To set the particle lifespan:
1000
represents the lifespan (in milliseconds) of each particle (1000 ms = 1 second). Change this to the duration you want.
If desired, you can change the transparency of a particle by changing its alpha value. You can make a particle semi-transparent by lowering its alpha value. You can also change the alpha value of each particle over time to make the particles fade in or fade out.
For example, "smoke" particles for an explosion would look more realistic if they were semi-transparent and faded out over time.
By default, particles have an alpha value of 1 (no transparency applied).
However, you can set your own range for the particle transparency by setting the minimum and maximum alpha values for the particles. Each particle will receive a random alpha value within your specified range.
For example, to set the alpha range for a smokeEmitter
:
Change the values to whatever you need:
An alpha value of 0 is completely transparent.
Alpha values between 0-1 are semi-transparent. Lower numbers are more transparent.
An alpha value of 1 has no transparency applied.
If desired, you can make each particle fade in or out over a specified amount of time.
To do this, you specify the starting and ending alpha values, as well as the amount of time over which the change in transparency should occur.
startAlpha
represents the starting alpha value for each particle. Use whatever number you want.
endAlpha
represents the ending alpha value for each particle. Use whatever number you want, but it should be different from the starting value. Use a larger value to make the particles become less transparent (fade in), or use a smaller value to make the particles more transparent (fade out).
duration
represents the amount of time (in milliseconds) over which each particle will fade (1000 ms = 1 second). Change this to the duration you want.
A "fade in" will typically change the alpha from 0 to 1. It could start or end at other values, as long as the ending value is higher.
A "fade out" will typically change the alpha from 1 to 0. It could start or end at other values, as long as the ending value is lower.
For example, to make "rain" particles fade in over time
For example, to make "smoke" particles fade out over time:
For example, to make "smoke" particles fade in and then fade out:
By default, all the particles will be displayed at actual size (the size of your particle's image or spritesheet frame). If desired, you can set a range to scale the particles, so they vary in size. Alternatively, you can make the particles change in size over a specified duration.
You can set a range for the particle sizes by setting the minimum and maximum scales for the particles. Each particle will receive a random scale within your specified range.
For example, to set the scale range for the asteroidEmitter
:
Change the values to whatever you need:
A scale value less than 1 will make the particle smaller. For example, a value of 0.5 would make the particle half of its original size.
A scale value of exactly 1 will keep the particle its original size.
A scale value greater than 1 will make the particle larger. For example, a value of 2 would make the particle twice its original size.
If desired, you can make each particle change in size over a specified amount of time.
To do this, you specify the starting and ending scale values for the x
direction and the y
direction. (To avoid distorting your particle images, use the same values for both x
and y
.) Then specify the amount of time over which the change in size should occur.
startX
represents the starting scale value in the x
direction (horizontal). Use whatever number you want.
endX
represents the ending scale value in the x
direction (horizontal). Use whatever number you want, but it should be different from the starting value. Use a larger value to make the particles grow larger, or use a smaller value to make the particles get smaller.
startY
represents the starting scale value in the y
direction (vertical). It is recommended to use the same value as you did for startX
.
endY
represents the ending scale value in the y
direction (vertical). It is recommended to use the same value as you did for endX
.
duration
represents the amount of time (in milliseconds) over which each particle will change in size (1000 ms = 1 second). Change this to the duration you want.
For example, to make "rain" particles grow in size over time
For example, to make "asteroid" particles shrink in size over time:
For example, to make "smoke" particles grow larger and then shrink:
If needed, you can emit a single particle from the emitter:
The explode()
method is used to emit a one-time burst of particles from the emitter. You can either emit all the particles or a certain quantity. This is commonly used to create an explosion effect.
The explode()
method is typically placed in the update()
function or in a custom function to start the particle burst when needed.
For example, to emit a one-time burst of particles from an emitter named asteroidEmitter
:
1000
represents the lifespan (in milliseconds) of each particle (1000 ms = 1 second). Change this to the duration you want.
5
represents the number of particles to emit. Change this to the quantity you want. (If you want to emit all the particles, then just leave out this number).
The flow()
method is used to emit a continuous flow of particles from the emitter at specified time intervals.
You can make the flow emit one particle at a time — or multiple particles each time. You can allow the flow to run forever — or have it stop after a certain number of particles have been released. You can also stop the flow manually if needed.
For particle emitters being used for weather effects (such as rain, etc.), the flow()
method is typically placed in the create()
method, so that the particle flow will be running at the start of the game.
However, you can also use the flow()
method in your update()
function or in a custom function to start or stop the particle flow when needed.
To continuously emit one particle at specified intervals:
1500
represents the lifespan (in milliseconds) of each particle (1000 ms = 1 second). Change this to the duration you want.
25
represents the time interval (in milliseconds) between each release of a particle (1000 ms = 1 second). Change this to the interval you want.
To continuously emit multiple particles at specified intervals:
1500
represents the lifespan (in milliseconds) of each particle (1000 ms = 1 second). Change this to the duration you want.
25
represents the time interval (in milliseconds) between each release of a particle (1000 ms = 1 second). Change this to the interval you want.
5
represents the quantity of particles to release at each interval. Change this to the quantity you want.
To have the emitter stop flowing after a certain number of particles have been released:
1500
represents the lifespan (in milliseconds) of each particle (1000 ms = 1 second). Change this to the duration you want.
25
represents the time interval (in milliseconds) between each release of a particle (1000 ms = 1 second). Change this to the interval you want.
5
represents the quantity of particles to release at each interval. Change this to the quantity you want.
2000
represents the total number of particles to release before stopping. Change this to the total you want.
You can also manually stop the flow from an emitter by using:
This makes the emitter release one last particle and then turn off automatically. If necessary, you can always turn the emitter back on again using a different flow()
command.
By default, particles do not collide with each other or with other sprites. However, if you want, you can make them collide. The particle emitter acts like a group of sprites, so just use the variable name for the particle emitter in collide()
methods to make the individual particles collide with each other and/or with other sprites (or groups).
See the Physics and Collisions reference section for more details on the collide()
method.
Phaser has built-in methods to generate different types of random numbers (and to select things at random). Using these methods is easier than writing your own code to generate specific types of random numbers based on the JavaScript Math.random()
function.
Random numbers are commonly used in video games to introduce variation that makes the game less predictable. Random numbers are also used to make decisions based on probabilities of certain events happening. Applying random numbers in the right ways can increase the challenge in the game and make repeated playing more enjoyable.
For example, some games place certain resources or obstacles in random or varied locations. Many games use random numbers to create variation in how enemies behave.
Your game doesn't have to use random numbers to be enjoyable, but you should try to think about ways that variation or randomness could improve the gameplay experience.
Get Random Position in Game World
Get Random Member of Group
Get Random Whole Number Within Range
Get Random Decimal Number Within Range
Get Random Angle in Degrees
Get Random Number Between 0 and 1
Get Random Number Between -1 and 1
Get Random Sign (Positive or Negative)
Properties and Methods for Random Data Generator
You can get a random x
position and/or random y
position within your game world boundaries. For example, this could be used to place resources, obstacles, or enemies within your game.
For example, to add a zombie
to a random position within the game world:
You can get a random existing member of a group of sprites. For example, this could be used to add unpredictability to enemy behavior.
For example, to select a random member of zombieGroup
to move towards the player
:
You can get a random integer (whole number) within a range by specifying the minimum and maximum numbers for the range. (The minimum and maximum values will be included as part of the possible range to randomly select from.)
For example, to give each enemy a random velocity between 125 and 175 pixels per second:
You can get a random real number (decimal number) within a range by specifying the minimum and maximum numbers for the range. (The minimum and maximum values will be included as part of the possible range to randomly select from.)
For example, to give each asteroid
a different size by scaling it to a decimal value between 0.5 and 2:
You can get a random angle between -180 degrees and 180 degrees.
As a reference for angle direction, here are a few examples:
0
represents pointing to the right
-90
represents pointing up
-180
and 180
both represent pointing to the left
90
represents pointing down
For example, in a top-down game, this could be used to give each enemy in a group a random angle for its movement.
You can get a random decimal number between 0 and 1 (which is like getting a random fraction, proportion, or chance).
This is equivalent to using the regular JavaScript random function:
For example, you could use this to make decisions in the game by calculating the chances of something happening. Imagine that your game is designed so that an enemy's bullet has a 25% chance of damaging the player by 10 points — but will otherwise cause only 5 points of damage.
You can get a random decimal number between -1 and 1.
For example, imagine that you want to introduce a small margin of error in the fire angle of the enemy weapon (so the enemy is not a perfect shot every time it aims and fires at the player). Let's say you wanted to add a small random margin of error between -5 and 5 degrees to the enemy's aim.
You can get a random sign (positive or negative). It returns a result of either -1 or 1. Think of it like a coin flip: heads or tails.
For example, in a side-view game, this could be used to give each enemy in a group a random direction for its horizontal velocity: about half of the enemies will randomly get a positive velocity (moving to right), and the rest will get a negative velocity (moving to left).
You can keep track of the elapsed time (or remaining time) in your game. You can also add timers to make certain events occur after a designated amount of time has elapsed.
Event timers work by calling a custom function (that you create) once the designated amount of time has elapsed. Your custom function contains whatever actions you want to perform for the event.
For example, your game might have power-ups that give the player a boost in speed for a certain amount of time. Once the player collects a power-up, your game could start a timer so the speed boost will be stopped by a custom function after 10 seconds.
Useful Constants for Setting Timers
Add Timer for Single Event
Add Timer for Repeated Event
Add Timer for Looping Event
Get Elapsed Game Time
Calculate Elapsed Time Between Events
Create Count Up Timer (Elapsed Game Time)
Create Countdown Timer (Game Time Remaining)
Start Timer After Specific Key Pressed
Timers are set in milliseconds (1000 ms = 1 second). You can list a specific number (e.g., 5000
would be 5 seconds) or a calculated number (e.g., 1000 * 5
would also be 5 seconds).
Phaser has two constants that are helpful when setting a timer:
Phaser.Timer.SECOND
represents the number of milliseconds in one second (1000 ms = 1 second).
Phaser.Timer.MINUTE
represents the number of milliseconds in one minute (60000 ms = 1 minute).
For example, imagine that you need to set a timer for 2 minutes. Any of these could be used to represent the number of milliseconds in 2 minutes:
120000
1000 * 60 * 2
Phaser.Timer.SECOND * 120
Phaser.Timer.MINUTE * 2
Even though any of those could be used, the last option is probably the easiest to understand when reading the code (and the least likely for you to make a mistake when typing the code).
You can set a timer for a single event. When the designated amount of time has elapsed, a custom function will be called to perform whatever actions you decide.
The generic code for setting a single event timer is:
time
is the amount of time (in milliseconds) that should elapse before calling the event function. Change this to any time that you need.
timerEvent
is the name of the custom function to run when the timer is completed. List the function name without parentheses. You will have to create this custom function in your game. Change this to any unique function name that makes sense for your game.
If you want the timer to start when the game starts, then set the timer in your create()
function.
If you want to start the timer at some other point during the game, then set the timer in your update()
function or in a custom function.
For example, to set a 10 second timer:
Then be sure to add your custom function after the update()
function:
You can set a timer for a repeated event. When the designated amount of time has elapsed, a custom function will be called to perform whatever actions you decide. Then a new timer will be started — this will repeat however many times you specify.
The generic code for setting a repeated event timer is:
time
represents the amount of time (in milliseconds) that should elapse before calling the event function. Change this to any time that you need.
quantity
represents the number of times that the timer should repeat itself.
timerEvent
is the name of the custom function to run when the timer is completed. List the function name without parentheses. You will have to create this custom function in your game. Change this to any unique function name that makes sense for your game.
If you want the timer to start when the game starts, then set the timer in your create()
function.
If you want to start the timer at some other point during the game, then set the timer in your update()
function or in a custom function.
For example, to set a 10 second timer that will repeat itself 5 times:
Then be sure to add your custom function after the update()
function:
You can set a timer for a looping event. When the designated amount of time has elapsed, a custom function will be called to perform whatever actions you decide. Then a new timer will be started — this will keep repeating in an endless loop throughout the game.
The generic code for setting a looping event timer is:
time
represents the amount of time (in milliseconds) that should elapse before calling the event function. Change this to any time that you need.
timerEvent
is the name of the custom function to run when the timer is completed. List the function name without parentheses. You will have to create this custom function in your game. Change this to any unique function name that makes sense for your game.
If you want the timer to start when the game starts, then set the timer in your create()
function.
If you want to start the timer at some other point during the game, then set the timer in your update()
function or in a custom function.
For example, to set a 30 second timer that will keep repeating in a loop:
Then be sure to add your custom function after the update()
function:
Phaser keeps track of how much total time has elapsed (in seconds) since the game started. You can get this value and assign it to a variable to do something with the result.
The total elapsed time will be a decimal number representing exactly how many seconds have elapsed since the game started.
If you want to get the exact time that a particular event occurred, you would use this:
If you want to get the elasped time rounded down to the nearest whole second, you would use this:
You can use global variables to store the exact time that two different events occurred in the game, and then subtract the times to calculate how much time passed between them.
For example, get the time that the first event occurred and store it in a global variable:
Then get the time that the second event occurred and store it in another variable. Subtract the two times to get the amount of time that elapsed between the two events:
As a more specific example, imagine that your game rewards the player for how fast they progress from one checkpoint to the next. Your game could have an overlap()
method to detect when the player reaches a checkpoint (assume that each checkpoint is a member of a group of sprites). When an overlap occurs, a custom function is called that gets the exact time the event occurred and awards a score point bonus based on how much time elapsed since the previous checkpoint.
For example, some of the code might look like this:
You can create a "count up" timer to display the elapsed time since the game started.
Create a global variable named timeText
that will be used to display the count up timer.
In your create()
function, add a text object by assigning it to timeText
. If you have an extended game world, be sure to make the text stay fixed to the camera view.
Next you'll add a custom function that get the current elapsed time (rounded down the nearest second) and then convert that into minutes and seconds to display on-screen as text.
After your update()
function, add this custom function:
update()
Now you just need to call the custom function in your update()
function, so it will update the count up timer every game loop.
Just include this as the first line of code inside your update()
function:
You can create a countdown timer to display the amount of time remaining in the game (based on a specified time limit). The timer starts counting down when the game starts.
You could display the countdown timer as text or as a timer bar. Both options will be provided below.
Before your preload()
function, declare a global variable named timeLimit
for your countdown timer, and assign it a value representing the number of seconds for the countdown timer (e.g., 120
would represent a 2 minute countdown timer). Declare a global variable named timeOver
that will be used to detect when the countdown timer has reached zero. Also declare a global variable named timeText
that will be used to display the time remaining.
In your create()
function, add a text object by assigning it to timeText
. If you have an extended game world, be sure to make the text stay fixed to the camera view.
Next you'll add a custom function that get the current elapsed time (rounded down the nearest second) and then convert that into minutes and seconds to display on-screen as text.
After your update()
function, add this custom function:
update()
Now you just need to call the custom function in your update()
function, so it will update the count up timer every game loop until the time runs out.
Just include this at the beginning of your update()
function:
Create an image of a solid rectangle (whatever color you want) that will be used as the timer bar (to represent time remaining). This example will use a green rectangle that is 200px in width and 20px in height:
Create an image of an identical-sized solid rectangle (of a different color) that will be used as a background bar (to represent elapsed time). This example will use a red rectangle that also is 200px in width and 20px in height:
Before your preload()
function, declare a global variable named timeLimit
for your countdown timer, and assign it a value representing the number of seconds for the countdown timer (e.g., 120
would represent a 2 minute countdown timer). Declare a global variable named timeOver
that will be used to detect when the countdown timer has reached zero. Finally, declare a global variable named timeBar
for the timer bar:
In your preload()
function, load the two rectangle images:
The background bar (red) and the timer bar (green) will be added to the game at the same position. However, the background bar will be added first, so it will be stacked behind the timer bar.
In your create()
function, add the background bar by assigning it to a local variable. Then add the timer bar at the same position by assigning it to its global variable.
You should also add a text label to the left of the bars by assigning it to a local variable.
If your game world is larger than your game display, then be sure to make the images and text label fixed to the camera.
As the amount of time remaining decreases, the width of the timer bar is supposed to shrink proportionately. When the timer bar shrinks, it will reveal some of the background bar behind it.
We can make the timer bar shrink by changing its scale
property. The correct value for the scale can be calculated by dividing the time remaining by the time limit.
After your update()
function, add this custom function:
update()
Now you just need to call the custom function in your update()
function, so it will update the count up timer every game loop until the time runs out.
Just include this at the beginning of your update()
function:
You can also make your timer wait to start until a specific key is pressed. The player won't be able to perform any other actions until the "start" key has been pressed.
In this example, we'll use the spacebar as the "start" key. Once the game is "started," this same key can be used instead for other normal game actions. For example, some games use the spacebar for actions such as jumping or firing a weapon.
This example will work with a countdown timer or a countup timer - but you'll need to use a slightly modified function for your timer that adjusts for the actual "start time" when the player first pressed the spacebar key.
Before your preload()
function, declare a global variable named started
, and assign it a value of false
(later when the spacebar key is first pressed, you'll change this variable to a value of true
).
Declare another global variable named startTime
, and assign it an initial value of zero (later this variable will be used to store the time when the spacebar key is first pressed).
Finally, declare a global variable named spacebar
that will be used to represent the spacebar key.
Within your create()
function, make the spacebar
variable represent user input using the spacebar key.
Again, if you also want to use the spacebar for other player actions (such as jumping, firing weapon, etc.) after the game is "started," you'll be able to do so.
Within your update()
function, you'll add some if
statements that will check whether the variable named started
has a value of true
or false
. These will be added after your game.physics.arcade.collide()
functions (which are usually listed at the beginning of the update()
function).
If started
has a value of true
, then the game will updated the time left in the countdown timer and check for any player input to perform normal game actions (such as moving, jumping, firing weapon, etc.).
However, if started
has a value of false
, then the game will only check to see if the spacebar was pressed. The game will NOT update the countdown timer (meaning it will act like it is "paused"), and the game won't allow the player to perform any other actions. The game is simply waiting for the player to press the spacebar.
Once the spacebar is pressed, the game will change the value of started
to true
, and store the current game time in the startTime
variable (so the timer can adjust itself based on the start time).
Finally, you'll have to modify your timer function to adjust for the start time (when the player first pressed the spacebar).
The custom function for your timer should be placed after the update()
function.
Here's a modified countdown timer function for displaying a shrinking time bar:
If you are using a countup timer, your modified timer function would instead look like this:
You can add simulated physics interactions to your game. Objects in the game can move and accelerate. Objects can be affected by gravity and friction. Objects can collide and bounce off each other — and can respond differently based on their relative masses.
The cool thing about simulated physics is that you can make your game world behave however you want. You could change gravity so objects will "fall up" instead of falling down. You could selectively apply gravity only to certain objects — or eliminate gravity entirely.
You can set your game physics to simulate different kinds of actions in different kinds of environments, such as jumping into the air, sliding on ice, swimming in water, flying through space, etc.
NOTE: Phaser has several physics systems to choose from. However, this reference section will focus only on the Arcade Physics system, which is a good choice for many games.
Arcade Physics has lots of useful physics properties and methods, plus support for weapons (e.g., shooting or throwing objects such as bullets, lasers, arrows, fireballs, snowballs, etc.). Arcade Physics is more simplistic than the other physics systems, but it runs much faster as a result.
Start Physics System
Enable Physics on Sprite or Group of Sprites
Set Physics Body Properties for Sprite
Gravity
Collide With World Boundaries
Velocity
Maximum Velocity
Acceleration
Drag
Bounce
Mass
Immovable
Friction
Angular Velocity
Maximum Angular Velocity
Angular Acceleration
Angular Drag
Check for Collide or Overlap of Sprites and Groups
Change Collision Area of Sprite
Find Group Member that is Closest or Farthest to Sprite
Get Distance from Sprite to Object, Location, or Pointer
Move or Accelerate Sprite Towards Object, Location, or Pointer
Get Angle Between Sprite and Object, Location, or Pointer
Move or Accelerate Sprite in Direction of Angle
Create Platforms that Move Back-and-Forth
In addition, see the Weapon section for coding references explaining Arcade Physics weapons.
In order to use any physics properties and methods in your game, you first need to start the physics system.
To start the Arcade Physics system, add this command at the beginning of your create()
function:
Physics properties and methods can only be applied to sprites or groups of sprites. However, you must first specifically "enable" physics on each sprite or group that you want to involve in physics interactions.
When you enable physics on a sprite (or a group of sprites), it creates a "physics body" for the sprite (or for each sprite in the group). You can then get or set the sprite's physics body properties and also check for collisions between the physics bodies of different sprites.
For example, to enable physics on an individual sprite named player
:
Add this command in your create()
function after adding the sprite (and before setting any of its physics body properties).
You will need to enable physics on every sprite that you want to involve in physics interactions.
For example, to enable physics on a group of sprites named platformGroup
:
Add this command in your create()
function after adding the group (and before setting any physics body properties for members of the group).
You will need to enable physics for every group that you want to involve in physics interactions.
When you enable physics on a sprite or a group of sprites, it creates a "physics body" for the sprite. This sub-section describes the most common physics body properties that you might use in your game.
You can use gravity to pull a sprite in a certain direction (make it fall down, etc.), and make the sprite collide with the world boundaries (to simulate colliding with a solid surface).
You can use velocity or acceleration to move a sprite, and set a drag that slows it down.
You can set the bounce and mass of a sprite to change how it is affected by collisions with other sprites.
You can make a sprite immovable to prevent collisions from changing its position or speed. If the "immovable" sprite is moving (e.g., moving platform), you can set a friction value that affects other sprites riding on top of it.
You can use angular velocity or angular acceleration to rotate a sprite, and set an angular drag that slows it down.
We're used to Earth's gravity pulling objects down towards the surface. Earth's gravity affects all objects and causes them to fall at the same rate.
However, in your game world, you have custom control over each sprite's gravity. You can set a sprite's gravity to make it fall up — or to make the sprite get pulled sideways by gravity — or do both. Gravity only affects the sprites you want, and you can set different gravity values for different sprites.
For certain games (e.g., game using top-down perspective, game set in outer space, etc.), you might not want your sprites to be affected by gravity (i.e., you'd just leave the sprite's gravity at its default value of zero).
You can set a sprite's gravity value for the horizontal direction (x
) and/or the vertical direction (y
). The value represents the acceleration due to gravity measured in pixels per second squared. By default, a sprite's gravity is set to zero in each direction, unless you specifically change it.
body.gravity.x
— change this value to add gravity on the sprite in the horizontal direction. Positive values will pull the sprite to the right, and negative values will pull the sprite to the left.
body.gravity.y
— change this value to add gravity on the sprite in the vertical direction. Positive values will pull the sprite downwards, and negative values will pull the sprite upwards.
If you want to add a "normal" Earth-type gravity to a sprite, then set a positive value for its body.gravity.y
property.
For example, to make the player
sprite fall downward:
You can force a sprite to stay within the game world by making it collide with the world boundaries. Otherwise, it might leave the game world if it is moving (due to its velocity or gravity). By default, a sprite will not collide with the world boundaries, unless you specifically tell it to do so.
For example, to force a sprite named player
to collide with the game world boundaries:
player.body.collideWorldBounds = true;
Velocity represents the speed and direction of a sprite:
A positive velocity means the sprite is moving in a particular direction.
A negative velocity means the sprite is moving in the opposite direction.
A velocity of zero means the sprite is not moving.
Thus, if you set a sprite's velocity to any non-zero value (positive or negative), it will cause the sprite to move.
The velocity is a number representing the sprite's speed in pixels per second. This property is set separately for the x
and y
directions:
body.velocity.x
— change this value to move the sprite in the horizontal direction. Positive values will move the sprite to the right, and negative values will move the sprite to the left. Larger numbers (whether positive or negative) move the sprite faster.
body.velocity.y
— change this value to move the sprite in the vertical direction. Positive values will move the sprite down, and negative values will move the sprite up. Larger numbers (whether positive or negative) move the sprite faster.
For example, to make the player
sprite move to the left:
-200
represents the speed in pixels per second. The negative value represents moving to the left. Change this value as needed.
NOTE: Once a sprite is given a non-zero velocity, it will keep moving at that speed and direction unless something else causes the velocity to change.
For example, if you were creating a side-scrolling endless runner game, you could set a positive value for player.body.velocity.x
in the create()
function to make the player move to the right at a constant speed. Then your update()
function might only give the player the option to move up and down by changing the player.body.velocity.y
value.
You can also get (i.e., read) the values to check the speed and/or direction of a sprite.
For example, to determine if the player
sprite is moving to the left or right (or not moving at all):
You can set a maximum velocity for a sprite to set a limit on how fast it can move. This is typically done in the create()
function.
The maximum velocity is set as a number representing the maximum speed in pixels per second. This property is set separately for the x
and y
directions:
body.maxVelocity.x
body.maxVelocity.y
For example:
It is common to use the same value for both directions, so you can also set both directions at the same time. For example, to set a maximum velocity for a sprite named player
:
500, 500
represent the values for the x
and y
directions (in that order). If you only list one number, then it will use that same value for both directions. Change these numbers as needed.
Acceleration is the rate of change in a sprite's velocity:
A positive acceleration means the sprite is speeding up.
A negative acceleration means the sprite is slowing down.
An acceleration of zero means the sprite is maintaining the same speed.
Acceleration is a number representing the sprite's change in speed in pixels per second. This property is set separately for the x
and y
directions:
body.acceleration.x
— change this value to change the sprite's velocity in the horizontal direction. Positive values will speed up the sprite, and negative values will slow down the sprite. Larger numbers (whether positive or negative) change the speed more quickly.
body.acceleration.y
— change this value to change the sprite's velocity in the vertical direction. Positive values will speed up the sprite, and negative values will slow down the sprite. Larger numbers (whether positive or negative) change the speed more quickly.
For example, to accelerate a sprite named player
in the horizontal direction:
In the example above, if the player.body.velocity.x
were initially zero (not moving), then after 1 second its velocity would be 100 pixels per second, after 2 seconds it would be 200, after 3 seconds it would be 300, etc.
NOTE: If you keep applying an acceleration, it will continue to change the sprite's velocity:
Example 1: A sprite initially has a velocity of zero (not moving). Applying a positive acceleration will increase the sprite's velocity (causing it to start moving). Continuing to apply a positive acceleration will continue to increase the sprite's velocity (making it move faster over time).
Example 2: A sprite initially has a positive velocity (moving). Applying a negative acceleration will decrease the sprite's velocity (making it slow down). Continuing to apply a negative acceleration will continue to decrease the sprite's velocity (making it slow down further) until it reaches zero (stops moving) and then becomes negative (moving again but now in the opposite direction).
Drag represents a negative acceleration that slows down the sprite when it is moving. Drag is a type of friction also referred to as air resistance (or fluid resistance if moving in a liquid).
Drag only affects the sprite when it is moving. If the sprite is not moving (velocity is zero), then drag will not affect it.
Drag is a number representing the sprite's reduction in speed in pixels per second squared. This property is set separately for the x
and y
directions. By default, a sprite's drag is set to zero in each direction unless you specifically change it.
body.drag.x
— reduces the sprite's velocity in the horizontal direction when the sprite is moving
body.drag.y
— reduces the sprite's velocity in the vertical direction when the sprite is moving
For example, to set the horizontal drag of the player
sprite:
It is common to use the same value for both directions, so you can also set both directions at the same time. For example:
25, 25
represent the values for the x
and y
directions (in that order). If you only list one number, then it will use that same value for both directions. Change these numbers as needed.
The effects of drag are most noticeable if your game changes the sprite's speed by using acceleration (instead of by setting a specific velocity value).
You can set the bounce
property for a sprite's physics body, which affects its rebound velocity after colliding with another physics body (i.e., another sprite).
The bounce
property typically uses a value between 0-1. A value of 0 results in no bounce after a collision. A value of 1 results in a perfectly elastic bounce, where the sprite bounces backward from a collision without losing any velocity. A value of 0.5 results in the sprite bouncing backwards with only 50% of its original velocity.
However, you can also set the bounce
to a value greater than 1, which will actually cause the sprite to gain velocity whenever it bounces. A value of 1.25 would result in the sprite bouncing backwards with 125% of its original velocity. This could be an interesting feature for certain games.
You can set a sprite's bounce value for the horizontal direction (x
) and/or the vertical direction (y
). By default, a sprite's bounce is set to zero in each direction unless you specifically change it.
body.bounce.x
— change this value to add a bounce to the sprite in the horizontal direction. You would typically use a value between 0-1 (but can use a higher value if desired).
body.bounce.y
— change this value to add a bounce to the sprite in the vertical direction. You would typically use a value between 0-1 (but can use a higher value if desired).
For example, to give the player
sprite a small bounce effect in the vertical direction:
You can also set both directions at the same time. For example, to make a sprite named pinball
have perfectly elastic bounces (without losing any velocity):
1, 1
represent the values for the x
and y
directions (in that order). If you only list one number, then it will use that same value for both directions.
You can set the mass of a sprite (basically the "weight" of the sprite). The mass will affect how the velocity of the sprite changes after a collision because Phaser physics systems conserve momentum during collisions (just like real-life physics).
Momentum is calculated as an object's mass multipled by its velocity. When objects collide, they transfer kinetic energy, which can cause one object to slow down while the other object speeds up. These changes in speed are affected by the masses of the objects and their velocities before the collision.
The conservation of momentum means that the total momentum of objects before a collision is equal to the total momentum of the objects after the collision.
Every sprite's mass is set to 1 by default, unless you change it. The value for mass is relative: use a value less than 1 to make a sprite less massive, or use a value greater than 1 to make a sprite more massive.
If you want to add more realistic collisions between objects, then set different values for their masses.
For example, to set the mass of a sprite named player
:
This would make the player
sprite have 2 times the mass of other sprites (if they still have the default mass of 1).
A sprite that is set as "immovable" will not be affected by collisions (meaning its position and velocity will not be changed if something else collides with it).
Platforms, walls, and similar types of objects are typically set to be immovable. If you didn't set these to immovable, then they would move if something collided with them, such as the player jumping onto a platform or the player running into a wall. (Though, collapsible platforms and movable walls might be an interesting feature for certain games.)
For example, to make a sprite named platform
immovable:
platform.body.immovable = true;
NOTE: Despite the name "immovable" — you can make a immovable sprite actually move by setting its velocity, acceleration, or gravity. For example, this would allow you to create moving platforms that the player could jump on and ride.
The best way to understand "immovable" is this:
An immovable sprite can move itself, but it cannot be moved by other sprites that collide with it.
In Arcade Physics, the friction property is only applicable to "immovable" sprites. Friction affects other sprites that are riding on top of a moving "immovable" sprite (such as a moving platform, etc.).
Friction is set as a value between 0-1 where 0 represents no friction and 1 represents maximum friction. Friction is set separately for the x
and y
directions. By default, an immovable sprite's friction is set to 1 for the x
direction and 0 for the y
direction, unless you specifically change them.
body.friction.x
(defaults to 1)
body.friction.y
(defaults to 0)
To change the horizontal friction for a sprite named platform
:
Imagine the player sprite jumps on top of a horizontally-moving platform (set to be "immovable"):
If the platform's body.friction.x
is set to 1, the player will stick to the platform and can ride it. As the platform moves, the player will be carried along at the same speed.
If the platforms's body.friction.x
is set to 0, the player can land on the platform but won't stick to it — the platform will slide along beneath the player (without carrying the player along) — in order to stay on the platform, the player will eventually have move itself.
If the platforms's body.friction.x
is set to 0.5, the player can land on the platform, but it will be less sticky. As the platform moves, it will slide a bit underneath the player, only moving the player at half the platform's speed (and eventually the player will slide off unless the player moves itself).
The best way to understand friction in Arcade Physics is to try different values in your game to see the effect when another sprite (such as the player) lands on a moving "immovable" object.
Angular velocity represents the rotational speed and direction of a sprite:
A positive angular velocity means the sprite is rotating clockwise.
A negative angular velocity means the sprite is rotating counter-clockwise.
An angular velocity of zero means the sprite is not rotating.
Thus, if you set a sprite's angular velocity to any non-zero value (positive or negative), it will cause the sprite to rotate.
The angular velocity is a number representing the sprite's rotational speed in degrees per second.
body.angularVelocity
— change this value to rotate the sprite. Positive values will rotate the sprite clockwise, and negative values will rotate the sprite counter-clockwise. Larger numbers (whether positive or negative) rotate the sprite faster.
For example, to make the player
sprite rotate counter-clockwise:
-200
represents the rotational speed in pixels per second. The negative value represents counter-clockwise rotation. Change this value as needed.
You can set a maximum angular velocity for a sprite to set a limit on how fast it can rotate. This is typically done in the create()
function.
The maximum angular velocity is set as a number representing the maximum speed in degrees per second.
For example, to set a limit on the rotational speed of the player
sprite:
Angular acceleration is the rate of change in a sprite's velocity:
A positive angular acceleration means the sprite's rotation is speeding up.
A negative angular acceleration means the sprite's rotation is slowing down.
An angular acceleration of zero means the sprite's rotation is maintaining the same speed.
Acceleration is a number representing the sprite's change in rotational speed in pixels per second.
body.angularAcceleration
in degrees per second squared
For example, to accelerate the rotation of the player
:
In the example above, if the player.body.angularVelocity
were initially zero (not rotating), then after 1 second its angular velocity would be 100 pixels per second, after 2 seconds it would be 200, after 3 seconds it would be 300, etc.
NOTE: If you keep applying angular acceleration, it will continue to change the sprite's angular velocity:
Example 1: A sprite initially has a angular velocity of zero (not rotating). Applying a positive angular acceleration will increase the sprite's angular velocity and cause it to start rotating clockwise. Continuing to apply a positive acceleration will continue to increase the sprite's angular velocity (making it rotate faster over time).
Example 2: A sprite initially has a positive angular velocity (rotating clockwise). Applying a negative acceleration will decrease the sprite's angular velocity (making its rotation slow down). Continuing to apply a negative acceleration will continue to decrease the sprite's angular velocity (making it slow down further) until it reaches zero (stops rotating) and then becomes negative (rotating again but now in the counter-clockwise direction).
Angular drag represents a negative acceleration that slows down the player when it is rotating. Angular drag is a type of friction also referred to as air resistance (or fluid resistance if rotating in a liquid).
Angular drag only affects the sprite when it is rotating. If the sprite is not rotating (angular velocity is zero), then drag will not affect it.
Drag is a number representing the sprite's reduction in rotational speed in degrees per second squared. By default, a sprite's angular drag is set to zero, unless you specifically change it.
For example, to set the angular drag for the player
sprite:
The effects of drag are most noticeable if your game changes the sprite's speed by using angular acceleration (instead of by setting a specific angular velocity value).
Phaser's Arcade Physics system has two methods to detect when sprites "touch" each other:
collide()
checks to see if two sprites are touching and keeps them separated (so they don't overlap, as if they were "solid") — if one or both of the objects is moving, they can transfer momentum between each other (e.g., one sprite might speed up while the other slows down, etc.)
overlap()
simply checks to see if two sprites are touching (but the objects can overlap and move past each other as separate layers)
The two sprites being checked for collide()
or overlap()
can be individual sprites and/or groups of sprites, such as:
Sprite vs. Sprite
Sprite vs. Group
Group vs. Group
For groups, each member of the group is checked individually. You can even check a group against itself. For example, you could check to see if any members of the same group are colliding with each other.
The collide()
and overlap()
commands must be placed in your update()
function, so your game will constantly check them during gameplay.
Different sprites can overlap and will move past other as separate layers unless you use a collide()
statement to keep them separated.
For example, to make the player
sprite collide with the enemyGroup
:
player, enemyGroup
are the names of the two sprites to check for collisions. You can check: sprite vs. sprite, sprite vs. group, group vs. group. Change these to match the names of your sprites or groups.
If two moving sprites collide with each other, they can transfer momentum between each other (depending on the mass
and velocity
of each sprite), which can change how fast they are moving. Also, they might "pile up" unless you give one or both of them a value for their physics body bounce
property.
If one of the sprites or groups has been set to immovable
, then its position or velocity will not be affected when another sprite collides with it. Sprites or groups representing platforms, walls, etc. are typically set to immovable
and then made to collide with the other sprites or groups.
For example, to make the player
sprite, coinGroup
, and enemyGroup
all collide with the platformGroup
:
To make members of the same group collide with each other, you can just list the group once:
This command tells the Arcade Physics system to check for collisions between any two members of enemyGroup
. This will keep the members separated and prevent them from moving past each other.
However, if you do make members of the same group collide with each other, they might "pile up" unless you give each of them a value for their physics body bounce
property.
Often, you'll want your game to perform certain actions whenever two specific sprites collide with each other.
For example, if the player
sprite collides with a member of the coinGroup
, you might want to perform several actions, such as: remove the specific member of the coinGroup
, play a sound effect, change the player's score, etc.
You can modify the collide()
command to list the name of a specific custom function that should run whenever a collision occurs between the two sprites. You will have to create the custom function in your game code.
The specific sprites involved in the collision will be passed into the custom function as parameters (variables), so you can perform actions involving them (such as: damage one of the sprites, etc).
For example, to call a custom function when the player
sprite collides with any member of enemyGroup
:
player, coinGroup
are the names of the two sprites to check for collisions. You can check: sprite vs. sprite, sprite vs. group, group vs. group. Change these to match the names of your sprites or groups.
collectCoin
is the name of the custom function to run. List the function name without parentheses. You will have to create this custom function in your game. Change this to any unique function name that makes sense for your game.
null, this
are additional parameters that you need to list.
Next, you will need to create the custom function by adding it after your update()
function:
collectCoin
is the name of the custom function. Change this to match the name you listed in your collide()
command.
player, coin
are the parameter variables that will be passed into the function by your collide()
command. These represent the two specific sprites involved in the collision. Change these to names that make sense for your collision.
The parameters are received in the same order as the original sprites or groups are listed in your collide()
command, so the order of the names matters (in order to make your code perform the actions you intend).
The parameter names that you use can be different from the original sprite or group names. However, it will be much easier to understand what's happening in the custom function if the names are either identical or similar to the original sprite or group names. (If the parameter represents a member of a group, then use a name similar to the group — but not the actual group name, which might be confusing.)
Inside the custom function, whatever you do to the parameter variables will affect the original sprites they represent.
In this example function, player
represents the player
sprite involved in the collision, and coin
represents the specific member of the coinGroup
that was involved in the collision. So if the command coin.kill()
were used inside the custom function, it would remove that specific member of the coinGroup
from the game world.
For example, the complete code for the collectCoin()
function might look like this:
Sometimes, you want your game to perform certain actions whenever two specific sprites touch each other — but you don't want to separate them by making them collide. This is when you would instead using the overlap()
method.
For example, if the player
sprite overlaps with a member of the checkpointGroup
, you might want to perform several actions, such as: play a sound effect, change the player's score, etc.
In the overlap()
command, you list the name of a specific custom function that should run whenever an overlap occurs between the two sprites. You will have to create the custom function in your game code.
The specific sprites involved in the overlap will be passed into the custom function as parameters (variables), so you can perform actions involving them (such as: damage one of the sprites, etc).
For example, to call a custom function when the player
sprite overlaps with any member of checkpointGroup
:
player, checkpointGroup
are the names of the two sprites to check for overlap. You can check: sprite vs. sprite, sprite vs. group, group vs. group. Change these to match the names of your sprites or groups.
updateCheckpoint
is the name of the custom function to run. List the function name without parentheses. You will have to create this custom function in your game. Change this to any unique function name that makes sense for your game.
null, this
are additional parameters that you need to list.
Next, you will need to create the custom function by adding it after your update()
function:
updateCheckpoint
is the name of the custom function. Change this to match the name you listed in your collide()
command.
player, checkpoint
are the parameter variables that will be passed into the function by your overlap()
command. These represent the two specific sprites that are overlapping. Change these to names that make sense for your overlap.
The parameters are received in the same order as the original sprites or groups are listed in your overlap()
command, so the order of the names matters (in order to make your code perform the actions you intend).
The parameter names that you use can be different from the original sprite or group names. However, it will be much easier to understand what's happening in the custom function if the names are either identical or similar to the original sprite or group names. (If the parameter represents a member of a group, then use a name similar to the group — but not the actual group name, which might be confusing.)
Inside the custom function, whatever you do to the parameter variables will affect the original sprites they represent.
In this example function, player
represents the player
sprite involved in the collision, and checkpoint
represents the specific member of the checkpointGroup
that was involved in the collision. So if the command checkpoint.kill()
were used inside the custom function, it would remove that specific member of the checkpointGroup
from the game world.
For example, the complete code for the updateCheckpoint()
function might look like this:
By default, the physics body collision area for a sprite is treated as a rectangle the same width and height of the image or spritesheet frames used for that sprite.
Sometimes the image or spritesheet for a sprite includes transparent areas around the outer edges. These transparent areas are counted as part of the sprite's collision area, so it can make collisions look or feel unnatural.
For example, here is an enlarged view of a spritesheet frame used for the player
sprite in Practice 3.
The gray-and-white checkerboard pattern represents transparent areas in the frame image. (The checkerboard pattern would not be visible in the game.)
The original spritesheet frame is 32 pixels in width and 48 pixels in height (including the transparent areas). By default, this will be the size of the collision area for this sprite.
However, using this default collision rectangle can result in collisions that look unnatural. For example, during gameplay, the sprite can look like it has completely stepped off the edge of a platform — yet it won't fall off because the sprite's transparent corner is still on top of the platform.
Instead, this sprite would behave more naturally in collisions if its collision area more closely matched its visual appearance, as shown below:
A pink rectangle has been added to show the size and position of a new collision rectangle that would work better for this sprite. (The rectangle would not be visible in the game.) This new collision rectangle is 20 pixels in width and 40 pixels in height. It is offset from the sprite's top-left corner by 8 pixels horizontally and 8 pixels vertically, so that it will be better positioned to match the sprite's visual body.
This new collision rectangle would ignore most (but not all) of the transparent area. Even this new collision rectangle is not perfect, as some of the sprite's head extends outside of the rectangle. However, overall, this rectangle is a much better match to the visual size of the body.
If necessary, you can change the size and position of the rectangle used to detect collisions of the sprite's physics body. (This won't change the visual appearance of the sprite.)
For example, to change the collision size for the player
sprite:
20, 40
represent the width and height (in pixels) of the collision rectangle. Change these numbers to match the size you want to use for your sprite.
8, 8
represent the horizontal offset and vertical offset (in pixels) of the collision rectangle's top-left corner, relative to the top-left corner of the sprite. Change these numbers to position the collision rectangle where you need it to be.
You can even change the sprite's collision area to have the shape of a circle, instead of a rectangle. In certain situations, this may make more sense than using a rectangular collision area.
For example, here is a spritesheet frame used for the asteroid sprites in Practice 2.
The spritesheet frame is 40 pixels in width and 40 pixels in height (including the transparent areas represented by the checkerboard pattern). By default, this would be the size of the collision area for this sprite.
A pink circle has been added to show the size and position of a new collision area that would work better for this sprite. (The circle would not be visible in the game.) This new collision circle is 30 pixels in diameter (15 pixels in radius). It is offset from the sprite's top-left corner by 5 pixels horizontally and 5 pixels vertically, so that it will be better positioned to match the sprite's visual body.
For example, to change the collision size for a asteroid
sprite:
15
represents the radius (in pixels) of the collision circle. Change this number to match the size you want to use for your sprite.
5, 5
represent the horizontal offset and vertical offset of the collision circle, relative to the top-left corner of the sprite. Change these numbers to position the collision circle where you need it to be.
WARNING: In some games, changing a sprite's collision area to be a circle can result in unusual bounce
effects if it collides with a sprite that has a rectangular collision area. Even if the sprite visually looks like a circle, sometimes the best way to get natural-looking collisions is to use a rectangular collision area for the sprite.
Arcade Physics has methods to find the specific member in a group that is closest or farthest to a target sprite (such as the player
sprite).
The closest()
and farthest()
methods return the specific member as an object, so you must assign the result (i.e., the specific member) to a local variable, in order to do something with the result.
For example, you could find the closest enemy to the player and then make that enemy fire a weapon at the player. The code might look like this:
Arcade Physics has methods to find the distance (in pixels) from a sprite to another object (another sprite, an image, etc.), a specific x, y
location, or the pointer's cursor location.
These distance methods return the distance as a number, so you must assign the result to a local variable, in order to do something with the result.
For example, you could find the distance between the player and a boss enemy, and then make the boss enemy fire a weapon at the player if the distance is below a certain value. The code might look like this:
Arcade Physics has methods to move or accelerate a sprite towards another object (another sprite, an image, etc.), a specific x, y
location, or the pointer's cursor location.
In the methods, you specify a value for the speed or acceleration to use when moving the sprite:
Positive values will move the sprite forwards.
Negative values will move the sprite backwards.
NOTE: These methods will move or accelerate the sprite in a direct line towards the destination. If you want the sprite to only move horizontally and/or vertically, then you may want to move the sprite using your own code, rather than these methods.
For example, you could find the closest enemy to the player and then move that enemy towards the player at a speed of 150 pixels per second. The code might look like this:
For example, your game could use the mouse to move the player. Whenever the mouse button is clicked, you could accelerate the player towards the mouse pointer location. The code might look like this:
Arcade Physics has methods to find the angle (in radians) from a sprite to another object (another sprite, an image, etc.), a specific x, y
location, or the pointer's cursor location.
These methods return the angle as a number, so you must assign the result to a local variable, in order to do something with the result.
Most people are familiar with using degrees to measure an angle. Radians are just another unit that can used to measure an angle (2π radians = 360°).
If needed, Phaser has a method to convert radians to degrees:
angleRad
represents an angle measured in radians
For example, the fire angle of a weapon uses degrees (not radians), so you could get the angle between a sprite and a "target" location, convert the angle from radians to degrees, and then set the weapon's fire angle (so the weapon is lined up with the "target").
As a reminder, sprites have a property called rotation
that can be used to rotate a sprite to a specific angle (in radians).
angleRad
represents an angle measured in radians
For example, once you get the angle between a sprite and a "target" location, you could rotate the sprite to that angle (so the sprite is lined up with the "target").
For example, you could get the angle between an enemy and the player, and rotate the enemy to that angle, so the enemy is pointed towards the player. The code might look like this:
For example, your game could use the mouse to shoot the player's weapon. Whenever the mouse button is clicked, you could get the angle between the player and the mouse pointer location. Then you could set the player's weapon to that angle (by converting radians to degrees) and fire the weapon. The code might look like this:
Arcade Physics has methods to move or accelerate a sprite in the direction of a specific angle. These methods are most commonly used to move a sprite in the direction of its current angle or rotation.
In the methods, you specify a value for the speed or acceleration to use when moving the sprite in the direction of the angle:
Positive values will move the sprite forward.
Negative values will move the sprite backwards.
As a reminder, Phaser has two properties for a sprite's angle of rotation:
sprite.angle
can be used to get or set the angle of rotation of the sprite, measured in degrees
sprite.rotation
can be used to get or set the angle of rotation of the sprite, measured in radians (2π radians = 360°)
Both properties do the same thing — they just use different units of measurement.
A common value used for angleDeg
is the sprite's current angle: sprite.angle
For example, you could use the arrow keys to rotate the player and to move the player forward or backwards in the direction of its current angle. The code might look like this:
A common value used for angleRad
is the sprite's current rotation: sprite.rotation
For example, for each member of an enemy group, you could rotate the enemy towards the player and then move the enemy forward towards the player. The code might look like this:
A common value used for angleRad
is the sprite's current rotation: sprite.rotation
For example, you could get the angle between an enemy and the player, and then accelerate the enemy forward at that angle (towards the player) — but without rotating the enemy's body.
You can create platforms that will move back-and-forth in a repeating pattern — either side-to-side or up-and-down. The player's sprite (and other sprites) can ride on top of these moving platforms.
You can make a platform move simply by setting a non-zero value for its body.velocity.x
(horizontal motion) or body.velocity.y
(vertical motion).
The instructions below will show you how to add moving platforms into your "regular" platform group, which will also contain your non-moving platforms.
In your create()
function, create a JSON array listing data for the moving platforms.
The example JSON array below lists data for 4 moving platforms:
Modify the JSON data for your platforms. Within the array, each platform's data is listed inside curly braces { }
using name-value pairs. You can list data for however many moving platforms you want.
For each platform, the following data should be listed:
x
represents the initial x-position of the top-left corner of the platform. List the value you want.
y
represents the initial y-position of the top-left corner of the platform. List the value you want.
asset
represents the name of the image asset key to use for the platform. Different platforms can use different assets, in order to look different and/or have different sizes. List the name of the asset you want to use. Be sure to list the asset name inside quotes.
direction
represents whether the platform should move horizontally (left-and-right) or vertically (up-and-down). List either 'horz'
or 'vert'
based on the motion you want.
distance
represents how far the platform should move (in pixels) before reversing its motion. Change this to the value you want.
speed
represents the speed (in pixels per second) at which the platform should move. Change this to the value you want. You can list either a positive value or a negative value, depending on which direction you want the plaftform to be initially moving:
For horizontally-moving platforms, a positive speed will make the platform initially move to the right, and a negative speed will make the platform initially move to the left. In either case, the platform will reverse direction once it has moved its specified distance.
For vertically-moving platforms, a positive speed will make the platform initially move downwards, and a negative speed will make the platform initially move upwards. In either case, the platform will reverse direction once it has moved its specified distance.
In your create()
function, create the moving platforms within your "regular" platform group using a for
loop and the JSON data.
Insert this for
loop after the commands that add your platformGroup
and enable physics on the group members. You can insert the for
loop before or after your code that adds non-moving platforms into the group.
If necessary, change platformGroup
to the name of your platform group (only need to change this in the first line of code inside the for
loop).
After adding all the platforms (moving and non-moving), be sure to set the body.immovable
property to true
for all members of your platform group.
In your update()
function, you need to check each moving platform in your platform group to see if it has moved its specified distance. If so, its direction will be reversed by multiplying its velocity by -1.
If necessary, change platformGroup
to the name of your platform group (only need to change this in the first line of code).
This code can be inserted anywhere in your update()
function, but it might make sense to place it towards the end after your collide()
statements and player input actions.
Computing by Design targets the intersection of design, technology, and business — where innovative product and service experiences are created.
This project focuses on the game design process. Like all Computing by Design projects, this course will help students learn how to build a solution using a particular computing technology. However, just as importantly, this project will help students learn how to design their solution to meet the needs and expectations of the intended users. After all, how do you market and sell a solution that no one actually wants to use?
Perhaps even more importantly, this project will help students practice and strengthen their skills in the 4Cs: Creative Thinking, Critical Thinking, Communication, and Collaboration. Expertise in the 4Cs will serve students for a lifetime, regardless of their specific college and career path.
The assumption is that students participating in this video game project have completed at least one previous Computing by Design project and have some (albeit limited) experience with HTML, CSS, and JavaScript. The project materials will provide references and examples to assist learners with using the Phaser JS game engine to code their games.
This video game project is based on a revision of the Fall 2016 pilot curriculum. Project revisions for Fall 2017 include (but are not limited to):
Revised and expanded practice assignments up-front to provide all students with increased knowledge and experience in Phaser JS coding, as well as with asset creation (sprites, sounds)
Phaser coding Reference section within GitBook (instructions, examples, troubleshooting, etc.) to supplement other online resources available elsewhere
Project will require team to create marketing website for game (instead of, or in addition to, promotional cards), as well as a trailer video demonstrating the gameplay
This team-based project is intended to be completed as a semester-long course (but could be shortened or extended as desired). As a semester-long course, the suggested schedule is as follows:
Research Problem Space = 4-5 weeks
Define Problem to Solve = 1-2 weeks
Design Solution & Deliver Proposal = 2-3 weeks
Build & Develop Solution = 4-6 weeks
Evaluate & Improve Solution = 1 week
Reflect & Present Project Results = 1 week
The "Research Problem Space" phase consists of individual student assignments, whereas the project team assignments begin with the "Define Problem to Solve" phase. It may be best to wait to form project teams until the end of the first phase, so that students can be partnered based on their personal gaming motivations and their areas of strength and interest (coding, art, etc.). It is important that teams are diverse yet complementary in their interests, thinking, and skills.
This project utilizes the Phaser JS game engine, which offers many advantages for learners new to creating video games. However, this project could be modified to use other available game engines. The game design process will be similar, regardless of the specific game engine used for development.
History of Video Games
Crash Course: Games (YouTube series)
Game Design Process
Fullerton, T. (2014). Game design workshop: A playcentric approach to creating innovative games (3rd ed.). Boca Raton, FL: CRC Press. LINK
Schell, J. (2015). The art of game design: A book of lenses (2nd ed.). Boca Raton, FL: CRC Press. LINK
Coding Games with Phaser.js
Phaser.io (download Phaser.js, code examples, API documentation, etc.)
Faas, T. (2016). An introduction to HTML5 game development with Phaser.js. Boca Raton, FL: CRC Press. LINK
The following Phaser coding references are provided in this GitBook:
There are many other Phaser references, examples, and tutorials available online. Here's some you may find particularly useful:
Phaser CE API documentation (provides details for every Phaser CE command)
Phaser CE code examples on Phaser.io (Phaser 2 = Phaser CE)
Phaser community tutorials on Phaser.io (older examples prior to 2018 use Phaser 2, but newer examples use Phaser 3)
Phaser game examples by Emanuele Feronato (her older examples prior to 2018 use Phaser 2, but newer examples use Phaser 3)
PHASER 3 VS. PHASER CE: Phaser 3 is the newest version of Phaser (first released in February 2018), which is maintained and updated by the folks at Photon Storm. Phaser CE is an open-source version of the previous Phaser 2, which is maintained and updated by the Phaser developer community.
While the API methods for Phaser 3 are similar to Phaser CE, code written for one will not work in the other without modification. All of the code examples in this project guidebook are based on Phaser CE. Eventually, this guidebook will be revised to use Phaser 3, which does offer improvements. However, in the meantime, be sure to use Phaser CE for this project.
This section provides instructions and starter code to create a Phaser game template (HTML, CSS, and JS files with starter code).
NOTE: Your Phaser game has to run on a web server, which means you need to have your HTML, CSS, and JS files hosted online. There are several options:
use an online code editor that allows you to upload asset files (for images & sounds), such as: repl.it, etc.
use a desktop code editor and open your HTML file in a web browser (however, some browsers prevent local JS files from loading - if so, try using Google Chrome)
use a desktop code editor and upload your files to your own web host
use a desktop code editor and create a local web server on your computer (MAMP, etc.)
Create a new folder for your game containing a new HTML file (index.html), new CSS file (style.css), and new JS file (code.js). Be sure to give your game folder a unique name.
Add a copy of Phaser (phaser.min.js) into your game folder. If necessary, download the latest version of Phaser CE from Phaser.io.
If you cannot access Phaser.io, you can download this archived version of Phaser CE (version 2.13.1 — released May 15, 2019) from Google Drive. Click the download icon to save the file to your computer (and then add a copy of the file to your game folder in your code editor).
Inside your game folder, create another folder called assets (for images, sounds, tilemaps, etc.).
RECOMMENDED: Inside the assets folder, create subfolders (with names such as images and sounds) to keep your asset files better organized.
EXCEPTION: For the Practice assignments (1-5, 1-6, 1-7), you do not need to create an assets folder. Instead, you will download a zip file of a provided assets folder, and place the extracted folder (which will contain image files and sound files) inside your game folder.
Copy-and-paste the starter code provided below into your blank HTML, CSS, and JS files. This represents the "bare bones" code needed to create a webpage with a blank Phaser game canvas.
If your HTML, CSS, or JS files already have code in them, delete the code, and replace it with the starter code provided below.
Preview your HTML file in an online web editor to verify that a blank Phaser game canvas appears (i.e., a solid black box). If so, you can start coding your game in the JS file by adding Phaser commands inside the preload()
, create()
, and update()
functions.
Be sure to add your game asset files (i.e., images, sounds, etc.) into the assets folder (or the appropriate subfolder within this folder).
Phaser will insert your game into <div id="my-game></div>
. You can change the id name, but be sure that your CSS and the Phaser.Game object in your JS use the same id name as your HTML.
Be sure that the Phaser <script>
tag is listed before your game code <script>
tag, so they will load in that order (otherwise, your game won't work).
If you want, you can add other content (text, images, etc.) to the webpage by inserting other HTML elements within the body before or after <div id="my-game></div>
.
The CSS shown above is optional, but this styling will center the game display horizontally within the webpage and add a thin black border around it.
If your CSS lists a width and height for your game, be sure it matches the width and height used to create the Phaser Game object in your JS.
If you want, you can add CSS to style other elements on the webpage around the game display (e.g., set a background color for the body, etc.).
You set the width and height (in order) of the game display when you create the Phaser.Game object. In the code above, the game display will be 800 pixels in width by 600 pixels in height. You can change these numbers for your game.
The size of the game world (which can extend beyond the game display) can be set using a separate command (not shown above).
You will need to declare global variables for certain game objects (e.g., the player, a group of enemies, the score, etc.). These global variables are traditionally listed at or near the top of your JS code before the preload()
function.
If desired, you can avoid global variables by creating variables inside the game object functions using the this
keyword. (However, if you are a beginning programmer, it will be simpler to just use global variables.)
A basic Phaser game has 3 core functions: preload()
, create()
, and update()
. You will add your own Phaser code inside these functions:
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 to be used. The preload()
function runs one time at the start of the game (i.e., when the webpage is loaded or refreshed).
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 input 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, 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.
Most Phaser games will also have custom functions that you create to do certain things based on conditions or events that occur in the game (such as the player colliding with an enemy, etc.). These custom functions are traditionally listed at the bottom of your JS code after the update()
function.
GAME STATES: Phaser allows you to break up your game into different states. For example, you could have separate game states for: loading, menu, level 1, level 2, level 3, game over, etc. This template is for a single-state game, which is simpler to code if you are new to creating games.
After preparing your Phaser game template, you should see a solid black game canvas when you preview your webpage in an online web editor.
PHASER NOT LOADING? If you only see a black outline (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 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 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. Also be sure the filename listed in the second HTML <script>
tag matches the actual name of the JS file containing your game code. For example, instead of code.js, you could use a different name for your JS file, such as: script.js or game.js (etc.)
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, repl.it, etc.).
Check your browser's JavaScript console to see if there are errors listed.