Notes: 03. Learn Flame's Core Concepts
Flame has great documentation, it might help if you take a look at it.
Before adding custom elements to your game, it’s important that you understand how Flame works.
Flame uses a component based system to simplify the management for you as a developer and for the game loop as well.
In a very similar way that everything is a Widgetin Flutter; with Flame, everything is a Component.
A Component is every single part of the game that needs to interact with the player, the game or both.
Here’s how the lifecycle of a Component is:
-
onGameResizeis called whenever the screen is resized, and also when this component gets added into the component tree, before theonMount. -
onLoadis where you might run asynchronous initialization code for the component, like loading an image. It is executed afteronGameResize, but beforeonMount. It’ll execute only once during the lifetime of the component, so you can think of it as an “asynchronous constructor”. -
onMountruns every time the component is mounted into a game tree. You should not use it to initialize variables, since it might run multiple times throughout the component’s lifetime. It only runs if the parent is already mounted. If the parent is not mounted yet, then it’ll wait in a queue without affecting the rest of the game. -
onRemoveruns code before the component is removed from the game, it is only run once.
There are many useful types of Components in Flame, even MeteormaniaGame itself is a type Component. Let’s talk about some of the most common ones:
FlameGame: is a special kind of Component based on Game, a mixin that defines a lot of the behaviour needed for developing a game. You’ve already used it when you created MeteormaniaGame.
PositionComponent: represents a positioned object on the screen. It has a position, size, scale, angle, and anchor and uses that to transform how the component is rendered.
SpriteComponent: a special type of PositionComponent that allows you to create Sprite-based components and position them wherever you want in the game. You will soon see it in action.
Now let’s put all of this knowledge to work.
MeteormaniaGame
Back in meteormania_game.dart, override onLoad in MeteormaniaGame.
@override
FutureOr<void> onLoad() async {
}
Then, you’ll load a background asset. Everytime you are going to need assets, make sure you load them before using them in any component.
await Flame.images.load('bg1.png');
Since we are loading an image to a component, the best component for this is a SpriteComponent.
A Sprite is a graphical object that consists of an image or a group of images that can be combined to create animations.
Since you already loaded the image, now you only need to get it from Flame’s cache in order to load it to a Sprite.
final background = SpriteComponent(
sprite: Sprite(
Flame.images.fromCache('bg1.png'),
),
size: Vector2(
size.x,
size.y,
),
);
Finally, use add to include the background component into the game.
add(background);
Build and run Meteormania. Great! You can now see the stars in the background, almost like you were in space.
All games have this amazing and sometimes imaginary world to live in. In it, your game characters interact with each other and with the environment around them.
This mindset allows your game to peek at the world from different perspectives at different times.
When creating your game, it is common that you might have a certain size of game world in mind.
For a small game, it can be just a small point in space with stars in the background (like in Meteormania).
In contrast, for an open-world game, you might have a much more complex world with different places, characters and environments.
The difficulty of handling your world also increases when you start to add other circumstances like asset optimization, device screen sizes or coordinating between game characters or environments.
Flame’s also got you covered in this regard. There’s a built-in World class and a CameraComponent that take away some of the complexity of managing your game’s world.
World: Is the representation of your game’s world. It’s also a great centralized point to keep all your components and keep track of them.
CameraComponent: Think of it like a window into your game’s world. You can have multiple windows into your world or just a single one.
World & CameraComponent
In meteormania_game.dart, let’s change how you add background to MeteormaniaGame.
Start by adding a couple new variables to MeteormaniaGame called _world and _cameraComponent:
late World _world;
late CameraComponent _cameraComponent;
Then, in onLoad, initialize _world and use add on the instance of World component to include background to it’s children components. Also, remember to include _world to the game using MeteormaniaGame’s add function.
_world = World()..add(background);
add(_world);
Since components should only have one parent you’ll also need to remove add(background). This way, _world is now responsible for displaying the background. You’ll also need to update the size of background to match the width and height of the screen.
final background = SpriteComponent(
sprite: Sprite(
Flame.images.fromCache('bg1.png'),
),
size: Vector2(
GameConstants.cameraWidth,
GameConstants.cameraHeight,
),
);
Finally, you’ll need to initialize a CameraComponent and add it to MeteromaniaGame. CameraComponent receives a World instance in its constructor. Then, set viewfinder’s visibleGameSize, position, and anchor like so. And finally add _cameraComponent to the game using add function.
final _cameraComponent = CameraComponent(world: _world)
..viewfinder.visibleGameSize =
Vector2(GameConstants.cameraWidth, GameConstants.cameraHeight)
..viewfinder.position =
Vector2(GameConstants.cameraWidth / 2, GameConstants.cameraHeight / 2)
..viewfinder.anchor = Anchor.center;
add(_cameraComponent);
Here, viewfinder knows which location in the underlying game world you are looking at. In this case, visibleGameSize set to a vector with the size determined by cameraWidth and cameraHeight. You’ve also set its position to the center of the screen. And finally, you’ve also set the anchor to the center as well, this serves as the logical center of the camera.
For example, in side-scrolling action games it is common to have the camera focused on the main character who is displayed not in the center of the screen but closer to the lower-left corner. In your case you’re making the whole game world visible that is portion containing the background image and setting the position and anchor both to the center of the screen.
Let’s build and run the game to see how it looks now.
Great job! You have learned about Flame’s Component System and created a World for your game!