Phaser AE

Interaction rules

Pro packv3.0.0-02DArcade

One table of "what happens when X meets Y", instead of a collision callback per pair scattered through an update loop. Rules are registered for pairs of kinds, run once a frame over your actors, and read like the game's design.

This is a Pro pack. Upgrade to Phaser Pro to use it in your games.See Phaser Pro →

Released October 19, 2026

Interaction rules: a screenshot of its demo
A note about these graphics

Expansion packs don't come with graphics. This screenshot is one way of drawing what the pack does, made for its demo: none of the sprites, tiles or backgrounds in it are in the pack.

That's by design. A pack is a game's logic, mechanics and effects, and it works with whatever art you give it. Some packs draw effects, backdrops or 3D scenes of their own, but none carries a game's sprites.

So the look is yours. Make the art on phaser.io, bring your own, or let your agent draw it in code: the same pack can look like anything.

What it does

An actor is any plain object with an x, a y and a kind, and optionally a radius and an alive flag. You build a table with createInteractions and add rules with on(from, to, …): a shot meeting a mine splits the mine and scores, a ship meeting the sun dies. A rule matches when the two are within their radii added, or a reach of its own, or at any range, and its optional when test holds. Each frame, resolve tests every ordered pair in one list, or pair tests two lists against each other, shots against rocks without rocks against rocks.

The sweep respects deaths as it goes: once an actor's alive turns false it stops counting for the rest of the sweep, so a shot that has already hit cannot also take out the rock behind it. Each rule receives a context you choose, usually the game state, so its effect is a line of code beside the rule rather than a branch in the update loop.

Some interactions are actions rather than collisions. A rule with a verb, such as steal, dock or capture, never fires in a sweep; your game fires it when the player presses a button, and target finds the nearest candidate the verb would match.

Features

  • Rules by kind — one way round for each pair, chainable, with as many rules per pair as you need.
  • Reach and conditions — the two radii by default, a fixed reach, or any range, plus a when test for anything else.
  • Death-aware sweeps — one list against itself or two lists against each other, returning how many rules fired.
  • Named verbs — pressed rather than collided, with the nearest valid target found for you.
  • Ready-made conditions — where.sameLane for tube lanes, where.samePlane for depth planes, where.facing for a facing cone and where.notOwn so a shot never hits the thing that fired it.

Good for

  • Vector shooters with a handful of rules: shots and mines, ships and suns, shots and enemies.
  • Games with thieves and cargo, where an enemy steals a canister and the player rescues it.
  • Tube and lane shooters, where only actors in the same lane or depth plane can meet.
  • Board and token games whose pieces are plain objects and whose meetings are rules.
  • Any game whose rules should read as data, so a designer can see every interaction in one place.

Make it yours

The table is yours to fill: the kinds, the rules, the reach, the conditions and what each rule does. Rules can be combined with && in a when, and the context type is the game state you pass, so a rule can score, spawn, split or end the game.

Two very different uses: an asteroid-field shooter where shots are swept against rocks with pair, where.notOwn keeps a ricochet off its owner, and the ship dies on touching the sun at a reach of its own; and a tow-and-dock game where nothing collides at all, and every interaction is a verb the player presses, with target picking the nearest ship that can be docked with.

A quick look

Three rules, one of them a verb, resolved once a frame:

import { createInteractions, where, type Kinded } from '@phaser-ae/interaction-rules';
type Actor = Kinded & { id: number; owner?: Actor };
const rules = createInteractions<Actor, Game>()
  .on('shot', 'mine', { when: where.notOwn(), then: (s, m, g) => { s.alive = false; g.split(m); g.score(750); } })
  .on('ship', 'sun', { radius: 40, then: (_, __, g) => g.die() })
  .on('thief', 'canister', { verb: 'steal', then: (t, c) => { t.cargo = c; } });
game.step('rules', () => { rules.resolve(actors, state); });

Works with

  • maths — used for the facing test's angles.
  • collision — for kernel objects with tags, its overlap rules do the same job on a spatial hash, and scale to thousands.

Good to know

  • The sweep tests every pair in the list it is given: right for dozens of actors, wrong for thousands, where collision is the better choice.
  • Overlap is circles only; any other shape test goes in when.
  • Rules are one way round: a rule for a shot meeting a rock does not fire for a rock meeting a shot.
  • The pack is pure logic and runs headless, with no options of its own.

More Arcade packs

Show all →