Category
Software Engineering

Published
July 23, 2017

Read
3 min

Tweet
Share
Comment

The Event Aggregator Pattern in Unity

Article Purpose

The purpose of this article is to visit a loosely coupled approach in Unity for game/application-wide communication. In using the Event Aggregator design pattern with C#’s Action class, we have a killer combo for creating a simple, efficient, and flexible eventing approach that works across our entire game or application.

If you use public properties just to wire updates between components (which you should not), then you really need to read on.

Event Aggregator

The Event Aggregator design pattern is simple to implement and to understand, especially if you are familiar with the Observer design pattern. With the Event Aggregator, many different game/application-wide changes (events or actions) can be managed with a single object. I have personally found this pattern to be extremely useful in projects where two distinct and recurring questions present themselves. These questions are:

How do disparate (and often numerous) elements in the game/app:

  1. Share responsibility for communicating a particular change?
  2. Update themselves based on a shared interest in a particular change?

The Event Aggregator pattern is a solid approach for solving both problems. It does so by aggregating the access to all/many of the shared interest events. This empowers any object in your game or application to:

  1. Dispatch specific events
  2. React to specific events

With great power comes great responsibility however. This approach can be abused, so use it with care. There are situations where the Observer pattern has a best fit.

Unity Example

My personal approach takes three steps:

  1. Create the Event Aggregator class (I call mine Signals.cs)
  2. Define worthy game/application-wide events and their signatures
  3. Codify these signatures in an API Chunk

An API Chunk is simply a grouping of an event Action with an accompanying method for triggering the action. Here is an example:


public static event Action<int> OnChangeLevel;
public static void ChangeLevel(int levelIndex){ if(OnChangeLevel != null) OnChangeLevel(levelIndex); }

For the game Crossy Word that I made, you can see two separate runtime actions should, and do, trigger the above ChangeLevel() method call.

Crossy Word Action Change Level Button

1. Signals.ChangeLevel() via user interaction

Crossy Word Action Change Level Auto

2. Signals.ChangeLevel() via level completion

In the images above, the UI menu managing class, the game's word tiles managing class, and the game state model managing class are all interested in the Signals.ChangeLevel() call. Additionally, both the UI menu and the word tiles managing classes, share responsibility for making the call. With the Event Aggregator pattern, shared interest and responsibility is trivially accomplished. The code snippet for reacting to the call is super simple as well:


void OnEnable() {
    Signals.OnChangeLevel += OnChangeLevel;
}

All that is left, is the implementation of the OnChangeLevel(int levelIndex) handler. The various classes that are interested in knowing about the action simply have the following:


void OnChangeLevel(int levelIndex) {
    Debug.Log("Change Level to " + levelIndex);
}

It is worth noting that you should clean up after yourself when the action is no longer of interest. You can do so like this:


void OnDisable() {
    Signals.OnChangeLevel -= OnChangeLevel;
}

Conclusion

As you can see the Event Aggregator pattern is super simple to implement and extremely powerful and useful for game/application-wide communication. You can build off my example above to better fit your code architecture and/or your team size. Here are a few ideas:

  • Leverage namespacing and/or multiple Signals.cs classes to group event/action types
  • Better control access by opting for a protected vs public accessor
  • Use inheritance vs the static keyword

As always, if you have any thoughts or comments, don't hesitate to contact me on Twitter @derekknox.