CaptureZone
A sphere that takes whatever tagged thing flies into it. In Space Golf it is the mineral plant.
What it does
Each frame the zone checks every live entity tagged acceptTag against its radius, measured from its own MeshPrimitive position. A hit emits capturezone.captured with the zone's points, takes the entity out of play (parked if it came from a pool, destroyed if not) and swells the zone's mesh in a short pulse so the catch is seen.
Candidates are found by tag, not by a component query, so one zone catches a GravityBody rock, a physics ball or a walking crate. Parked pool slots keep their tags, so the zone skips anything inactive. It skips pool blueprints too, since a blueprint carries the tags of its copies.
Once a frame is enough even for a fast rock, because GravityBody never lets one slip through between looks. After each of its fixed 1/120 s steps it checks every zone with this component's zoneAt, and stops a body on the step it comes in. The zone then finds the rock where it arrived, on the step the aim line showed, so a black hole parked on the zone's edge cannot swallow a rock the line showed caught.
With release off the entity stays in play, and is caught once each time it enters rather than on every frame it stays inside.
An optional engine plugin draws the zone's true reach: a soft glow with a crisp rim at exactly radius, on a quad that always faces the camera. A sphere looks like a disc from every side, so the one glow shows the right reach in a top view, a side view and every angle between. It flares on each catch.
Setup
The engine work ships with the component, in adapter/register.ts. A project made with bjs create installs it for you: src/main.ts calls every adapter/register.ts that bjs download puts in the project, before the scene loads. If you boot the game from your own code, call it once, right after building the adapter:
Without it zones catch exactly the same but show no glow, and the System warns once. The plugin is for BabylonAdapter only; with any other adapter the call does nothing.
Use it in a scene
A scene is just data — a list of entities, each with its components. At startup the SceneLoader turns this JSON into a live world; edit the file and reload to rebuild it.
Build it from scratch with the bjs CLI:
- 1Scaffold a project
World, renderer, and dev server — ready to run.
- 2Install dependencies
- 3Add CaptureZone
Copies its source into src/ so the scene resolves.
First time? Run bjs login once.
- 4Paste the scene into src/scenes/arcade-room.ts and run
SceneLoader builds the world from the JSON; reload to rebuild.
Anything tagged rock with a MeshPrimitive is a candidate. In Space Golf the plant is a pool blueprint that the director moves for each level.
Props
radius(number, default2) — reach from the zone's MeshPrimitive position. The rim counts as inside.acceptTag(string, default"rock") — only entities with this tag are caught.points(number, default100) — passed along on the event for whoever keeps score. The zone scores nothing itself.release(boolean, defaulttrue) — take the catch out of play. Off, it stays in play and is caught once per entry.pulse(number, default0.35) — how much the mesh swells on a catch, as a fraction of its size. It eases back over 0.35 s. 0 turns it off.color([r, g, b], default[0.3, 1, 0.6]) — colour of the plugin's glow.
Events
Emits:
capturezone.captured—{ zoneId, entityId, points, x, y, z }, the point being where the entity was caught. A released catch is already out of play when listeners hear about it.
Listens to nothing.
Dependencies
MeshPrimitive— on the zone and on every target.
Notes
- Where zones overlap, a target is taken once, by the first zone to see it. A zone never takes itself, even when it carries the tag it accepts.
- With
releaseoff, a GravityBody still stops for the rest of the frame it comes in on, then flies on from inside. - The pulse writes the zone mesh's scale and always comes to rest at exactly 1. Size the mesh through MeshPrimitive, not by scaling it from elsewhere.
- The glow is sized when it is first attached. Changing
radiusin play moves the catch but not the glow. - Catching needs no renderer, so a headless world scores the same.










































