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\$