Flutter Intermediate Lesson 106 of 135 3 min read
The Flame Game Loop: update, dt, Velocity and Bouncing
Learn the game loop in Flame: update and render every frame, delta time, velocity and gravity, and bouncing balls off the screen edges, with a runnable example.
On this page
A game is a loop that runs about 60 times a second:
┌──────────── one frame ────────────┐
│ 1. handle input (taps, keys) │
│ 2. update(dt) → move things │
│ 3. render(canvas) → draw things │
└───────────────────────────────────┘
repeat ~60 times a second
Flame runs this loop for you. Your job is to write update(dt): how should this thing change in dt seconds?
Velocity
Velocity is speed with a direction, stored as a Vector2 in pixels per second. Each frame:
position += velocity * dt;
Bouncing balls
import 'dart:math';
import 'package:flame/components.dart';
import 'package:flame/game.dart';
import 'package:flutter/material.dart';
void main() => runApp(GameWidget(game: BounceGame()));
class BounceGame extends FlameGame {
final random = Random();
@override
Future<void> onLoad() async {
camera.viewfinder.anchor = Anchor.topLeft;
for (var i = 0; i < 8; i++) {
world.add(Ball(
position: Vector2(100 + random.nextDouble() * 200, 100 + random.nextDouble() * 200),
velocity: Vector2(random.nextDouble() * 400 - 200, random.nextDouble() * 400 - 200),
color: Colors.primaries[i % Colors.primaries.length],
));
}
}
}
class Ball extends CircleComponent with HasGameReference<BounceGame> {
Ball({required super.position, required this.velocity, required Color color})
: super(radius: 18, anchor: Anchor.center, paint: Paint()..color = color);
final Vector2 velocity;
@override
void update(double dt) {
super.update(dt);
position += velocity * dt;
final screen = game.size;
// Bounce off the left and right edges
if (position.x < radius || position.x > screen.x - radius) {
velocity.x = -velocity.x;
position.x = position.x.clamp(radius, screen.x - radius);
}
// Bounce off the top and bottom edges
if (position.y < radius || position.y > screen.y - radius) {
velocity.y = -velocity.y;
position.y = position.y.clamp(radius, screen.y - radius);
}
}
}
The clamp puts the ball back inside the screen, so it cannot get stuck outside an edge.
Gravity
Gravity is a constant acceleration: it changes the velocity a little every frame.
final gravity = Vector2(0, 900); // pixels per second, per second
@override
void update(double dt) {
super.update(dt);
velocity += gravity * dt; // speed up downwards
position += velocity * dt; // then move
final floor = game.size.y - radius;
if (position.y > floor) {
position.y = floor;
velocity.y = -velocity.y * 0.8; // bounce and lose 20% energy
}
}
Replace the update in Ball with this one and the balls fall and bounce like real balls.
Moving towards a target
final target = Vector2(300, 400);
const speed = 150.0;
@override
void update(double dt) {
super.update(dt);
final toTarget = target - position;
if (toTarget.length > 2) {
position += toTarget.normalized() * speed * dt;
}
}
normalized() turns any vector into length 1, so * speed gives an exact speed in any direction. This is how enemies chase a player.
Useful Vector2 tools
| Code | Meaning |
|---|---|
Vector2(3, 4).length | Distance from (0,0): 5 |
a.distanceTo(b) | Distance between two points |
v.normalized() | Same direction, length 1 |
v.scale(2) | Make it twice as long (in place) |
Vector2.zero(), Vector2.all(5) | Handy constructors |
Try it yourself
Add gravity to the bouncing balls, then make each ball change colour every time it hits the floor. (Tip: paint.color = ....)