Flutter Intermediate Lesson 108 of 135 3 min read
Collision Detection in Flame: Hitboxes and CollisionCallbacks
Detect collisions in Flutter Flame games with hitboxes, CollisionCallbacks and onCollisionStart, in a runnable catch-the-coins game.
On this page
A collision is when two things in the game touch: the ball hits a brick, the player picks up a coin, a bullet hits an enemy. Flame finds collisions for you in three steps:
- Add
HasCollisionDetectionto the game. - Give each component a hitbox child:
RectangleHitbox(),CircleHitbox()orPolygonHitbox. - Add
CollisionCallbacksto a component and overrideonCollisionStart.
Catch the falling coins
import 'dart:math';
import 'package:flame/collisions.dart';
import 'package:flame/components.dart';
import 'package:flame/events.dart';
import 'package:flame/game.dart';
import 'package:flutter/material.dart';
void main() => runApp(GameWidget(game: CoinGame()));
class CoinGame extends FlameGame with HasCollisionDetection {
final random = Random();
int score = 0;
late final TextComponent scoreText;
@override
Future<void> onLoad() async {
camera.viewfinder.anchor = Anchor.topLeft;
scoreText = TextComponent(text: 'Score: 0', position: Vector2(16, 16));
world.add(scoreText);
world.add(Basket()..position = Vector2(size.x / 2, size.y - 40));
world.add(TimerComponent(period: 0.8, repeat: true, onTick: dropCoin));
}
void dropCoin() {
world.add(Coin(Vector2(20 + random.nextDouble() * (size.x - 40), -20)));
}
void addPoint() {
score++;
scoreText.text = 'Score: $score';
}
}
class Basket extends RectangleComponent with DragCallbacks, HasGameReference<CoinGame> {
Basket()
: super(
size: Vector2(110, 26),
anchor: Anchor.center,
paint: Paint()..color = Colors.brown.shade300,
children: [RectangleHitbox()], // the area that can collide
);
@override
void onDragUpdate(DragUpdateEvent event) {
position.x = (position.x + event.localDelta.x).clamp(size.x / 2, game.size.x - size.x / 2);
}
}
class Coin extends CircleComponent with CollisionCallbacks, HasGameReference<CoinGame> {
Coin(Vector2 position)
: super(
position: position,
radius: 14,
anchor: Anchor.center,
paint: Paint()..color = Colors.amber,
children: [CircleHitbox()],
);
@override
void update(double dt) {
super.update(dt);
position.y += 220 * dt;
if (position.y > game.size.y + 20) removeFromParent(); // missed
}
@override
void onCollisionStart(Set<Vector2> intersectionPoints, PositionComponent other) {
super.onCollisionStart(intersectionPoints, other);
if (other is Basket) {
game.addPoint();
removeFromParent();
}
}
}
Drag the basket to catch the coins.
The collision methods
| Method | Called |
|---|---|
onCollisionStart(points, other) | Once, when two hitboxes start touching |
onCollision(points, other) | Every frame while they touch |
onCollisionEnd(other) | Once, when they stop touching |
Use onCollisionStart for “picked up”, “hit” and “damage”. Use onCollision for things like standing on a platform.
other is Basket checks what you collided with, so one component can react differently to walls, enemies and coins.
Hitbox shapes
| Hitbox | Best for |
|---|---|
RectangleHitbox() | Boxes, platforms, most characters |
CircleHitbox() | Balls, coins, round enemies |
PolygonHitbox([...]) | Odd shapes (a triangle ship) |
ScreenHitbox() | The screen edges (add it to the game) |
A hitbox fills its parent by default. Make it smaller for fairer games: RectangleHitbox(size: Vector2(40, 40), position: Vector2(5, 5)).
Seeing the hitboxes
Set debugMode = true on a component (or on the whole game) to draw outlines of every component and hitbox. It is the fastest way to find out why something does or does not collide.
Performance tips
- Only things that need to collide should have hitboxes.
- Use
CollisionType.passiveon hitboxes that never need to check each other, such as walls and coins:RectangleHitbox(collisionType: CollisionType.passive). Two passive hitboxes are never compared, which saves work.
Try it yourself
Add red “bomb” circles that also fall. Catching a bomb sets the score back to zero. (Tip: make Bomb a second class and check if (other is Basket) in it too.)