Recipes
Event wiring one trigger emits another responder reacts
A pressure plate over here should open a door over there; a switch should light a lamp across the room; stepping into a zone should start a sequence somewhere else. The sensor and the responder are DIFFERENT entities, so wire them with a named custom event rather than having the trigger reach into the other entity's components blindly.
engine v1.0.234since script-semantic-patch-ops.v1, action-catalog.v1.0.232, mutation.v1, hierarchy.v1Copy for LLM
Use this for
making one entity react when a DIFFERENT entity is triggered; a pressure plate that opens a distant door, a switch that lights a far lamp, a trigger that starts a sequence elsewhere; decoupling the sensor from the responder with a custom event
Not for
a trigger that acts on the SAME object it lives on (see doors-buttons-and-interactions); launching the entering player (see jump-pads-and-launchers); scoreboard or match events (see match-flow-and-scoreboards)
Pairs with: Doors buttons levers and press-to-interact, Moving platforms, elevators, rotating hazards, and objects on a patrol path, Timers countdowns cooldowns and enemy waves, Manager scripts driving many objects from one system
The recipe
Author both scripts with project_apply_script_semantic_patch using the ai_safe op pack.
- Sensor side: give the trigger object a trigger
ColliderComponent(collision:"trigger") viaproject_add_component, thenaddTriggerHandler({ "handlerKey":"onSignal" }). In the whole-body GessaScript (replaceScriptFromGessaScript) emit the event:ctx.events.emit("door_opened", { open: true }). TheemitCustomEventop is the declarative equivalent when you want the event declared in the script's event set. - Responder side: on the door (or lamp, or gate) entity declare the matching handler with
createCustomEventHandler({ "eventKey":"door_opened" }), which creates a guardedonCustomEventhandler for that semantic key. patchComponent acts on the entity the handler lives on, so its body does the reaction directly, e.g.ctx.entity.patchComponent("TransformComponent", { position: {...open} }). - Idempotency: guard the responder with
setBooleanState({ "field":"opened", "value": true }) and check it first, so a second emit does not re-run the open. - Payload: pass ids or values in the emit payload (
{ source, amount }) and read them in the handler; do not encode data in the event NAME. - Fan-out is free: any number of responders that declare the same
onCustomEventkey all react to one emit. Use distinct event names per wire (gate_north_open,lamp_hall_on) so unrelated responders stay silent.
Pitfalls
ctx.events.emitis ROOM-scoped (audience room), so EVERY subscriber of that name reacts. Name events per wire, not generically, or a far responder fires unexpectedly.- Do not make the trigger reach across and mutate the other entity directly by guessing its id; the event wire is what keeps the sensor and responder decoupled and reorderable.
- The responder needs its OWN script carrying the
onCustomEventhandler; an emit with no declared subscriber is silently dropped. - An event is ephemeral: a responder spawned AFTER the emit never sees it. Drive persistent open/closed truth through
setBooleanState, and read that on spawn.
Verify
simulation_run(qa.run.start) tripping the sensor trigger and asserting the remote responder reached its reacted state.project_get_graph_snapshotto confirm the trigger handler on the sensor and theonCustomEventhandler on the responder both landed.