This is the second part of my Platformer 101 with Flame Engine series. In , we learned how to create a Flame game project, load assets, and make our character, The Boy, run. However, for now, it’s an empty screen, so let’s fix that in this part. We’re gonna design a game level, add camera movement, and parallax background. the previous part Creating a game level If you remember from the last part, we store our game assets as a set of tiles, each of them representing a single cell in the game world. Now we need to design the level itself, telling the game where to draw ground, platforms, or other game objects. So we need a place that stores that information. It could be as easy as an array of integers, that stores grid position and tile type. But usually games store level information in separate files, using JSON, XML, or even custom format - then the game parses the level file and translates it to game objects. Then you can go even further and create a level editor, that understands the level format you picked, so it’d be very easy to create new levels and modify existing ones. Luckily, such tools already exist, one of the most prominent ones is - UI level editor with its own format, based on XML. It supports a lot of game engines, including Flame. Let’s Tiled and create our game map. Tiled download After it’s downloaded, click New Map. These are the settings for my map. One thing: it’s better to use tiles of target size, as if you want to scale the map in the Flame itself, it might end up being blurry. Next, click New tileset, which will store tiles used in our map. Save both files to the folder of your project. assets/tiles Next, drag and into your tileset and then open the map tab. Using tiles from the tileset draw the level how you like it. Mine looks like this: ground.png platform.png Don’t worry about the gray background, we’ll add it later in the game code. Save changes, and return to the IDE. Now we need to tell the game to load the level we just created. First, open the . We need to do two things here: to include dependency and specify the path to our level in the assets section. Then press the button. pubspec.yaml flame_tiled pub get dependencies: flutter: sdk: flutter flame: ^1.6.0 flame_tiled: ^1.9.1 flutter: assets: - assets/images/ - assets/tiles/ Then go to file and load our level file in method, before adding component: game.dart onLoad TheBoy final level = await TiledComponent.load("level1.tmx", Vector2.all(64)); mapWidth = level.tileMap.map.width * level.tileMap.destTileSize.x; mapHeight = level.tileMap.map.height * level.tileMap.destTileSize.y; add(level); The first param is the name of the level we want to load and the second is the size of a single cell in our game grid. You can put any value here, but again if you set a value bigger than the size of your tiles assets, they will be scaled and might not look very crispy. It’s better to have the tiles in the desired size. Also, create two class variables and that represents the size of our level. We’ll use these variables later. mapWidth mapHeight If you run the game now, depending on your map setup you probably won’t see anything except the player character. That’s because the game size is now much bigger due to the added , and you don’t see it completely. It could be fixed by setting a viewport of the game camera. Wait, what's a camera? TiledComponent Controlling the camera Imagine a Super Mario bros level. When the character reaches a certain point, the screen scrolls as if the camera was following the player. That’s exactly it: the game level could be any size, but we need to show only a small portion of it and the viewport is responsible for telling the game what portion of our level we want to show. So let’s add this to the bottom of our method: onLoad camera.viewport = FixedResolutionViewport(Vector2(1920 , 1280)); If you run the game you should see this. We put this resolution intentionally, to see the whole level for now. Now let’s move The Boy’s position to the left corner of our level. final theBoy = TheBoy( position: Vector2(128, mapHeight - 64), ); add(theBoy); Next, let’s zoom our camera a little bit, so the player won’t see the whole level at once: camera.zoom = 2; Finally, let’s make our camera move the player character so that when they reach the side of the screen, the level scrolls. camera.followComponent(theBoy, worldBounds: Rect.fromLTWH(0, 0, mapWidth, mapHeight)); The second parameter will stop our camera from moving beyond level bounds when the player reaches the edge of the level. worldBounds The last thing we want to do is to restrict The Boy from moving away from the screen. It can be easily done, by setting the x velocity to 0, if our component’s position is around the edge of the screen. Let’s add two methods, to check for that: TheBoy bool doesReachLeftEdge() { return position.x <= size.x / 2 && _horizontalDirection < 0; } bool doesReachRightEdge() { return position.x >= game.mapWidth - size.x / 2 && _horizontalDirection > 0; } Easy enough, we check if the player is facing the edge and if their x position is the same as the edge coordinate. One thing, we need to account for the sprite size, otherwise, the player will be able to move out of the screen by the half-sprite size distance (because the sprite’s anchor point is in the center). Now, we just restrict the movement, if the player has reached one of the screen edges: @override void update(double dt) { super.update(dt); if (doesReachLeftEdge() || doesReachRightEdge()) { _velocity.x = 0; } else { _velocity.x = _horizontalDirection * _moveSpeed; } ... } Awesome, our game looks a lot better! But we can make it even better by adding a nice background. Parallax background What makes side-scrolling game graphics really stand out is a parallax background. It’s a technique, when objects in the foreground move faster than the objects in the background, creating an illusion of depth. Luckily Flame has tools to easily add a parallax background to your game. Let’s create a new file in folder: background.dart lib/objects class ParallaxBackground extends ParallaxComponent<PlatformerGame> { ParallaxBackground({required super.size}); @override Future<void> onLoad() async { final clouds = await game.loadParallaxLayer( ParallaxImageData(Assets.CLOUDS), velocityMultiplier: Vector2(1, 0), fill: LayerFill.none, alignment: Alignment.topCenter, ); final mist = await game.loadParallaxLayer( ParallaxImageData(Assets.MIST), velocityMultiplier: Vector2(2, 0), fill: LayerFill.none, alignment: Alignment.bottomCenter, ); final hills = await game.loadParallaxLayer( ParallaxImageData(Assets.HILLS), velocityMultiplier: Vector2(3, 0), fill: LayerFill.none, alignment: Alignment.bottomCenter, ); positionType = PositionType.viewport; parallax = Parallax( [clouds, mist, hills], baseVelocity: Vector2.all(10), ); } } Here, we’re creating three parallax layers that will be moving at different speeds. There are several parameters that we can configure each layer with. - how fast the layer will be moving compared to the of the . The further the layer from the foreground, the higher the value should be. velocityMultiplier baseVelocity ParallaxComponent - if we want to scale the sprite to fill the viewport fill - position of the layer on the game screen. With the current version of Flame, you only have options to attach the layer to the top, center, or bottom of the screen. If you want more fine-tuning, you need to add paddings to the asset itself. Another option would be to have several nested . alignment ParallaxComponents With that done, let’s add our component to the game tree, before other components: ParallaxBackground add(ParallaxBackground(size: Vector2(mapWidth, mapHeight))); And also, let’s override method in , to have a nicer background color: backgroundColor game.dart @override Color backgroundColor() { return const Color.fromARGB(255, 69, 186, 230); } Much better, but currently the background moves independently of the player’s movement. But we want the parallax background to move with the camera. Let’s fix it! Return to the and add a new class variable: background.dart Vector2 _lastCameraPosition = Vector2.zero(); Next, set param value in object constructor to baseVelocity Parallax Vector2.zero() Finally, add the implementation of method: update @override void update(double dt) { final cameraPosition = gameRef.camera.position; final baseVelocity = (cameraPosition - _lastCameraPosition) * 10; parallax!.baseVelocity.setFrom(baseVelocity); _lastCameraPosition.setFrom(gameRef.camera.position); super.update(dt); } So, instead of the constant we want to calculate it dynamically, based on the camera movement. We calculate the difference in camera position since the last game loop and multiply it by the base velocity rate (in this instance 10). Then we update with the calculated value. Let’s test it. baseVelocity baseVelocity That’s what we wanted. The background doesn’t move on its own, but when the camera moves, the background moves with it, each layer with its own speed, creating a nice parallax effect. Conclusion That’s the end of Part 2. We learned how to create a level map using the Tiled editor, made the camera follow the player’s character, and added awesome parallax background. All the game code we have at this point can be found and the first story of the series is . on my GitHub here Now our project looks like a real game. But in the next chapter, we’ll make it even better, by teaching The Boy how to jump. 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 https://opengameart.org/content/arcade-platformer-assets Tiled Editor documentation https://doc.mapeditor.org/en/stable/manual/introduction/ DevKage’s Flame Game Development Series: https://youtu.be/aHS4auPhDzc Craig Oda’s channel https://youtu.be/hwQpBuZoV9s Ember Quest Game Tutorial https://github.com/flame-engine/flame/blob/main/doc/tutorials/platformer/platformer.md Flame Engine documentation https://docs.flame-engine.org/1.6.0/flame/flame.html