Flutter Tutorial

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

CodeMeaning
Vector2(3, 4).lengthDistance 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 = ....)

Practise in the playground Updated