Driving Unity3d with c# eventing, the good and the bad.
Here is a design-pattern I like a lot, I call it an EventBus and it goes something like this (don’t copy-paste it’s off the top of my head):
public class GenericEvent
{
public UnityEngine.Object sender;
public string token;
public System.Object data;
}
public static class EventBus
{
public delegate void GenericEventHandler(GenericEvent genEvent);
//listener delegates can be registered here
public static GenericEventHandler m_handler;
//dispatchers can use this method
public static void Dispatch(GenericEvent genEvent)
{
if(m_handler != null) m_hander(genEvent);
}
}
This is nice because any MonoBehaviour can ‘talk’ to another MonoBehaviour without having to ‘know’ that MonoBehaviour (again, code off the top of my head):
//MonoBehaviour 'foo'
const string FOO_COMPLETED = "foo completed task";
void Update()
{
...do some stuff
EventBus.Dispatch(new GenericEvent(){
sender=this,token=FOO_COMPLETED, null
});
}
//MonoBehaviour 'bar'
void Start()
{
EventBus.handle += HandleEvent;
}
void OnDestroy()
{
EventBus.handle -= HandleEvent;
}
void HandleEvent(GenericEvent genEvent)
{
if(genEvent.token == Foo.FOO_COMPLETED) ...do some stuff
}
The good
- no more remembering to drag-and-drop behaviours onto referencing ‘inlets’ in the editor.
- no need to give every MonoBehaviour static access to every other MonoBehaviour (and create a glue-ball of code)
- delegates are like function-pointers, calling a delegate has similar overhead to calling the method on another object.
- delegates are called on the MonoBehaviour regardless of whether the hosting GameObject is active, or the MonoBehaviour is enabled.
Observable state
by logging all the events that pass through the EventBus you can see the state of your game in real-time, making it easier to trap errors.
The bad
Events invoke delegates
- delegates are also not like function-pointers, they retain a reference to the object that they call the method on, as such you must remove a MonoBehaviours event handling delegate when the MonoBehaviour is destroyed. This is not a big problem.
- delegates are called on the MonoBehaviour regardless of the state of the MonoBehaviour, because there is no guarantee of execution order for ‘start’ methods you may find that your listener is registered before the event is dispatched. This can be a moderately big problem.
Loose coupling
- events can obfuscate your logic. This can be a big problem.

The diagram above was generated by one of my tools, it shows the connection of events in a (simple) event-driven game. It wasn’t useful. Here are a couple of the ‘gotchas’ I experienced from my obfuscated logic:
- infinite loops (and subsequent editor lock-out) hidden by several layers of indirection (A calls B calls C calls D calls A)
- race conditions, where data shared by A, B and C was not modified in the order I expected (because the order of calls to Update() etc are not guaranteed.)