Using Collision Detection to Make Your Game Character Jumpby@eugene-kleshnin

# Using Collision Detection to Make Your Game Character Jump

March 12th, 2023

This is Part 3 of the series, where I learn to create a simple platformer game using Flame Engine. In this part, we’re gonna add vertical movement with the help of collision detection. In the last parts, we created a game level with a parallax background, added a player character, and taught him to run.

### People Mentioned

This is Part 3 of the series, where I learn to create a simple platformer game using

Flame Engine.

In the last parts (1, 2), we created a game level with a parallax background, added a player character, The Boy, and taught him to run. Of course, any platformer would be boring without jumping, so in this part, we’re gonna add vertical movement with the help of collision detection.

The first thing we need to do is to add the force that constantly pulls the player to the ground.

I need to say, that if your game requires complex physics, it might be worth exploring Forge2D - the physics engine module for Flame. It deserves a series of its own, so to keep it simple in this series we’ll replicate gravity with just the means of Flame.

Add several new constants to the top of the `TheBoy` component:

``````final double _gravity = 15; // How fast The Boy gets pull down
final double _jumpSpeed = 500; // How high The Boy jumps
final double _maxGravitySpeed = 300; // Max speed The Boy can have when falling
``````

Now, let’s apply it to the player’s y velocity by adding these two lines to the `update` method, right after setting `velocity.x`:

``````_velocity.y += _gravity;
_velocity.y = _velocity.y.clamp(-_jumpSpeed, _maxGravitySpeed);
``````

The first line increments the vertical velocity of The Boy each game cycle by the `_gravity` amount. The second line limits the velocity to be between the `_jumpSpeed` and `_maxGravitySpeed` in order to avoid unlimited acceleration.

If we run the game now, we’ll see that The Boy falls through the ground. At this point, it’s the expected behavior, because the game doesn’t know yet, that the ground should be solid. For the game engine, our level is just a set of sprites, and we need to tell it, which tiles are the platforms. Luckily, it could be done, with the Tiled editor, which we used to create the level in Part 2.

Return to the Tiled. As you may have noticed, a Tiled map has different layers, similar to Adobe Photoshop. We already have a Tile layer, that contains tiles for our level. Now, we’ll add a new Object layer, which will represent the boundaries of our platforms. Name it “Platforms”

Now, select this layer, and using the Rectangle tool, draw rectangles where you placed your platforms and ground tiles. Once you’re finished, if you hide your Tile layer, it should be like this:

What we did, is we added the “invisible” layer, that holds the information on the position of the platforms. Next, we’ll use the game engine to translate those into actual components, that we can later use for collision detection. Save the level file and return to the IDE.

Let’s add the component that represents the platform to the `objects` folder:

``````class Platform extends PositionComponent {

Platform(Vector2 position, Vector2 size) : super(position: position, size: size);

@override
}
}
``````

Now, go to the `game.dart` and add a new method:

``````void spawnObjects(RenderableTiledMap tileMap) {
final platforms = tileMap.getLayer<ObjectGroup>("Platforms");

for (final platform in platforms!.objects) {
}
}
``````

And call it from the `onLoad` after we added our level component:

``````spawnObjects(level.tileMap);
``````

Let’s go through what happened here. We took our tilemap object and fetched the layer we just created by its name. Be aware that the key here should exactly match the layer name in Tiled: `tileMap.getLayer<ObjectGroup>("Platforms")`.

Then, we iterate through all the objects in this layer, and for each of them create a `Platform` component with the same position and size.

Now our game is populated with `Platforms`, that correspond to the platform tiles the player sees. However, they do nothing for now, and for them to work we need to add collision detection.

## Detect collisions

Collision detection is a very common problem in game dev. Essentially, it means that when two game objects intersect, we want to trigger some events. For example, a bullet hits the target, two cars crash into each other, or The Boy lands on a platform.

The Flame engine provides a useful API to handle collision detection. First, let’s do some prep work. Open `game.dart` and add `HasCollisionDetection` mixin to tell the engine that we want to track collisions:

``````class PlatformerGame extends FlameGame with HasKeyboardHandlerComponents, HasCollisionDetection
``````

Then, go to the `Platform` class and add a hitbox, the component’s area we want to use for collision detection:

``````@override
}
``````

There’re three types of collisions:

• Active - can collide with other hitboxes of Active and Passive type
• Passive - can collide only with Active hitboxes
• Inactive - collision detection is disabled

Since platforms will only collide with The Boy, their type is passive.

Let’s go to `theboy.dart` and add a hitbox for him as well to the end of `onLoad`:

``````add(CircleHitbox());
``````

Here we’re using a Circle hitbox because it fits the shape of the character better, but also it’d be easier to detect the collision for this shape. But more about it later.

Add the `CollisionCallbacks` mixin to `TheBoy`:

``````class TheBoyPlayer extends SpriteAnimationComponent
with KeyboardHandler, CollisionCallbacks, HasGameRef<PlatformerGame>
``````

And override the method `onCollision`

``````@override
void onCollision(Set<Vector2> intersectionPoints, PositionComponent other)
``````

This method will be triggered every time `TheBoy` component collides with another component.

The first param is the points where two components intersect, and the second param is the component `TheBoy` collides with.

The collision handling method I will use is the one described in the DevKage’s tutorial (link at the end of the story) with slight improvements. I should mention, that there are a bunch of ways a collision could be resolved. I’m gonna use a rather simple one that does a decent job, but of course, it could be improved further.

The collision of the player’s circle hitbox and the platforms’ rectangular hitbox at the moment `onCollision` is called, could be schematized like this:

The problem here is that hitboxes intersect and in each game loop, the player will continue to sink into the platform. To avoid that, we need to calculate how deep the circle hitbox sank into the rectangular and move it up the same amount of pixels. That will create the illusion of the player standing on the platform.

