If you've gone through the basic first game in modern JavaScript or the infinite jumper book then you've got the basics down so let's try making something a little bit more complicated!
We suggest Memory Match. A Mario Party-inspired memory game where you control a character to pick boxes until all matches are found within a limited amount of time.
For part 3, we are going to start selecting boxes and revealing what is inside of them.
We also have a video version on YouTube if you prefer to watch or want to see how it is coded in real-time.
When a Box Can Be Opened
The player will be able to open a box by pressing the space key. But first, we need to know which box the player wants to open.
There's a couple of ways to do this and we will take the simplest approach by designating a box active when the player collides with it.
Update the code that creates a collider like this:
|
|
We broke this line up into multiple lines for ease of reading. You are free to keep it as one line if you prefer.
The main thing to notice is the addition of the this.handlePlayerBoxCollide
callback and the passing of this
as the last argument.
This will allow Phaser to call our handlePlayerBoxCollide()
method with the context set to this
so that we can continue to reference properties on the Scene.
Next, let's create the handlePlayerBoxCollide()
method:
|
|
This method simply sets the passed in box
–which represents the box the player collided with–and sets it as the activeBox
. Then it changes the frame to 9
so that it is displayed as a green box instead of the gray one.
Notice that activeBox
is a class property. Remember to add it as a property declaration like this:
|
|
Run into a box and you'll see it turn green. ✅
Now, we need to change it back to being gray when the player is too far and can no longer open it.
When a Box is No Longer Active
We will consider an active box to no longer be active when the distance between the player and the activeBox
is greater than 64px
.
Let's start by adding an updateActiveBox()
method.
|
|
First, we early exit and do nothing if this.activeBox
is not set. This happens on lines 3 - 6.
Then we get the distance between the player and the currently active box. We'll do nothing if that distance is less than 64.
Once the distance is 64 or greater, we set the activeBox
back to using frame 10
and set this.activeBox
to undefined
to designate that no box is currently active.
Give this a try by adding a call to updateActiveBox()
in the update()
method after the player movement code.
|
|
You should now see that touching a box sets it to be active and then walking away sets it to not active.
What's in a Box?
We'll need to have something in each box before we open them or there will be nothing to show!
There are many ways to do this as well and we will use the simplest approach of creating a 2D array at the top of Game.js
that defines what is inside each box.
Create a level
variable after the module imports like this:
|
|
Each unique number in this 2D array represents a different animal that will come out of the box.
You can use strings if that is easier to understand but it is often easier to visually align a 2D array with numbers because words can vary widely in length.
For numbers to work, we just need to agree that 0
is a Bear, 1
is a Chicken, 2
is a Duck, 3
is a Parrot, and 4
is a Penguin.
Next, let's change how we create the boxes to use the level
array instead of a hard-coded row and column size.
In the createBoxes()
method:
|
|
We replaced the hard-coded 3
in the for
loops to use level.length
and level[row].length
respectively.
Then we call setData()
on each created box and give the 'itemType'
key the value from level[row][col]
. This value is the number representing which animal should be revealed by the box.
The 'itemType'
key is just something that we decided to use. You can use any string with setData()
to store arbitrary data with a GameObject
.
Preload Animal Textures
We'll need to preload each texture for the 5 different animals that can be revealed by a box.
Download the Animal Pack Redux by Kenney here if you haven't already.
You'll need these images:
- bear.png
- chicken.png
- duck.png
- parrot.png
- penguin.png
Pick out the variants that you like best. There's round, square, outlined, and others.
Then add them to the Preloader
Scene like this:
|
|
Opening a Box
Now that each box knows what it can reveal, we can add logic to open a box.
Start by adding this openBox(box)
method:
|
|
This code is fairly straight-forward. We get the value stored in the 'itemType'
key of each box. Then we use a switch
statement to create the appropriate Sprite
for the given type.
Next, we want to specify that the box
is opened and then tween the item
from a scale
and alpha
of 0
to 1
so that it appears with a bit a fanfare.
|
|
We need to call this method to see it in action so let's make pressing the space key call openBox()
.
Add this to the update()
method right above the this.updateActiveBox()
line:
|
|
Now when you make a box active and press space, an item will animate-in and become revealed. 🎉
Quick Code Clean Up
We'll wrap up with some quick code clean-up. You may have noticed that instead of creating a new Sprite
each time a box is opened we can reuse Sprites
by using a Group
.
Recall that we used a Group
for boxes to make it easier to add colliders but reusing GameObjects
was another benefit.
We can make the change by creating a new class property called itemsGroup
like this:
|
|
Next, update the switch
statement handling itemType
to look like this:
|
|
Take note that we are using item.setTexture()
instead of passing in the texture key to this.itemsGroup.get()
. We do this because items are being reused and this ensures the actual texture we want will be used and not the previously used texture.
The second item to improve is the update()
method as it looks a bit cluttered now.
Let's simplify it by moving the player movement logic into a separate method so that update()
is easier to read.
|
|
Then change update()
to look like this:
|
|
Next Steps
Items are being revealed but we don't have any logic to check if a match was made.
In part 4, we will check for matches and then handle what happens when there is a match and when there is no match.
Be sure to sign up for our newsletter so you don't miss any future Phaser 3 game development tips and techniques!
Drop your email into the box below.
Don't miss any Phaser 3 game development content
Subscribers get exclusive tips & techniques not found on the blog!
Join our newsletter. It's free. We don't spam. Spamming is for jerks.