Create a Breakout Game in Flutter With Flame and Forge2D – Part 1
Learn how to create a Flutter version of the classic Breakout game using Flame and Forge2D. By Michael Jordan.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Create a Breakout Game in Flutter With Flame and Forge2D – Part 1
30 mins
- Getting Started
- Breakout Game Requirements
- Understanding the Flame Game Engine and Forge2D
- Adding Flame and Forge2D Dependencies
- Setting up Your Flame Game Loop
- Creating the Ball
- Defining a Ball’s Physical Properties
- Creating a Game Arena
- Understanding Forge2D Units and Coordinates
- Units
- Coordinate Systems
- The Flame Camera
- Breakout Game Metrics
- Adjusting the Camera, Arena and Ball Body Properties
- Where to Go From Here?
Creating the Ball
FlameGame
, and by extension, Forge2DGame
, is a component-based game framework. It manages a tree of components, similar to how Flutter manages a tree of widgets. The game loop repeatedly calls the update
and render
methods of the components you add to your game, allowing you to interact with components and add game logic.
To create a ball, you need to describe the physical properties of the ball as a rigid body for Forge2D and wrap it in a component for Flame to manage. You’ll provide this description by declaring a Ball
class that extends from a BodyComponent
.
Defining a Ball’s Physical Properties
Bodies are the fundamental objects in the physics scene. They hold a rigid body’s physical properties. There are three types of bodies in Forge2D: static, dynamic and kinematic:
- Static bodies don’t move. The bricks in the brick wall will be static bodies.
- Dynamic bodies react to forces. Forge2D updates dynamic bodies while obeying Newton’s laws of motion. The ball and paddle are dynamic bodies.
- Kinematic bodies are a hybrid between static and dynamic bodies. A Ferris wheel is an example of a kinematic body. The Ferris wheel position remains fixed, but the motion of the Ferris wheel rotating around its center is dynamic. The Breakout game doesn’t use kinematic bodies.
Fixtures are the shape of a body. Forge2D uses fixtures to determine collisions between bodies. Bodies can have zero or more fixtures. A body with no fixtures is relatively meaningless, as fixtures give the body a physical presence in the Forge2D world. Fixtures have a shape and density, thus providing mass to the body. For example, the ball in your game will have a single circular shape fixture. So why would a body have multiple fixtures? Consider a fan with four blades. A fan body would have four fixtures, a polygon shape for each fan blade positioned at 90-degree intervals around the body’s center.
Create a new folder named components in the lib folder. You’ll keep your game components files in this folder. Then, create a ball.dart file in this folder, and add the following lines of code to this file:
import 'package:flame_forge2d/flame_forge2d.dart';
import '../forge2d_game_world.dart';
// 1
class Ball extends BodyComponent<Forge2dGameWorld> {
// 2
final Vector2 position;
final double radius;
Ball({required this.position, required this.radius});
// 3
@override
Body createBody() {
// 4
final bodyDef = BodyDef()
..type = BodyType.dynamic
..position = position;
// 5
final ball = world.createBody(bodyDef);
// 6
final shape = CircleShape()..radius = radius;
// 7
final fixtureDef = FixtureDef(shape);
// 8
ball.createFixture(fixtureDef);
return ball;
}
}
Going through this step-by-step:
- You begin by declaring your
Ball
to be aBodyComponent
, a rigid body in Forge2D and a component for Flame. Then, specifyForge2dGameWorld
as theBodyComponent
game world type. This association gives your ball class access to the public properties of your game world. - When you create your ball, you specify its initial position and size.
- You tell Forge2D how to create your body in
createBody
. Forge2D callscreateBody
when you add a body to the game world. - Define the base properties of the ball’s body. The ball moves freely around the world, so its type is dynamic. The position will be passed into the constructor when adding the ball to the world; this allows you to set the beginning position of the ball.
- Use the body definition to create a rigid body in your game world.
world
is an inherited property fromBodyComponent
to yourForge2dGameWorld
instance. - If the
Body
is the soul of the rigid body,Fixture
s are its skin and bones. To define a fixture, you begin by defining a shape. In this case, your ball will have a circle shape in this 2D world. - Using the shape, you create a fixture definition.
- Use the ball’s
createFixture
method to create and add the fixture to the ball’s body.
Next, create the ball and add it to your Forge2D world by opening the file forge2d_game_world.dart and creating a private method named _initializeGame
. Now, call the routine from onLoad
like so:
import 'package:flame_forge2d/flame_forge2d.dart';
import 'components/ball.dart';
class Forge2dGameWorld extends Forge2DGame {
@override
Future<void> onLoad() async {
await _initializeGame();
}
Future<void> _initializeGame() async {
final ball = Ball(
radius: 1.0,
position: size / 2,
);
await add(ball);
}
}
Give the ball a radius
of 1.0 and a starting position
in the center of the game area. size
provides you with the size of the visible game area in the Forge2dGameWorld
. A discussion of Forge2D units, coordinates, viewports and camera are coming. So, use these values for now with the understanding that you’ll get an explanation shortly.
Build and run your project, and you’ll see a small white circle representing your ball falling off the bottom of the screen.
What’s going on? Where did the ball go? The ball is still there in your Forge2D world. It’s just forever falling into the vast, dark emptiness beyond the bottom of the screen, much like Voyager 1 and 2 speeding through space.
A ball falling off the screen isn’t much fun. So next, you’ll learn how to build walls to constrain the ball to the game area.
Creating a Game Arena
A Forge2D game world is more like a vast, empty space than a world. You create the bodies and other components of your world to fill the space.
The user plays Breakout within an enclosed area, like an arena. You’ll now create an arena to constrain the ball to a fixed region of your world. Create an arena.dart file in the components folder, and add the following lines of code to this file:
import 'package:flame_forge2d/flame_forge2d.dart';
import '../forge2d_game_world.dart';
// 1
class Arena extends BodyComponent<Forge2dGameWorld> {
Vector2? size;
// 2
Arena({this.size}) {
assert(size == null || size!.x >= 1.0 && size!.y >= 1.0);
}
late Vector2 arenaSize;
// 3
@override
Future<void> onLoad() {
arenaSize = size ?? gameRef.size;
return super.onLoad();
}
// 4
@override
Body createBody() {
final bodyDef = BodyDef()
..position = Vector2(0, 0)
..type = BodyType.static;
final arenaBody = world.createBody(bodyDef);
// 5
final vertices = <Vector2>[
arenaSize,
Vector2(0, arenaSize.y),
Vector2(0, 0),
Vector2(arenaSize.x, 0),
];
// 6
final chain = ChainShape()..createLoop(vertices);
// 7
for (var index = 0; index < chain.childCount; index++) {
arenaBody.createFixture(FixtureDef(chain.childEdge(index)));
}
return arenaBody;
}
}
The arena has many of the same elements you learned when creating the ball's body. It can be helpful to go over what's the same and what's new step-by-step:
- The arena is another body component in your game world. It acts like a fence enclosing the objects in your game.
- The
Arena
constructor has an optionalsize
parameter for defining the extent of the rectangular arena. Leave this blank; theonLoad
method will set the size to fill the available widget space. -
onLoad
is aBodyComponent
state method.onLoad
is called beforecreateBody
to allow for any initialization you might need to perform. Here, you're getting the size of the visible area in Forge2D world coordinates. You'll learn more about world coordinates in the next section. - You've seen this method before. Here's where you build your arena body. Setting the
position
to the world origin aligns the arena with the upper left-hand corner of theGameWidget
. Since the arena walls won't move, the arena's body type is static. - With the arena body created, you now need to define its fixtures. The arena's fixtures will be the walls that enclose the area. Forge2D has a
ChainShape
, a free-form sequence of line segments perfect for the arena enclosure. So first, you create a list of the locations of the arena's four corners. - Then, create a
ChainShape
from the vertex list. ThecreateLoop
method automatically closes the loop for you. - Now, create fixtures for each edge of the chain.
ChainShape
provides an excellent method that returns anEdgeShape
for each segment in the chain to use to create the fixtures of the arena. These are the walls of your arena.
Now, you need to instantiate the arena and add it to your Forge2D world. Open the file forge2d_game_world.dart, add an import for arena.dart and create an instance of Arena
in _initializeGame
above where you instantiate the Ball
:
import 'components/arena.dart';
Future<void> _initializeGame() async {
final arena = Arena();
await add(arena);
Build and run your project. The white circle now falls and stops at the bottom edge of the GameWidget
area. Congratulations! You've corralled the ball and are well along the way to creating your Breakout game.