We know the circle radius, and intersection points, so we can calculate the penetration depth:

``````@override
void onCollision(Set<Vector2> intersectionPoints, PositionComponent other) {
if (other is Platform) {
if (intersectionPoints.length == 2) {
final mid = (intersectionPoints.elementAt(0) +
intersectionPoints.elementAt(1)) / 2;

final collisionVector = absoluteCenter - mid;
double penetrationDepth = (size.x / 2) - collisionVector.length;

collisionVector.normalize();
position += collisionVector.scaled(penetrationDepth);
}
}

super.onCollision(intersectionPoints, other);
}
``````

First, we calculate the middle point between the points of intersection (`mid`). Then, using the circle center and `mid` we calculate the collision vector - the direction The Boy moves towards the platform. Next, using the radius (`size.x / 2`) and length of `collisionVector` we calculate how deep the circle hitbox is penetrating the rectangular platform. Finally, we normalize the vector to have just the direction of the collision and update the player’s position by `penetrationDepth` multiplied by the normalized vector to keep the direction.

If we run the game, The Boy doesn’t fall through the platform anymore. And if he moves forward, the wall stops him.

## Jumping

Let’s add jumping. First, we need to detect if the arrow key was pressed. Add a new variable to the `TheBoy` class:

``````bool _hasJumped = false;
``````

Modify the `onKeyEvent` method and add the following to the bottom:

``````_hasJumped = keysPressed.contains(LogicalKeyboardKey.keyW) || keysPressed.contains(LogicalKeyboardKey.arrowUp);
``````

Next, edit the `update` method and add this right after applying gravity:

``````if (_hasJumped) {
_velocity.y = -_jumpSpeed;
_hasJumped = false;
}
``````

Here we check if the arrow key was pressed, and update The Boy’s y velocity by the `_jumpSpeed`

If you run the game now, you’ll see that The Boy learned how to jump! The only problem is that if you press the arrow key mid-jump, he will jump again. We need to fix that.

We’re gonna return to the `onCollision` method and save the component The Boy is standing on. Then, when the collision ends, we’re gonna clear this reference. Lastly, we’re gonna check if the reference is not empty before applying our jump logic. This will allow The Boy to jump only when there’s an active collision with the ground.

Add these variables to the `TheBoy` class:

``````Component? _standingOn; // The component The Boy is currently standing on
final Vector2 up = Vector2(0, -1); // Up direction vector we're gonna use to determine if The Boy is on the ground
final Vector2 down = Vector2(0, 1); // Down direction vector we're gonna use to determine if The Boy hit the platform above
``````

Go to `onCollision` method and add the following right after `collisionVector` normalization:

``````if (up.dot(collisionVector) > 0.9) {
_standingOn = other;
}
``````

Here we check if The Boy is colliding with the platform below him and save the reference to that component.

Next, add `onCollisionEnd` method implementation, which will be triggered every time The Boy stops colliding with a platform:

``````@override
void onCollisionEnd(PositionComponent other) {
if (other == _standingOn) {
_standingOn = null;
}
super.onCollisionEnd(other);
}
``````

Finally, modify the jumping logic:

``````if (_hasJumped) {
if (_standingOn != null) {
_velocity.y = -_jumpSpeed;
}
_hasJumped = false;
}
``````

Cool, The Boy should be allowed to jump only once now. But there’s one more improvement we can add. If you jump below a platform, you’ll notice that The Boy hangs for a second. To avoid that, we’re gonna check if The Boy is colliding with a platform on the top, similarly as we did with the ground. Then we’re gonna bounce The Boy by modifying his vertical velocity.

``````if (up.dot(collisionVector) > 0.9) {
_standingOn = other;
} else if (down.dot(collisionVector) > 0.9) {
_velocity.y += _gravity;
}
``````

Well done! Now The Boy can jump and fall.

## Update jump animation

One little improvement we can add, that greatly improves the look of the game is using different animations for jumps. Let’s do that.

Add `_jumpAnimation` and `_fallAnimation` to `onLoad` method, similar to what we’ve done for idle and run animations:

``````_jumpAnimation = SpriteAnimation.fromFrameData(
game.images.fromCache(Assets.THE_BOY),
SpriteAnimationData.range(
start: 4,
end: 4,
amount: 6,
textureSize: Vector2.all(20),
stepTimes: [0.12],
),
);

_fallAnimation = SpriteAnimation.fromFrameData(
game.images.fromCache(Assets.THE_BOY),
SpriteAnimationData.range(
start: 5,
end: 5,
amount: 6,
textureSize: Vector2.all(20),
stepTimes: [0.12],
),
);
``````

Then, let’s modify `updateAnimation` method to include new animations:

``````void updateAnimation() {
if (_standingOn != null) {
if (_horizontalDirection == 0) {
animation = _idleAnimation;
} else {
animation = _runAnimation;
}
} else {
if (_velocity.y > 0) {
animation = _fallAnimation;
} else {
animation = _jumpAnimation;
}
}
}
``````

Here, we check if The Boy is standing on the ground (`_standingOn != null`), then we use the run or the idle animation. Otherwise, we check if The Boy jumps or falls by checking the sign of the vertical velocity and then we apply the appropriate animation.

Awesome! The animations look much better.

## Summary

That’s it for Part 3. We learned how to add simple physics and collision detection. The Boy now can jump and the game is starting to take its final shape. In the last part, we’ll add Coins - another type of object The Boy can interact with. And also we’ll add a HUD to display the collected Coins.

Other stories of the series:

The complete code for this part you can find on my github

## Resources

At the end of each part, I’ll be adding a list of awesome creators and resources I learned from.

L O A D I N G
. . . comments & more!