FlameGame¶
Every game needs a central object that owns the game loop, the continuous cycle of updating state
and rendering frames that drives all real-time games. In Flame, FlameGame fills that role while
also serving as the root of the component tree. If you are familiar with Flutter, think of
FlameGame as the equivalent of MaterialApp: the top-level entry point that everything else
hangs off of.
The base of almost all Flame games is the FlameGame class. It is the root of your component
tree. We refer to this component-based system as the Flame Component System (FCS). Throughout the
documentation, FCS is used to reference this system.
The FlameGame class implements a Component based Game. It has a tree of components
and calls the update and render methods of all components that have been added to the game.
Components can be added to the FlameGame directly in the constructor with the named children
argument, or from anywhere else with the add/addAll methods. Most of the time however, you want
to add your children to a World, the default world exists under FlameGame.world and you add
components to it just like you would to any other component.
A simple FlameGame implementation that adds two components, one in onLoad and one directly in
the constructor can look like this:
import 'package:flame/components.dart';
import 'package:flame/game.dart';
import 'package:flutter/widgets.dart';
/// A component that renders the crate sprite, with a 16 x 16 size.
class MyCrate extends SpriteComponent {
MyCrate() : super(size: Vector2.all(16));
@override
Future<void> onLoad() async {
sprite = await Sprite.load('crate.png');
}
}
class MyWorld extends World {
@override
Future<void> onLoad() async {
await add(MyCrate());
}
}
void main() {
final myGame = FlameGame(world: MyWorld());
runApp(
GameWidget(game: myGame),
);
}
Custom World type¶
FlameGame has a generic type parameter W that defaults to World. By specifying a custom world
type, the world getter on your game will return your specific world type directly, without needing
to cast it.
This is useful when you have a custom World subclass and want to access its properties or methods
from within your game class:
class MyWorld extends World {
int score = 0;
}
class MyGame extends FlameGame<MyWorld> {
MyGame() : super(world: MyWorld());
void incrementScore() {
// No cast needed, `world` is already typed as `MyWorld`.
world.score++;
}
}
When using this generic parameter, you must pass a matching world instance to the super
constructor. If the generic type is specified but no world is provided, a runtime assertion error
will be thrown.
Note
If you instantiate your game in a build method your game will be rebuilt every
time the Flutter tree gets rebuilt, which usually is more often than you’d like.
To avoid this, you can either create an instance of your game first and
reference it within your widget structure or use the GameWidget.controlled
constructor.
To remove components from the list on a FlameGame the remove or removeAll methods can be used.
The first can be used if you just want to remove one component, and the second can be used when you
want to remove a list of components. These methods exist on all Components, including the World.
The FlameGame has a built-in World called world and a CameraComponent instance called
camera, you can read more about those in the Camera section.
Game Loop¶
The GameLoop module is a simple abstraction of the game loop concept. Basically, most games are
built upon two methods:
The render method takes the canvas for drawing the current state of the game.
The update method receives the delta time in seconds since the last update and allows you to move to the next state.
The GameLoop is used by all of Flame’s Game implementations.
Resizing¶
Every time the game needs to be resized, for example when the orientation is changed, FlameGame
will call all of the Component’s onGameResize methods and it will also pass this information to
the camera and viewport.
The FlameGame.camera controls which point in the coordinate space that should be at the anchor of
your viewfinder, [0,0] is in the center (Anchor.center) of the viewport by default.
Lifecycle¶
The FlameGame lifecycle callbacks, onLoad, render, etc. are called in the following sequence:
When a FlameGame is first added to a GameWidget the lifecycle methods onGameResize, onLoad
and onMount will be called in that order. Then update and render are called in sequence for
every game tick. If the FlameGame is removed from the GameWidget then onRemove is called.
If the FlameGame is added to a new GameWidget the sequence repeats from onGameResize.
Note
The order of onGameResize and onLoad are reversed from that of other
Components. This is to allow game element sizes to be calculated before
resources are loaded or generated.
The onRemove callback can be used to clean up children and cached data:
@override
void onRemove() {
// Optional based on your game needs.
removeAll(children);
processLifecycleEvents();
Flame.images.clearCache();
Flame.assets.clearCache();
// Any other code that you want to run when the game is removed.
}
Note
Clean-up of children and resources in a FlameGame is not done automatically
and must be explicitly added to the onRemove call.
onHotReload¶
When Flutter’s hot reload is triggered (debug mode only), the GameWidget calls onHotReload on
the FlameGame, which automatically propagates the notification to every component in the tree that
is loading or loaded. Override this method on any component to reload assets, refresh cached values,
or react to source code changes during development:
class MyGame extends FlameGame {
@override
void onHotReload() {
// Refresh game-level state affected by code changes.
super.onHotReload();
}
}
Note
onHotReload is only called in debug mode. Components that are still in the
lifecycle queue (loading but not yet mounted) also receive the notification.
Always call super.onHotReload() so the event continues to propagate to
children.
dispose()¶
As a convenience, FlameGame provides a dispose() method that handles all of the common cleanup
in a single call:
game.dispose();
This removes all children from the game (triggering onRemove on every component in the tree),
processes all pending lifecycle events, and clears the images and assets caches.
The difference between dispose() and onRemove is that dispose() is a method you call
explicitly to perform cleanup, while onRemove is a lifecycle callback that is invoked
automatically when the game is removed from a GameWidget. You can use dispose() from within
onRemove, or call it independently whenever you need to reset the game state.
Debug mode¶
Flame’s FlameGame class provides a variable called debugMode, which by default is false. It
can, however, be set to true to enable debug features for the components of the game. Be aware
that the value of this variable is passed through to its components when they are added to the
game, so if you change the debugMode at runtime, it will not affect already added components by
default.
To read more about the debugMode on Flame, please refer to the Debug Docs
Change background color¶
To change the background color of your FlameGame you have to override backgroundColor().
In the following example, the background color is set to be fully transparent, so that you can see
the widgets that are behind the GameWidget. The default is opaque black.
class MyGame extends FlameGame {
@override
Color backgroundColor() => const Color(0x00000000);
}
Note that the background color can’t change dynamically while the game is running, but you could just draw a background that covers the whole canvas if you would want it to change dynamically.
SingleGameInstance mixin¶
An optional mixin SingleGameInstance can be applied to your game if you are making a single-game
application. This is a common scenario when building games: there is a single full-screen
GameWidget that hosts a single Game instance.
Adding this mixin provides performance advantages in certain scenarios. In particular, a component’s
onLoad method is guaranteed to start when that component is added to its parent, even if the
parent is not yet mounted itself. Consequently, await-ing on parent.add(component) is guaranteed
to always finish loading the component.
Using this mixin is simple:
class MyGame extends FlameGame with SingleGameInstance {
// ...
}
Low-level Game API¶
The abstract Game class is a low-level API that can be used when you want to implement the
functionality of how the game engine should be structured. Game does not implement any update or
render function for example.
The class also has the lifecycle methods onLoad, onMount and onRemove in it, which are
called from the GameWidget (or another parent) when the game is loaded + mounted, or removed.
onLoad is only called the first time the class is added to a parent, but onMount (which is
called after onLoad) is called every time it is added to a new parent. onRemove is called when
the class is removed from a parent.
Note
The Game class allows for more freedom of how to implement things, but you
are also missing out on all of the built-in features in Flame if you use it.
An example of what a Game implementation could look like:
class MyGameSubClass extends Game {
@override
void render(Canvas canvas) {
// ...
}
@override
void update(double dt) {
// ...
}
}
void main() {
final myGame = MyGameSubClass();
runApp(
GameWidget(
game: myGame,
)
);
}
Pause/Resuming/Stepping game execution¶
A Flame Game can be paused and resumed in two ways:
With the use of the
pauseEngineandresumeEnginemethods.By changing the
pausedattribute.
When pausing a Game, the GameLoop is effectively paused, meaning that no updates or new renders
will happen until it is resumed.
While the game is paused, it is possible to advance it frame by frame using the stepEngine
method. It might not be very useful in the final game, but it can be very helpful for inspecting
game state step by step during the development cycle.
Backgrounding¶
The game will be automatically paused when the app is sent to the background,
and resumed when it comes back to the foreground. This behavior can be disabled by setting
pauseWhenBackgrounded to false.
class MyGame extends FlameGame {
MyGame() {
pauseWhenBackgrounded = false;
}
}
This flag currently only works on Android and iOS.
HasPerformanceTracker mixin¶
While optimizing a game, it can be useful to track the time it took for the game to update and render each frame. This data can help in detecting areas of the code that are running hot. It can also help in detecting visual areas of the game that are taking the most time to render.
To get the update and render times, just add the HasPerformanceTracker mixin to the game class.
class MyGame extends FlameGame with HasPerformanceTracker {
// access `updateTime` and `renderTime` getters.
}