Skip to content

Observable<T> and Event<T>

Observable<T>, ObservableList<T>, and Event<T> are the three runtime types that make up a bridge surface.

Observable<T>

A reactive cell. Get and set .Value; the bridge serialises and pushes to the UI on every change.

[BridgeState] public Observable<int> Score { get; } = new(0);
// somewhere in your game logic:
Score.Value = 100; // triggers a state push
int now = Score.Value; // read back

Observable<T> is also subscribable from C# if you need engine-side reactivity (e.g., updating a debug HUD):

Score.Changed += (newValue, oldValue) => {
Debug.Log($"score went {oldValue} → {newValue}");
};

The Changed event fires after .Value is set, on the same thread that set it. It does not fire if the new value is Equals() to the old one.

ObservableList<T>

A reactive list. Supports the usual Add, Remove, Insert, Clear, this[int] indexer, plus a Changed event with item-level granularity.

[BridgeState] public ObservableList<string> Messages { get; } = new();
Messages.Add("hello");
Messages[0] = "hi";

The UI sees a typed array; fine-grained changes don’t re-render the whole list (Solid’s signal model handles this efficiently when the UI is written against the underlying signal).

Event<TPayload>

A one-shot fan-out. C# fires with a payload; the UI receives a typed callback.

C#
[BridgeEvent] public Event<DamageEvent> Damaged { get; } = new();
Damaged.Fire(new DamageEvent { Amount = 10 });
TS
onEvent('damaged', (e) => {
flashRed(e.amount)
})

Events do not buffer. If the UI is not yet connected (e.g. boot phase), fired events are dropped. If your engine fires an event before the UI is up, either delay the fire or use a state field instead.

Threading

All three types are designed for the main thread. Pushing state from a background thread is undefined behaviour. If you need to update state from a worker, UnityMainThreadDispatcher.Enqueue(...) (or your engine’s equivalent) to the main thread first.