flutter3d
Showcase Changelog 38 packages API reference

Layers and contact callbacks

since 0.2.0 or earlier Physics and particles

Two colliders are tested against each other only when each is in the other's mask. Which bit means what is not this package's business: it ships one constant, Layers.all, and a game names the rest itself.

Step 1: A zone that only wants one layer #

A trigger blocks nothing and only reports. Its mask names the one layer it cares about, so a collider outside that layer is never even tested against it, whatever mask that other collider carries.

// The zone's mask names one layer, so only a collider carrying that bit
// is ever considered for it, whatever mask that collider carries itself.
final Collider zone = Collider(
  shape: CollisionSphere(0.6),
  position: _zoneCentre,
  kind: ColliderKind.trigger,
  mask: _kPlayerLayer,
  listener: _listener,
);
_world.add(zone);

Step 2: Two movers, two layers #

The player carries the layer the zone's mask names. The enemy does not. Both start well away from the zone.

_player = _world.add(
  Collider(
    shape: CollisionBox(Vector3(0.3, 0.3, 0.3)),
    position: _away,
    kind: ColliderKind.kinematic,
    layer: _kPlayerLayer,
    userData: 'player',
  ),
);
_enemy = _world.add(
  Collider(
    shape: CollisionBox(Vector3(0.3, 0.3, 0.3)),
    position: _away,
    kind: ColliderKind.kinematic,
    layer: _kEnemyLayer,
    userData: 'enemy',
  ),
);
_world.update();

Step 3: Walk both of them in #

Moving a collider is only ever moveTo; the world does the rest the next time it is asked to update.

// Both walk into the same spot. Only the one carrying the layer the
// zone's mask names will be heard.
_player.moveTo(_zoneCentre);
_enemy.moveTo(_zoneCentre);
_world.update();

Step 4: Who gets heard #

Only the player's arrival should reach the listener. The enemy touches exactly the same point in space and is never even considered, because the zone's mask never named its layer.

if (_listener.started.length != 1 || _listener.started.first != 'player') {
  throw StateError(
    'only the player should have entered the zone, got ${_listener.started}',
  );
}

Note. CollisionListener is a mixin with three defaults, all empty. Almost everything overrides only onCollisionStart; onCollision fires on every step two colliders keep overlapping, and onCollisionEnd once they stop.