Events & tokens

Lifecycle Recipes

Apply tokens and bags to MonoBehaviours, managers, pooled listeners, rebinding, and debug dispatch.

  • MonoBehaviour
  • Managers
  • Pooled listeners
  • Rebinding

These recipes show where tokens and bags usually belong in Unity code.

Lifecycle Recipes

Recipe: Simple MonoBehaviour Subscriber

Use this for most scene objects.

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

    private void OnEnable()
    {
        CombatEvents.HealthChanged.Subscribe(OnHealthChanged).AddTo(_subscriptions);
    }

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

    private void OnHealthChanged(int health)
    {
        // Play audio.
    }
}

Recipe: Persistent Manager

Use this for objects that subscribe once and live for the whole scene or app lifetime.

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

    private void Awake()
    {
        CombatEvents.CombatStarted.Subscribe(OnCombatStarted).AddTo(_subscriptions);
        CombatEvents.DamageReported.Subscribe(OnDamageReported).AddTo(_subscriptions);
    }

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

    private void OnCombatStarted()
    {
    }

    private void OnDamageReported(in DamageReport report)
    {
    }
}

Recipe: Temporarily Muting One Listener

private AzEventToken _token;

private void Awake()
{
    _token = CombatEvents.HealthChanged.Subscribe(OnHealthChanged);
}

public void SetMuted(bool muted)
{
    if (muted)
    {
        _token.Pause();
    }
    else
    {
        _token.Resume();
    }
}

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

Recipe: Temporarily Muting a Group

private readonly AzEventTokenBag _uiSubscriptions = new();

public void HidePanel()
{
    _uiSubscriptions.PauseAll();
}

public void ShowPanel()
{
    _uiSubscriptions.ResumeAll();
}

Recipe: Rebinding to a Different Event Source

When an object changes the source it listens to, dispose the old source token before subscribing to the new one.

private AzEventToken _doorToken;
private Door _door;

public void Bind(Door door)
{
    _doorToken.Dispose();

    _door = door;
    _doorToken = _door.Opened.Subscribe(OnDoorOpened);
}

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

Recipe: Instance Event Producer and Consumer

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

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

public sealed class DoorLight : MonoBehaviour
{
    [SerializeField] private Door _door;

    private AzEventToken _token;

    private void OnEnable()
    {
        if (_door != null)
        {
            _token = _door.Opened.Subscribe(OnDoorOpened);
        }
    }

    private void OnDisable()
    {
        _token.Dispose();
        _token = AzEventToken.Invalid;
    }

    private void OnDoorOpened()
    {
        // Turn on the light.
    }
}

Recipe: Safe Debug Dispatch

AzInvokeResult result = CombatEvents.DamageReported.InvokeSafe(in report);

if (result.Exceptions > 0)
{
    Debug.LogWarning(
        $"DamageReported invoked {result.Invoked} handlers with {result.Exceptions} exception(s). " +
        $"First: {result.FirstException?.Message}");
}