First run

Getting Started

Create a first event, a first state, a token cleanup pattern, and a first tracking pass in Unity.

  • Setup assumptions
  • Minimal event bus
  • Minimal state
  • First tracking pass

This guide gets a new Unity user from zero to a working event, state, token cleanup pattern, and first tracking pass.

For deeper reference, keep these guides nearby:

Setup Assumptions

The examples assume Azkar EDA has been imported into your Unity project under Assets/Azkar Industries/Azkar EDA, and your scripts can reference:

using Azkar.Eda;

Azkar EDA requires Unity's com.unity.mathematics package. The included sample UI uses Unity UI Toolkit and does not require the legacy Canvas UI package.

If your assembly definitions are split, make sure the assembly containing your gameplay scripts references the assembly that contains Azkar EDA.

Minimal Event Bus

An event represents a discrete signal. A common Unity pattern is to put shared buses in a static class and let scene objects subscribe during OnEnable.

using Azkar.Eda;

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

Publish from gameplay code:

using UnityEngine;
using Azkar.Eda;

public sealed class ScoreSystem : MonoBehaviour
{
    private int _score;

    public void AddScore(int amount)
    {
        _score += amount;
        GameEvents.ScoreChanged.Invoke(_score);
    }
}

Subscribe from UI:

using UnityEngine;
using UnityEngine.UIElements;
using Azkar.Eda;

public sealed class ScoreHud : MonoBehaviour
{
    [SerializeField] private UIDocument document;

    private readonly AzEventTokenBag _tokens = new();
    private Label _scoreText;

    private void OnEnable()
    {
        _scoreText = document.rootVisualElement.Q<Label>("scoreText");
        GameEvents.ScoreChanged.Subscribe(OnScoreChanged).AddTo(_tokens);
    }

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

    private void OnScoreChanged(int score)
    {
        _scoreText.text = score.ToString();
    }
}

The bag belongs to the listener lifecycle. When the UI disables, the subscription is removed.

Minimal State

State is for values with current meaning. A score, selected item, health amount, current quest, and settings value are all state-shaped.

using Azkar.Eda;

public static class GameState
{
    public static readonly AzState<int> Score = new(0);
}

Update the state from gameplay code:

using UnityEngine;
using Azkar.Eda;

public sealed class ScoreWriter : MonoBehaviour
{
    public void AddScore(int amount)
    {
        GameState.Score.SourceSet(GameState.Score.Value + amount);
    }
}

Bind or subscribe from a reader:

using UnityEngine;
using UnityEngine.UIElements;
using Azkar.Eda;

public sealed class ScoreStateHud : MonoBehaviour
{
    [SerializeField] private UIDocument document;

    private readonly AzEventTokenBag _tokens = new();
    private Label _scoreText;

    private void OnEnable()
    {
        _scoreText = document.rootVisualElement.Q<Label>("scoreText");
        GameState.Score.Subscribe(OnScoreChanged).AddTo(_tokens);
    }

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

    private void OnScoreChanged(int score)
    {
        _scoreText.text = score.ToString();
    }
}

Use AzEvent when you only care that something happened. Use AzState<T> when late subscribers should know the current value.

Token Cleanup Pattern

Every subscription returns a token. A token is one subscription handle. A bag owns many tokens.

using UnityEngine;
using Azkar.Eda;

public sealed class PlayerPresenter : MonoBehaviour
{
    private readonly AzEventTokenBag _tokens = new();

    private void OnEnable()
    {
        PlayerEvents.Damaged.Subscribe(OnDamaged).AddTo(_tokens);
        PlayerEvents.Healed.Subscribe(OnHealed).AddTo(_tokens);
        PlayerState.Health.Subscribe(OnHealthChanged).AddTo(_tokens);
    }

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

    private void OnDamaged(int amount) { }
    private void OnHealed(int amount) { }
    private void OnHealthChanged(int health) { }
}

Prefer this over trying to manually remember every -= or every individual token field.

First Tracking Pass

Tracking is editor observability. It helps you answer:

  • Which buses and states exist?
  • Which handlers are connected?
  • Which publishers fired recently?
  • Why is a UI not receiving updates?

Start with authored attributes:

using Azkar.Eda;

[AzEdaTrack("Game Events", Role = AzEdaTrackRole.Bus, Group = "Game")]
public static class GameEvents
{
    [AzEdaTrack("Score Changed", Role = AzEdaTrackRole.Event, CaptureValues = true)]
    public static readonly AzEvent<int> ScoreChanged = new();
}

Then:

  1. Open Window/Azkar EDA/Tracking.
  2. Start or refresh tracking in the window.
  3. Enter Play Mode.
  4. Publish an event or change a state.
  5. Inspect the Dashboard, Graph, Explorer, and State views.

For the full tracking workflow, see Tracking And Debugging.

What To Read Next

Read Choosing The Right Primitive before adding many buses. It will save you from using events where state would be clearer, or unordered events where a priority event would be safer.