Events & tokens

AzEvent

Use no-payload events for discrete signals where listener order is not part of correctness.

  • No payload
  • Event object
  • Mental model
  • Declaration

Use AzEvent when something happened and no payload is needed.

What AzEvent Is

AzEvent is Azkar.Eda's lightweight ordered event backend. It gives you an explicit event object that code can subscribe to, invoke, pause, resume, and inspect.

The core types are:

AzEvent

Use it when

The event has no payload.

Handler shape
Action
Invoke shape
Invoke()

AzEvent<T>

Use it when

The event has a small or cheap-to-copy payload.

Handler shape
Action<T>
Invoke shape
Invoke(value)

AzEventRef<T>

Use it when

The event has a large struct payload and you want to avoid copies.

Handler shape

AzRefHandler<T> / void Handler(in T value)

Invoke shape
Invoke(in value)

Every successful subscription returns an AzEventToken. That token is the preferred way to manage the subscription after it is created.

using Azkar.Eda;
using UnityEngine;

public static class GameEvents
{
    public static readonly AzEvent Started = new();
    public static readonly AzEvent<int> ScoreChanged = new();
    public static readonly AzEventRef<DamageReport> DamageReported = new();
}

public readonly struct DamageReport
{
    public readonly int Amount;
    public readonly Vector3 Point;

    public DamageReport(int amount, Vector3 point)
    {
        Amount = amount;
        Point = point;
    }
}

The Mental Model

An AzEvent is a list of subscribers. Calling Subscribe appends a handler to the list and returns an AzEventToken that represents exactly that subscription instance.

The token is not the handler. It is a handle to one slot in one event's internal subscription table. You can use it to:

  • Dispose() the subscription.
  • Pause() the subscription without removing it.
  • Resume() the subscription later.
  • Check IsDisposed.
  • Check IsPaused.
  • Log DiagnosticId or ToString() during debugging.

AzEventTokenBag is a small lifecycle container for many tokens. It lets a component say, "all of these subscriptions belong to me," then pause, resume, or dispose them as a group.

For most Unity components, the normal pattern is:

using Azkar.Eda;
using UnityEngine;

public sealed class ScoreHud : MonoBehaviour
{
    private readonly AzEventTokenBag _subscriptions = new();

    private void OnEnable()
    {
        GameEvents.ScoreChanged.Subscribe(OnScoreChanged).AddTo(_subscriptions);
    }

    private void OnDisable()
    {
        _subscriptions.DisposeAll();
    }

    private void OnScoreChanged(int score)
    {
        Debug.Log($"Score: {score}");
    }
}

Use AzEvent for Signals

Use AzEvent when the event itself is the entire message.

public static readonly AzEvent MatchStarted = new();

public static void StartMatch()
{
    MatchStarted.Invoke();
}

private AzEventToken _token;

private void Awake()
{
    _token = GameEvents.MatchStarted.Subscribe(OnMatchStarted);
}

private void OnDestroy()
{
    _token.Dispose();
}

private void OnMatchStarted()
{
    Debug.Log("Match started");
}

Declaring Events

The most common style is to keep shared game events on a static bus.

using Azkar.Eda;
using UnityEngine;

public static class CombatEvents
{
    public static readonly AzEvent CombatStarted = new();
    public static readonly AzEvent<int> HealthChanged = new();
    public static readonly AzEventRef<DamageReport> DamageReported = new();
}

You can also put an event directly on an instance when the event belongs to that object.

using Azkar.Eda;
using UnityEngine;

public sealed class Door : MonoBehaviour
{
    public readonly AzEvent Opened = new();

    public void Open()
    {
        Opened.Invoke();
    }
}

Use a static bus for broad application-level messages. Use instance events when the event is naturally owned by one object.