The Mighty EventBus: Or How I Stopped Wiring My Code Like a Madman
The Joy of Loose Coupling
One of the biggest joys of game development is seeing your systems work together like a finely tuned orchestra.
One of the biggest pains of game development is trying to make them communicate without turning your code into spaghetti. đ
Thatâs where EventBus comes in a magical middleman that lets my game objects talk to each other without hard dependencies!
The Problem â A World Without an EventBus
Imagine you have a UI manager that needs to update when the player collects resources.
- You could make the UI script reference the inventory system directly⊠but now itâs tightly coupled!
- What if I later change how the inventory works? Time to rewrite everything.
- What if I want multiple systems (audio, achievements, AI) to react to resource collection? Do I add a reference for each one? đ€Ż
This approach quickly turns into callback hell and a maintenance nightmare.
The Solution â Enter the EventBus
Instead of direct dependencies, the EventBus acts as a broadcaster:
- Components "subscribe" to events they care about (e.g., UI listens for inventory changes).
- Other components "publish" events when something happens (e.g., the player picks up an item).
- The EventBus delivers the message without anyone needing to know who else is listening.
Itâs like a radio stationâthe broadcaster doesnât care whoâs listening, and listeners donât care whoâs broadcasting. Pure, beautiful, decoupled communication.
How It Works â The Code
Hereâs my EventBus implementation:
using System;
using System.Collections.Generic;
namespace Valcriss.Scripts.Tools
{
public static class EventBus
{
private static readonly Dictionary<Type, List<Delegate>> EventDictionary = new();
public static void Subscribe<T>(Action<T> listener)
{
Type eventType = typeof(T);
if (!EventDictionary.ContainsKey(eventType))
{
EventDictionary[eventType] = new List<Delegate>();
}
EventDictionary[eventType].Add(listener);
}
public static void Unsubscribe<T>(Action<T> listener)
{
Type eventType = typeof(T);
if (EventDictionary.TryGetValue(eventType, out List<Delegate> value))
{
value.Remove(listener);
}
}
public static void Publish<T>(T eventToPublish)
{
Type eventType = typeof(T);
if (EventDictionary.TryGetValue(eventType, out List<Delegate> value))
{
foreach (Delegate listener in value)
{
(listener as Action<T>)?.Invoke(eventToPublish);
}
}
}
}
}
How to Use It
1. Subscribe to an event
EventBus.Subscribe<ResourceCollected>(OnResourceCollected);
2. Publish an event
EventBus.Publish(new ResourceCollected { ResourceType = "Water", Amount = 10 });
3. Handle the event
private void OnResourceCollected(ResourceCollected evt)
{
Debug.Log($"Player collected {evt.Amount} of {evt.ResourceType}");
}
Why Use It?
Loose Coupling â Systems donât need to reference each other directly.
More Flexible Code â Add or remove subscribers without modifying the event source.
Easier Debugging â Events are centrally managed, making behavior easier to track.
Final Thought â EventBus Saves My Sanity
Without an EventBus, my game would be a tangled web of dependencies. Now, systems communicate effortlessly, and I can focus on more important things like making sure my survivors donât starve in my post-apocalyptic bunker. đïž
LâEventBus : Mon Sauveur Contre le Code Spaghetti
Le ProblĂšme â Un Monde Sans EventBus
Faire communiquer les systĂšmes dâun jeu peut vite devenir un cauchemar. Imaginons que lâinterface utilisateur doive se mettre Ă jour quand le joueur collecte des ressources :
- On pourrait faire un appel direct Ă lâinventaire⊠mais bonjour le couplage fort !
- Et si je modifie le systĂšme dâinventaire plus tard ? Tout casse.
- Et si plusieurs systĂšmes (UI, son, succĂšs) doivent rĂ©agir ? Jâajoute une rĂ©fĂ©rence pour chacun ?! đ€Ż
Sans structure, ça devient vite une horreur de callbacks imbriqués et de dépendances en cascade.
La Solution â LâEventBus Ă la rescousse !
LâEventBus est une station radio pour mon jeu :
- Les objets "sâabonnent" aux Ă©vĂ©nements qui les intĂ©ressent.
- Dâautres objets "publient" un Ă©vĂ©nement quand quelque chose se passe.
- LâEventBus transmet le message sans que lâexpĂ©diteur et le rĂ©cepteur aient besoin de se connaĂźtre.
Plus besoin de coupler directement les systÚmes, tout est découplé et propre.
Le Code â Mon EventBus
using System;
using System.Collections.Generic;
namespace Valcriss.Scripts.Tools
{
public static class EventBus
{
private static readonly Dictionary<Type, List<Delegate>> EventDictionary = new();
public static void Subscribe<T>(Action<T> listener)
{
Type eventType = typeof(T);
if (!EventDictionary.ContainsKey(eventType))
{
EventDictionary[eventType] = new List<Delegate>();
}
EventDictionary[eventType].Add(listener);
}
public static void Unsubscribe<T>(Action<T> listener)
{
Type eventType = typeof(T);
if (EventDictionary.TryGetValue(eventType, out List<Delegate> value))
{
value.Remove(listener);
}
}
public static void Publish<T>(T eventToPublish)
{
Type eventType = typeof(T);
if (EventDictionary.TryGetValue(eventType, out List<Delegate> value))
{
foreach (Delegate listener in value)
{
(listener as Action<T>)?.Invoke(eventToPublish);
}
}
}
}
}
Comment lâutiliser ?
1. Abonner une fonction à un événement
EventBus.Subscribe<ResourceCollected>(OnResourceCollected);
2. Publier un événement
EventBus.Publish(new ResourceCollected { ResourceType = "Water", Amount = 10 });
3. GĂ©rer lâĂ©vĂ©nement
private void OnResourceCollected(ResourceCollected evt)
{
Debug.Log($"Player collected {evt.Amount} of {evt.ResourceType}");
}
Pourquoi câest gĂ©nial ?
Code dĂ©couplĂ© â Aucun systĂšme ne dĂ©pend directement des autres.
Extensible â Ajouter un nouvel abonnĂ© ne change rien au reste du code.
Facile Ă debugger â Tout est centralisĂ© et traçable.
Conclusion â Vive lâEventBus !
GrĂące Ă cet outil magique, mon jeu ne ressemble plus Ă une boĂźte de spaghettis. Tout communique proprement, et moi, je peux me concentrer sur la survie de mes pauvres rĂ©fugiĂ©s au lieu de dĂ©boguer un monstre de dĂ©pendances. đïžđ„