LogoIgnis

Collisions

Hitboxes, layers, and the two signals that report contact.

Loading the scene…
Try dragging!

Collisions work using two nodes, CollisionDetectionNode and ColliderNode.

CollisionDetectionNode sets up a collision detection arena. Whenever a ColliderNode is added to a scene, it finds the closest CollisionDetectionNode above it and registers itself.

Today, collision detection has two significant limitations:

  • It works only with Shape: circles and rectangles.
  • It only reports the other ColliderNode, not where they collided.

In exchange for these limitations, the arena is quite performant. See how many balls you can spawn before your FPS drops!

Layers and Masks#

ColliderNode supports specifying two bitmasks, layer and mask, to exclude certain collisions from consideration. layer indicates the physics layers the collider exists on, while mask indicates which physics layers it collides with. A pair only reports a collision to a side whose mask intersects the other's layer.

By the way...

While layer and mask are defined as integers, they should be treated as bitmasks. Each position in the integer is a separate physics layer. That also means Ignis only supports up to 32 collision detection layers. By default, layer and mask are -1 (all bits are 1), letting all colliders interact.

The code below excludes wall-wall collisions altogether, greatly improving collision detection performance and removing the need to handle those cases in your code.

const TERRAIN_LAYER = 1 << 0;
const UNIT_LAYER = 1 << 1;

wall
  ..layer = TERRAIN_LAYER
  ..mask = 0;

player
  ..layer = UNIT_LAYER
  ..mask = TERRAIN_LAYER | UNIT_LAYER;
GodotLineage

The names layer and mask come from Godot's collision system.

Examples#

Reporting a pair#

final collider = mover.add(
  ColliderNode(
    shape: mover.shape,
    anchor: mover.anchor,
  ),
);

collider
  ..onCollisionStart((_) => log('colliding', .red))
  ..onCollisionEnd((_) => log('not colliding', .green));
collisions.dart
Loading the scene…

Everything you touch#

final collider = mover.add(
  ColliderNode(
    shape: mover.shape,
    anchor: mover.anchor,
  ),
);

tick((_) {
  log(switch (collider.active.length) {
    0 => "can't touch this!",
    final n => 'touching $n',
  });
});
collisions.dart
Loading the scene…

A spinning hitbox#

final blade = add(
  ShapeNode(
    shape: .rectangle(.new(70, 10)),
    anchor: .center,
    position: _CENTER,
    paint: Paint()..color = _IDLE_COLOR,
  ),
);

final collider = blade.add(
  ColliderNode(
    shape: blade.shape,
    anchor: blade.anchor,
  ),
);

blade.add(SpinEffect(speed: pi / 2));
collisions.dart
Loading the scene…

Filtering with layers#

blue.add(
  ColliderNode(
    shape: blue.shape,
    anchor: blue.anchor,
    layer: BLUE_LAYER,
  ),
);

orange.add(
  ColliderNode(
    shape: orange.shape,
    anchor: orange.anchor,
    layer: ORANGE_LAYER,
  ),
);

final collider = mover.add(
  ColliderNode(
    shape: mover.shape,
    anchor: mover.anchor,
    mask: ORANGE_LAYER,
  ),
);
collisions.dart
Loading the scene…

Many at once#

final collider = add(
  ColliderNode(
    shape: shape,
    anchor: anchor,
  ),
);

collider
  ..onCollisionStart((_) {
    paint.color = _HIT_COLOR;
  })
  ..onCollisionEnd((_) {
    if (!collider.isColliding) {
      paint.color = _IDLE_COLOR;
    }
  });
collisions.dart
Loading the scene…
Try dragging!