Creating a Catching Game Using Phaser

Claudia Sinowato
The Startup
Published in
5 min readAug 30, 2020

Play here: https://hungry-dog.herokuapp.com/

During a four-day hackathon project, I created a simple food catching game written in JavaScript, using Phaser. Phaser is a 2D game framework that can be used to create HTML games. This article will cover some of the main aspects of creating such a game, namely moving the player, and managing collisions.

Player Movements

The main game assets are the sprites: the player sprite and the falling object sprites. To add animation to the main character, I used the following spritesheet and split it into frames:

Source: Redhead_Pueppi via Pixabay

Each scene in Phaser can be broken down into three parts: load, create, and update. In the load function, we can load the spritesheet as follows:

We have to specify the height and width of the frame so that Phaser knows how to split the spritesheet when we create the animations. In the create function, we can then call the following helper function:

This selects the frames to be played for certain movements. For example, on line 4, the “left” movement was created using the first two frames on the spritesheet, and on line 17, the “right” movement was created using the last two frames of the spritesheet. Note: instead of having a right-facing sprite, we also could have taken the left-facing sprite and use Phaser’s flipX component to horizontally flip the image.

We can then use the helper function above to update the player’s movements. This function takes cursors as a parameter and moves the sprite based on the cursors pressed. The first argument of this.play is the key that was defined in the createAnimations helper function. Lines 6, 13, and 24 play the previously defined animations.

Collisions

Collisions are key! In order for objects to interact with each other, we need to set collisions between them. There are two main steps to do this:

  • Enable physics
  • Add colliders

For each sprite, I created a class that extends Phaser.Physics.Arcade.Sprite. In each class’s constructor, I added the following code:

this.scene.physics.world.enable(this);

This enables physics on that particular entity, allowing us to use gravity and collisions on it. This allows the objects to fall, bump into each other, disappear upon impact etc. If physics were disabled, objects would just remain statically in the air. For this game, I wanted the main player to land on the ground object, so I added the following collider.

this.physics.add.collider(this.player, this.groundGroup);

This code allows the player to walk and jump on the ground object. Without this collider, the player would fall through the ground.

For this game, I wanted the fruits and vegetables to fall off the grid if the player doesn’t catch them. For this reason, no collider was added between the food objects and the ground.

Tips for Collisions

Tip#1: Set debug:true

Collisions can be very tricky! A tip for detecting collision issues is to set debug:true in the Phaser configuration file. This creates a box around each object so we can see if the objects are actually colliding with each other.

Tip#2: Check image margins

A problem that I faced during this project, was that even though a collider was added between two objects, the two objects were still falling through each other. After turning on debug mode, I realized that even though the objects appeared to be colliding, the actual image margin of one of the objects was very large but transparent. So the transparent part of one object was actually already overlapping with the second object. Since they were already overlapping, they couldn’t collide! Basically, double check the image margins since they could be transparent and overlapping!

Tip#3: refreshBody( )

When we scale an object, we need to make sure the changes are updated. refreshBody syncs the position and size of an object with its parent object. If debug mode is enabled, we can see that this syncs the collision boxes. For this project, the ground was made smaller, so refreshBody was called during its creation.

this.groundGroup.create(x, y, "ground").setScale(0.6).refreshBody();

If refreshBody was not called, the image would shrink, but the size of the collision box would remain at the original size, so the objects would not collide properly. As shown below, Phaser thinks the player is colliding with the ground object, but the player is actually jumping in the air!

Falling Objects

The first step in generating falling objects is to load and create the items. For this game I created a group of edible food for the player to catch, and a group of inedible food for the player to avoid. In order to make the game more dynamic, I made the object selection random, as shown below:

The speed of the object fall can be controlled using gravity. For this project, I set the gravity in the config file as follows:

gravity: {y:200}

This is the speed that game objects are pulled down along the y-axis. The higher the number, the faster the objects will fall.

Finally, I also wrote the helper function to continuously drop items from random coordinates. The game window width is 1000, so line 2 causes the items to fall randomly from x-coordinates between 2 and 970. To make the game more challenging, rather than have the player collect all items, I set it so that edible food would be dropped ⅔ of the time and inedible food would be dropped ⅓ of the time.

Removing Caught Items

What happens after an object is caught? The object is removed and the score and life counts are updated. In order to remove the object, we first need to add overlap to disable the object on collision.

In this game, the item is disabled and removed inside the collectEdibles helper function shown below.

Final Touches

Finally, in order to make the game more varied, I added multiple levels that the user can select from the home page. To make the game more difficult, I decreased the time delay between each item drop, so that even though the gravity is unchanged, in the harder levels more fruits and vegetables are falling at the same time.

Thanks for reading!

--

--