React when something enters a zone
Goal
Entering a sphere says “Inside”; leaving it says “Outside”, without a physics sensor.
Files you will edit
src/game.ts
Steps
Add these declarations beside the imports in
src/game.ts. Merge thegameableimports with the file's existing import.tsimport { createTriggers, type GameContext } from 'gameable'; const zoneHud = { message: 'Outside' }; const zones = createTriggers({ capacity: 1, watchCapacity: 1, onEnter() { zoneHud.message = 'Inside'; }, onExit() { zoneHud.message = 'Outside'; }, }); let zone = 0; function resetZones(entity: number): void { zones.reset(); zoneHud.message = 'Outside'; zone = zones.addSphere(0, 0, 0, 3); zones.watch(entity); } function checkZones(ctx: GameContext): void { zones.step(ctx); zoneHud.message = zones.count(zone) > 0 ? 'Inside' : 'Outside'; ctx.hud.set(zoneHud); }In
init, callresetZones(hero)after top-down spawns its hero (usectx.playerin a template with a declarative player). RuncheckZones(ctx)after movement and before the existing HUD write inupdate; remove that old HUD write for this exercise so it cannot overwrite the zone message. Alternatively put it insystemsafter movement systems. Mergezones.snapshot()/zones.restore(state)into the game's snapshot hooks.To despawn explicitly, call
zones.unwatch(entity, ctx)beforectx.despawn(entity)if an immediate exit callback matters. These are inclusive point tests onTransform, not body overlap or swept tests; an entity without Transform is outside. All exits run before all enters.
Verify
sh
npm run devMove the hero within three metres of the origin: the HUD says Inside. Cross the sphere boundary: it says Outside. Stand still inside: the enter callback must not fire again. The result does not depend on physics contacts.