When it comes to game development, a lot of games will require interaction between the player and the environment or the player and another player. For example, you’d probably want to prevent your player from falling through floors or make them lose health every time an opponent collides with them.
There are numerous strategies towards handling collisions in your game. You could identify the position and bounding boxes for every sprite in your game, something I don’t recommend, or you could make use of the physics engine that’s available with your game development framework.
The past few tutorials have focused on Phaser, so we’re going to proceed with developing a game using that framework.
In this tutorial, we’re going to look at using arcade physics, one of several physics engines available in Phaser 3.x, and we’re going to see how to handle collisions.
To get an idea of what we’re going to accomplish, take a look at the following animated image:
In the above image, we’re working with two sprites, one of which we already saw in a previous tutorial titled, Animate a Compressed Sprite Atlas in a Phaser Game. Essentially we have our player, the plane, and a totally random obstacle. The obstacle moves towards the plane and with it touches the collision boundaries, an event happens on the plane. The bounding boxes are visible for demonstration purposes.
With arcade physics in Phaser 3.x, you can use boxes or circles for collision boundaries. In a future tutorial we’re going to see how to use more tightly defined collision boundaries using another physics engine, but for now we’re sticking to arcade physics. The benefits of arcade physics is that it is very easy to use and will give you the best performance in your game.
If you haven’t already checked out the previous tutorial around animating the plane, I encourage you to do so. The code from that tutorial will not be thoroughly explained in this tutorial.
Before we start worrying about our physics engine and the collision details that come with it, we’re going to get our project ready to go. Create a directory on your computer and include an index.html file with the following markup:
<!DOCTYPE html>
<html>
<head>
<script src="//cdn.jsdelivr.net/npm/phaser@3.24.1/dist/phaser.min.js"></script>
</head>
<body>
<div id="game"></div>
<script>
const phaserConfig = {
type: Phaser.AUTO,
parent: "game",
width: 1280,
height: 720,
backgroundColor: "#5DACD8",
scene: {
init: initScene,
preload: preloadScene,
create: createScene,
update: updateScene
}
};
const game = new Phaser.Game(phaserConfig);
var plane, obstacle;
var isGameOver = false;
function initScene() { }
function preloadScene() {
this.load.atlas("plane", "plane.png", "plane.json");
this.load.image("obstacle", "obstacle.png");
}
function createScene() {
this.anims.create({
key: "fly",
frameRate: 7,
frames: this.anims.generateFrameNames("plane", {
prefix: "plane",
suffix: ".png",
start: 1,
end: 3,
zeroPad: 1
}),
repeat: -1
});
this.anims.create({
key: "explode",
frameRate: 7,
frames: this.anims.generateFrameNames("plane", {
prefix: "explosion",
suffix: ".png",
start: 1,
end: 3,
zeroPad: 1
}),
repeat: 2
});
}
function updateScene() {}
</script>
</body>
</html>
The above code configures our Phaser 3.x game and initializes some of our media assets as well as animations. If you’re not sure what a sprite atlas is, check out my previous tutorial. While I did include the spritesheet and atlas in my other tutorial, go ahead and create any kind of image that you want for the obstacle. In fact, you don’t need to be using any animated sprites at all for this example. Just come up with two different image files and you’ll be fine progressing through this tutorial.
If you ran the game right now, you should end up with a blue screen because we’re not showing any of our media assets and the animations we created are attached to nothing.
With the foundation of our game in place, we can start creating sprites from our image assets. However, we’re not going to create the sprites the same as we’ve seen in other tutorials. Instead we need to create physics sprites.
Within the index.html file, change your phaserConfig
to look like the following:
const phaserConfig = {
type: Phaser.AUTO,
parent: "game",
width: 1280,
height: 720,
backgroundColor: "#5DACD8",
physics: {
default: "arcade",
arcade: {
debug: true
}
},
scene: {
init: initScene,
preload: preloadScene,
create: createScene,
update: updateScene
}
};
We’ve enabled our default physics engine and we’ve enabled debug mode. With debug mode enabled, we can see our collision boundaries on each of our objects. When you publish your game, just disable debug mode so those boxes or circles disappear.
So we’ve enabled arcade physics, now we can add our sprites. Within the createScene
function of our index.html file, add the following:
function createScene() {
// Animations ...
plane = this.physics.add.sprite(300, 360, "plane");
plane.play("fly");
obstacle = this.physics.add.sprite(1100, 360, "obstacle");
}
Like I said, we’re not creating our sprites how we’ve done it in other tutorials. However, creating a physics enabled sprite isn’t much different. The above code creates two different sprites with no physics beyond the boxed collision bodies. This means there is no gravity, no friction, nothing that you’d expect from game related physics.
Had we wanted to use a circle boundary instead of a box boundary, we could have done something like this:
plane = this.physics.add.sprite(300, 360, "plane");
plane.setCircle(300);
plane.play("fly");
You would call the setCircle
method on the sprite and specify the size of the circle. This would replace the default bounding box with the circle.
Now that we have active collision bodies with the arcade physics on our sprites, we can monitor for when the collisions happen. If we did absolutely nothing right now, when the sprites collide, they’d just pass through each other and we wouldn’t even know a collision happened. If we had other physics data set, collisions might stop the movement due to friction, resistance, etc., but not for this example.
Before we start looking at the collision event, let’s make our obstacle move.
Within the updateScene
function of our index.html file, include the following:
function updateScene() {
obstacle.x -= 4;
}
The updateScene
function is constantly called by Phaser, so every time it is called, we decrease the obstacle horizontal position. This will simulate movement in the left direction.
Go ahead and test it out.
Now let’s figure out when the collision happens.
In the createScene
function of our project, include the following:
function createScene() {
// Animations ...
plane = this.physics.add.sprite(300, 360, "plane");
plane.play("fly");
obstacle = this.physics.add.sprite(1100, 360, "obstacle");
this.physics.add.collider(plane, obstacle, function (plane, obstacle) {
if (!isGameOver) {
plane.play("explode");
plane.once(Phaser.Animations.Events.SPRITE_ANIMATION_COMPLETE, () => {
plane.destroy();
});
isGameOver = true;
}
});
}
Notice that we’re using the collider
method in the above code. We’re specifying what should happen when two different sprites collide. When they collide, the callback function is executed, which passes the two colliding sprites into the function. The names of the callback parameters do not need to match the variable names of the sprites, but I did this for readability purposes.
The collider
method will trigger for as long as the two sprites are touching, and this includes when they pass through each other. For this reason we don’t want to continuously replay our animation or try to destroy a sprite that might not exist. This is why we’re making use of a isGameOver
boolean variable. If we’ve just now collided for the first time, play the new animation and toggle the boolean. When the animation is done, remove the sprite.
How you work with collisions is up to you. You could decrease player health, increase score, or do something else.
You just saw how to handle collisions in a Phaser 3.x game that made use of arcade physics. Phaser offers compatibility with quite a few physics engines, but the arcade physics engine is rated to be the most efficient at the sacrifice of using only bounding boxes and circles for collision bodies. This tutorial expanded on a previous tutorial I wrote around animated sprite atlases, hence why I didn’t go into details on the animation component.
While I didn’t show it in this tutorial, there is also an overlap
method that behaves similar to the collider
method. Depending on the functionality you want, it might be worth checking out.
In a future tutorial we’re going to explore a different physics engine and see how we can get more detailed collision boundaries that aren’t restricted to boxes and circles.