Phaser AE

3D tag collision rules

Free packv3.0.0-23DPhysics

Overlap rules by tag in a 3D world, with no dynamics. Pickups, bullets, triggers and a player who walks on floors and stops at walls all work by naming two tags and saying what happens when they meet, with the same calls as the 2D `collision` pack.

This is a Free pack. Every phaser.io account can use it in their games.

Released October 14, 2026 · updated October 19, 2026

3D tag collision 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.

The pack

collision3d

function collision3d(): Pack<{ collide: Collide3dApi }>

Overlap rules by tag and positional separation in a 3D world, with no dynamics. Runs after the game's own steps and after physics-3d.

Types

Collide3dApi

interface Collide3dApi

game.collide in a 3D world. Rules are scene-scoped: removed when the scene that made them exits.

  • on(tagA: string, tagB: string, cb: Hit3dFn): void — Overlap callback for every (a, b) pair of tags, each fixed step, after simulation.
  • solid(tagA: string, tagB: string, opts?: { push?: 'a' | 'b' | 'both'; cb?: Hit3dFn }): void — Keep A out of B by moving A (push: 'a', default), B, or both, along the shallowest axis. Positional only, no velocity.
  • overlaps(h: Handle, tag?: string): Handle[] — Every live object overlapping h right now, optionally only those tagged tag.
  • test(a: Handle, b: Handle): boolean — Do these two overlap right now?
  • contact(a: Handle, b: Handle): Contact3d | null — Which side of a b is on and how deep, or null when they do not overlap.
  • raycast(x: number, y: number, z: number, dx: number, dy: number, dz: number, maxDist: number, tag?: string): Hit3d | null — The nearest object along a ray from x, y, z in direction dx, dy, dz within maxDist, or null; tag limits the candidates.
  • shape(h: Handle, shape: Shape3d): void — Give an object a shape of its own after spawning it.

Contact3d

interface Contact3d

Which face of a the contact is on, how deep it goes, and the push that separates them.

  • side: 'left' | 'right' | 'bottom' | 'top' | 'back' | 'front' — The side of a that b is against: 'bottom' is what "am I standing on it" means (y is up).
  • overlap: number — The penetration on that axis, world units.

Hit3d

interface Hit3d

A raycast result.

  • handle: Handle — The object hit.
  • distance: number — Distance from the ray's origin, world units.
  • x: number — Where the ray met it, world x.
  • y: number — Where the ray met it, world y.
  • z: number — Where the ray met it, world z.

Hit3dFn

type Hit3dFn = (a: Handle, b: Handle, contact: Contact3d) => void

A rule callback: (a, b) in the order the rule named its tags, plus the contact, read before any separation.

Shape3d

interface Shape3d

An object's shape, as spawn data (collide: { … }) or game.collide.shape(h, …). Sizes are in the object's own units: its scale multiplies them.

  • shape?: 'box' | 'sphere' — 'box' (default) or 'sphere'.
  • size?: [number, number, number] — A box's size [w, h, d]. Default [1, 1, 1]: the mesh's own size.
  • radius?: number — A sphere's radius. Default half the largest side of the box.
  • offset?: [number, number, number] — The shape's center from the object's origin, [x, y, z]: a model's box sits on its feet. Default [0, 0, 0].