Tutorial: Per-Instance Pub/Sub with LocalSignalTransmitter¶
Some signals are global — every system listens to one bus, like the level-up event of an RPG. Other signals are per-instance — every prefab instance has its own routing, and a signal raised on one instance shouldn't fire listeners on another. That's the gap LocalSignalTransmitter fills.
By the end of this tutorial you'll have a prefab where each instance has its own signal bus — one chest opening doesn't fire the listeners on every other chest.
Prerequisites: Basic familiarity with SignalType and Events.
When LocalSignalTransmitter Beats a Shared EventAsset¶
EventAsset is global: any listener that subscribes to the asset hears every raise. That's exactly what you want for things like "game paused" or "tutorial step complete" — one bus, many listeners.
But consider a treasure chest prefab:
- Each chest has its own "opened" moment.
- Each chest has its own UI tooltip and particle effect.
- When chest A opens, only A's listeners should fire — not B's, not C's.
A shared EventAsset fans the signal to everyone. A direct UnityEvent works but can't be plugged into any IEvent slot. LocalSignalTransmitter gives you the best of both: routing scoped to a single instance, but exposed through the standard IEvent and SignalType surface.
1. Add a LocalSignalTransmitter to the Prefab¶
Open your Chest prefab. Add the LocalSignalTransmitter component.
Add Component → Jungle → Events → Local Signal Transmitter
The component carries no Inspector fields — it's a pure routing hub. The asset value comes through the SignalType assets you reference into it from elsewhere.
2. Create the Signals¶
Right-click in the Project → Create → Jungle → Events → Signal Type. Name it ChestOpenedSignal.
This is a shared key — the type of signal. The hub provides the scope: every chest's transmitter knows about ChestOpenedSignal but routes its listeners independently.
3. Raise the Signal on This Chest's Transmitter¶
When a chest opens, raise the signal on its own transmitter.
In the chest's "Opened" process (a phase process of an AttachableObject, a ProcessComponent, anywhere that runs a process), add a Raise Signal On Owner Transmitter Operation:
| Field | Value |
|---|---|
| owner | The chest GameObject (often this). |
| signal | ChestOpenedSignal. |
The operation finds the LocalSignalTransmitter on the owner GameObject and calls Raise(ChestOpenedSignal) on it. Only listeners that registered on that transmitter fire.
For "raise on a transmitter I'm holding by reference" use Raise Signal On Transmitter Operation instead — it takes a direct hub reference.
4. Listen to the Signal on the Same Chest¶
Anywhere on the chest prefab that wants to react to its own open signal: drop a Signal Event From Hub into an IEvent slot.
For example, on the same chest, add a ProcessComponent and set its Begin trigger to a SignalEventFromHub:
| Field | Value |
|---|---|
| transmitter | The chest's LocalSignalTransmitter. |
| signal | ChestOpenedSignal. |
When the chest raises ChestOpenedSignal on its transmitter, this ProcessComponent begins — and only this chest's ProcessComponent, not its siblings'.
5. Or Listen via a Reactor¶
Combine with a Reactor on the chest for typed reactions:
Reactor (on the chest)
└─ Event Target
├─ Source: SignalEventFromHub(LocalSignalTransmitter, ChestOpenedSignal)
└─ Entries
└─ Body: SpawnParticleCommand, PlayAudioCommand, UpdateUICommand
Same pattern, gives you per-entry overlap and retrigger policies plus the Reactor's lifecycle behaviour.
6. When to Use Reactor.Receive Instead¶
The Reactor's signal target uses a different mechanism — signals are pushed in via Reactor.Receive(signal). Use it when:
- The signal arrives from outside the chest (a global event, a manager).
- You want all signal-receiving targets on the Reactor to share the same routing.
Use LocalSignalTransmitter when the signal is intrinsic to this instance — same prefab, same chest, signal routed locally.
Both can coexist on the same chest — a Reactor for cross-system signals, a LocalSignalTransmitter for instance-local broadcasts.
7. SignalEventFromHub Anywhere¶
SignalEventFromHub is just an IEvent implementation. That means anywhere an IEvent slot exists — WaitForEventProcess, AttachingAgent grab/drop triggers, Reactor event targets, ProcessComponent begin/end — you can plug a transmitter-scoped signal in. The transmitter-scoped routing applies regardless of where you plugged it.
8. SignalType vs EventAsset Decision Table¶
| Question | If yes, pick... |
|---|---|
| Will many systems share this event globally? | EventAsset |
| Does every prefab instance need its own routing? | SignalType + LocalSignalTransmitter |
| Do you need typed access to a value inside the listener? | A Reactor target (Float, Bool, Vector3, ...) — not a signal at all. |
| Does the routing need to feed multiple reactor entries on one component? | Reactor.Receive(signal) |
There's no hard wall between the patterns — SignalEventFromHub exposes a transmitter-scoped signal as an IEvent, so it composes everywhere an EventAsset would.
Next Steps¶
- Reactor tutorial — typed reactions on signals and other sources.
- Events overview —
IEventand the standard trigger pattern. - LocalSignalTransmitter reference — full API.
- SignalType reference.