The Mighty EventBus: Or How I Stopped Wiring My Code Like a Madman

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:

  1. Components "subscribe" to events they care about (e.g., UI listens for inventory changes).
  2. Other components "publish" events when something happens (e.g., the player picks up an item).
  3. 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 :

  1. Les objets "s’abonnent" aux événements qui les intéressent.
  2. D’autres objets "publient" un événement quand quelque chose se passe.
  3. 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. 🏚️🔥