I been searching for a good event manager for Unity3D but haven’t found one that
suited all my needs.
I wanted an event manager with:
Type-Safe Events
This makes refactoring easier by using class names as the listener type instead of
string names, typos can be prevented. This also prevents casting the base event
to the event needed.
Event Queue
An event manager centric game will have a lot of events controlling every aspect of the game. So being able to queue events for the next frame will ensure not too many events will fire at once. It will prevent the game from advancing forward too quickly (event trigger chains) and will also help with the frame rate.
For frame sensitive events, I wanted to occasionally bypass the queueing
functionally. So a direct trigger event method had to be available as well.
I ended up creating my own modified event manager from these:
An event manager is generally a singleton that triggers events from anywhere in
a game. It is a great way to decouple communication between objects by
encapsulating the communication in an event.
For example when a monster takes damage a sound should
be played and a damage number should appear on screen. Normally it would be
coded like this.
This looks perfect at the start of the project, but will quickly turn into a nightmare. What
if you don’t want damage to always be displayed? Like if the game is currently in a cutscene? And how will that object get
references to the sound manager and gui manager? Will they be passed through
every object in the game?
Game logic, like taking damage, should always be completely
decoupled from view logic like displaying points gained and sound effects.
First a specific event is created to contain all the data. In this case TakeDamageEvent. It is then queued up to be
triggered on the next frame. This prevents too many events from triggered all in
the same frame. This event is then picked up by whoever is listening for it. In
this case it would be the sound manager and gui manager.
A new event has to be created for every type of event. This probably sounds like
a lot of work but I keep all my events in one file called “events.cs”.
The view logic and game logic are now completely decoupled. This makes game
development so much easier. I have created games without an event manager and
excluding non-trivial games, have always turned into balls of spaghetti.
For those of you who don’t like global objects, neither do I but an event
manager is worth it.
Event Manager Source
AddListener: Adds listener to the given event.
AddListenerOnce: Adds listener and on the first trigger the listener is subsequently removed.
RemoveListener: Removes given listener.
HasListener: Checks if the listener is registered.
QueueEvent: Queues the even to trigger next frame.
TriggerEvent: Triggers the event to all listeners.
RemoveAll: Removes all listeners and queued events.
Local Usage
Normally the event manager is used globally and events are sent to everyone. In
some cases this behaviour is not wanted. One problem I encountered is do you how listen to animation events from a specific game object?
This seems to be the desired behaviour but what if there are multiple animations running at once?
The cutscene manager could possibly run to the next frame because a different monster finished its animation. Only
animation complete events from a specific monster is wanted. You could try to do a
simple if condition to check the wanted monster is correct but this will get
tedious and is error-prone.
So I added the event manager to the specific monster prefab. Now listeners can be added
directly to it.
Triggering Events From Animations
I just want to mention how I setup triggering event manager events from
Unity3ds
animation events. I ended up creating an AnimEvents component which converts Unity3ds events.
I use the AddListenerOnce() method because usually the entity
will be moved to an other animation and subsequent events are not needed.
Further Reading
Game Coding Complete - Has a great chapter
on global event managers. Source code can be viewed for free.