This is Part 3 of the series, where I learn to create a simple platformer game using Flame Engine. In the last parts ( , ), 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. 1 2 Adding gravity 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 - 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. Forge2D Add several new constants to the top of the component: TheBoy 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 method, right after setting : update 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 amount. The second line limits the velocity to be between the and in order to avoid unlimited acceleration. _gravity _jumpSpeed _maxGravitySpeed 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. Adding platforms 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 folder: objects class Platform extends PositionComponent { Platform(Vector2 position, Vector2 size) : super(position: position, size: size); @override Future<void> onLoad() async { return super.onLoad(); } } Now, go to the and add a new method: game.dart void spawnObjects(RenderableTiledMap tileMap) { final platforms = tileMap.getLayer<ObjectGroup>("Platforms"); for (final platform in platforms!.objects) { add(Platform(Vector2(platform.x, platform.y), Vector2(platform.width, platform.height))); } } And call it from the after we added our level component: onLoad 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 component with the same position and size. Platform Now our game is populated with , 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. Platforms 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 and add mixin to tell the engine that we want to track collisions: game.dart HasCollisionDetection class PlatformerGame extends FlameGame with HasKeyboardHandlerComponents, HasCollisionDetection Then, go to the class and add a hitbox, the component’s area we want to use for collision detection: Platform @override Future<void> onLoad() async { add(RectangleHitbox()..collisionType = CollisionType.passive); return super.onLoad(); } 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 and add a hitbox for him as well to the end of : theboy.dart 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 mixin to : CollisionCallbacks 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 component collides with another component. TheBoy The first param is the points where two components intersect, and the second param is the component collides with. TheBoy The collision handling method I will use is the one described in the (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. DevKage’s tutorial The collision of the player’s circle hitbox and the platforms’ rectangular hitbox at the moment is called, could be schematized like this: onCollision 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 ( ). Then, using the circle center and we calculate the collision vector - the direction The Boy moves towards the platform. Next, using the radius ( ) and length of 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 multiplied by the normalized vector to keep the direction. mid mid size.x / 2 collisionVector penetrationDepth 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 class: TheBoy bool _hasJumped = false; Modify the method and add the following to the bottom: onKeyEvent _hasJumped = keysPressed.contains(LogicalKeyboardKey.keyW) || keysPressed.contains(LogicalKeyboardKey.arrowUp); Next, edit the method and add this right after applying gravity: update 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 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. onCollision Add these variables to the class: TheBoy 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 method and add the following right after normalization: onCollision collisionVector 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 method implementation, which will be triggered every time The Boy stops colliding with a platform: onCollisionEnd @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. Add else branch to the condition we added earlier: 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 and to method, similar to what we’ve done for idle and run animations: _jumpAnimation _fallAnimation onLoad _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 method to include new animations: updateAnimation 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 ( ), 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. _standingOn != null 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: Teaching your character to run in Flame Designing your level in Flame 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. GrafxKid’s Arcade platformer assets Tiled Editor documentation DevKage’s Flame Game Development Series: https://www.youtube.com/watch?v=mSPalRqZQS8&feature=youtu.be Craig Oda’s channel: https://youtu.be/hwQpBuZoV9s Ember Quest Game Tutorial Flame Engine documentation