Skip to content

Typed events ​

Declare a gameplay fact as an immutable record and publish it from the component that owns the event:

csharp
record ChestOpened { int Coins; }
class Chest : Component {
    [EventId("opened")] public event ChestOpened Opened;
    [Property] int CoinsTaken;

    public void Open() {
        CoinsTaken += 3;
        Opened.Emit(new ChestOpened(3));
    }
}

Another component observes future notifications with a named synchronous void handler, or awaits the next notification:

csharp
class ChestPresenter : Component {
    Subscription observation;

    void Start() {
        var chest = Scene.FindByName("Chest").GetComponent<Chest>();
        if (chest != null) observation = chest.Opened.Subscribe(OnOpened);
    }

    void OnOpened(ChestOpened result) { Transform.Move(result.Coins, 0); }

    public async Task<int> WaitForOpen(Chest chest) {
        var result = await chest.Opened.Next();
        return result.Coins;
    }
}

Subscriptions ​

Subscription.Cancel() unregisters the handler. Id, IsActive, and Reason are read-only. Subscriptions cannot be [Property] values or record members.

Retirement of either component removes the subscription. Registration inside a task also ties it to that task. Disabled listeners skip events without buffering them. A listener’s presentation tasks belong to its registration task, or its component when registered outside a task.

See Subscription.

Next() ​

Next() returns Task<Payload> and unregisters on completion, cancellation, failure, or either component’s retirement. The continuation runs on a later frame. Combine with Task.Race for an owned timeout.

Emit rules ​

  • Only the declaring component can Emit its own event.
  • Payloads must be immutable records. Nested records, enums, options, vectors, and typed assets keep their types.
  • Delivery is synchronous FIFO. An event emitted by a handler waits until the current event finishes.
  • One failing handler is reported without stopping other listeners.
  • Emit describes state you already committed — it does not make surrounding statements transactional.
  • Events are not replayed to later subscribers.
  • Pure functions and finally cannot emit, subscribe, or call Next, including through helpers. Cleanup may cancel an existing subscription.

Flow ​

The event appears under its component, with an aggregate Payload pin and typed member pins (for example Coins). Optional values expose presence separately; reading an absent value reports a binding error.

[EventId("opened")] keeps the Flow key stable. Source Rename inserts it automatically when needed. Script Studio shows event waits and live subscriptions with their source identities.

See Scripts and Flow.