I'm trying to make a centralized event system for my unity game - a central hub where other parts of the game can register/broadcast their own types of events. This means I have a singleton GameEventSystem that is globally accessible.
This is an example structure:
I have tried to combine multiple sources, trying to figure out how to do this, and this is where I got.
I have a Dictionary where the key is Type(that supposed to be T : GameEvent), and the value is GameEventListener<GameEvent>, GameEventListener is supposed to be Action<T : GameEvent>.
What I'm trying to achieve is a singleton class that manages Dictionary<T : GameEvent, GameEventListener<T : GameEvent>>.
The implementation of RegisterListener:
Type eventType = typeof(T); GameEventListener<GameEvent> listenerTemp; if (EventListeners.TryGetValue(eventType, out listenerTemp)) { listenerTemp += (GameEventListener<GameEvent>)listener; EventListeners[eventType] = listenerTemp; // Copy the newly aggregated listener back into the dictionary. } else { listenerTemp += (GameEventListener<GameEvent>)listener; EventListeners.Add(eventType, listenerTemp); } And this would be the UnregisterListener:
Type specificGameEventType = typeof(T); if (!AnyListeners(specificGameEventType, this.EventListeners)) { // No one is listening, we are done. return; } EventListeners[specificGameEventType] -= (GameEventListener<GameEvent>)listener; Passing an Action<UnitDiedEvent> to RegisterListener will lead to InvalidCastException: Specified cast is not valid., at the following line:
listenerTemp += (GameEventListener<GameEvent>)listener; Why can't I cast a GameEventListener<UnitDiedEvent> class, to GameEventListener<GameEvent>?
Is there a better approach to achieve a centralized event system in unity?
From my research I can't just make GameEventSystem a static class and expect it to be accessed by the rest of the game objects/components. That makes global systems/managers like this one a little problematic to instantiate and access.
I think I'm not fully grasping events/delegates and generics as a whole to tackle this on my own.
GameObject,Physics,Graphics,Mathf,Application,Screen, etc. are not derived from MoboBehaviour and can still be accessed from objects in the scene. You've read the covariance examples from the link, which show you can assign a value of a more derived type to a variable of a less derived type. Now read the contravariance examples where they show that delegate arguments follow the opposite rule: they assign anAction<Object>value (less derived argument) to anAction<String>variable (more derived argument), the opposite of what you've described in your comment. \$\endgroup\$