World boundary
WorldBoundary2DBehavior and WorldBoundary3DBehavior track when an entity crosses axis-aligned world limits and expose helpers to clamp movement along blocked axes. Hit semantics: left when x ≤ left, right when x ≥ right, bottom when y ≤ bottom, top when y ≥ top (3D adds Z faces). FSM coarse state is inside vs touching; the detailed hit set lives in extended state updated each frame.
Minimal example (2D)
website/snippets/behaviors/catalog/world-boundary.ts:
import { WorldBoundary2DBehavior } from '@zylem/game-lib/behavior';
const boundary = ship.use(WorldBoundary2DBehavior, {
boundaries: { top: 4, bottom: -4, left: -7, right: 7 },
});
ship.onUpdate(({ me, inputs, delta }) => {
let moveX = inputs.p1.axes.Horizontal.value * 500 * delta;
let moveY = -inputs.p1.axes.Vertical.value * 500 * delta;
const clamped = boundary.getMovement(moveX, moveY);
me.moveXY(clamped.moveX, clamped.moveY);
});
Pure helpers computeWorldBoundary2DHits and hasAnyWorldBoundary2DHit (and 3D equivalents) work without attaching the behavior when you only need hit tests.
Handle
getLastHits()— per-face booleans after spawn.getMovement(moveX, moveY)— zeros components pushing into a touched face.
Variations
- For WASM-only arcade scenes, see Runtime 2D
RuntimeBoundary2DBehavior. - Pair with Ricochet via
BoundaryRicochetCoordinatorwhen bounds should reflect velocity instead of clamping.
Pitfalls
getLastHits()returns null until the FSM has updated at least once after spawn.- Boundaries are world-axis aligned; rotated arenas need custom logic or different colliders.
API reference
WorldBoundary2DBehavior,WorldBoundary3DBehaviorcomputeWorldBoundary2DHits,hasAnyWorldBoundary3DHit- Generated reference: API