Event System
Lightweight publish/subscribe system for decoupled communication between game systems.
Source:
Assets/Scripts/Events/
Overview
Event channels allow systems to communicate without direct references. A sender raises an event, and any number of subscribers receive it.
flowchart LR
A[Sender] --> B[Event Channel]
B --> C[Subscriber 1]
B --> D[Subscriber 2]
B --> E[Subscriber N]
Event Channel Classes
The project uses simple C# classes (not ScriptableObjects) for events.
Source:
EventChannel.cs
EventChannel<T>
Generic event channel for typed events.
public class EventChannel<T>
{
private event Action<T> Handlers;
public void Raise(T value) => Handlers?.Invoke(value);
public void Subscribe(Action<T> handler) => Handlers += handler;
public void Unsubscribe(Action<T> handler) => Handlers -= handler;
public void Clear() => Handlers = null;
}
EventChannel (Void)
For events with no payload.
public class EventChannel
{
private event Action Handlers;
public void Raise() => Handlers?.Invoke();
public void Subscribe(Action handler) => Handlers += handler;
public void Unsubscribe(Action handler) => Handlers -= handler;
public void Clear() => Handlers = null;
}
Event Registries
Events are organized into static registry classes by category.
GameplayEvents
public static class GameplayEvents
{
// Arena
public static readonly EventChannel<int> PlayerDamaged = new();
public static readonly EventChannel<int> PlayerHealed = new();
public static readonly EventChannel<EnemyController> EnemySpawned = new();
public static readonly EventChannel<EnemyController> EnemyDespawned = new();
public static readonly EventChannel ArenaDefeatSequenceRequested = new();
public static readonly EventChannel ArenaVictorySequenceRequested = new();
// Stats
public static readonly EventChannel<int> GoldChanged = new();
public static readonly EventChannel<int> ExperienceChanged = new();
public static readonly EventChannel<int> LevelChanged = new();
// State
public static readonly EventChannel GamePaused = new();
public static readonly EventChannel GameResumed = new();
public static readonly EventChannel<GameState> GameStateChanged = new();
public static readonly EventChannel<ArenaState> ArenaStateChanged = new();
public static void Clear() { /* clears all events */ }
}
AudioEvents
public static class AudioEvents
{
public static readonly EventChannel<string> MusicRequested = new();
public static readonly EventChannel<string> SfxRequested = new();
public static readonly EventChannel<string> UISfxRequested = new();
public static readonly EventChannel<string> AmbienceRequested = new();
public static void Clear() { /* clears all events */ }
}
SystemEvents
public static class SystemEvents
{
public static readonly EventChannel SettingsSaveRequested = new();
public static readonly EventChannel SettingsLoadRequested = new();
public static readonly EventChannel PlayerSaveRequested = new();
public static readonly EventChannel PlayerLoadRequested = new();
public static readonly EventChannel<Locale> LocaleChangeRequested = new();
public static void Clear() { /* clears all events */ }
}
Usage Examples
Subscribing to Events
public class GoldDisplay : MonoBehaviour
{
private void OnEnable()
{
GameplayEvents.GoldChanged.Subscribe(UpdateDisplay);
}
private void OnDisable()
{
GameplayEvents.GoldChanged.Unsubscribe(UpdateDisplay);
}
private void UpdateDisplay(int newGold)
{
goldLabel.text = newGold.ToString();
}
}
Raising Events
// When enemy dies
GameplayEvents.EnemyDespawned.Raise(controller);
// When gold changes
GameplayEvents.GoldChanged.Raise(newGoldAmount);
Requesting Audio
Best Practices
| Rule | Reason |
|---|---|
| Always unsubscribe in OnDisable | Prevents memory leaks and null reference errors |
| Keep handlers lightweight | Long handlers block other subscribers |
| Match subscribe/unsubscribe methods | Using different method references causes leaks |
Correct Pattern: Method Reference
// Correct - same method reference for subscribe and unsubscribe
private void OnEnable() => GameplayEvents.GoldChanged.Subscribe(HandleGoldChanged);
private void OnDisable() => GameplayEvents.GoldChanged.Unsubscribe(HandleGoldChanged);
private void HandleGoldChanged(int gold) { /* ... */ }