An official pattern for managing events and state in Unity.
In going through a number of Unity tutorials awhile ago, I quickly determined that the common practice of sharing state between game objects through direct reference was going to be unsustainable (leading to spagetti-code).
At that time, my search for standard state management solutions in Unity turned up precious little. So, I turned to my web development background and built a state management solution using the Flux pattern; in particular modeled on Redux.
Unity: More Redux-Like Patterns
Exploring a robust and easy-to-troubleshoot approach to managing application state across an Unity application.
More recently, however, I bumped into the answer that I was originally looking for; an official solution for scalable state management in Unity.
Events: Creating a simple messaging system - Unity
As an introduction to UnityActions and UnityEvents, we will create a simple messaging system which will allow items in…
Went ahead and wrote a simple application using this approach (with EventManager) and thought to share my experience.
The final solution is available for download.
The application consists of a ball, an arena (floor and four walls), a pillar in the middle of the arena, a score indicator, and a start button. The game opens with the ball being stationary. Pressing the Start button sends the ball moving. Each time the ball hits the pillar the score indicator is incremented by one.
Breaking down this application into events and state, we have:
- The START event; initiated by the Start game object, observed by the Start and Ball game objects
- The COLLISION event; initiated by the Pillar game object, observed by the Score game object
- The score (integer) state; updated along-side of the COLLISION event, consumed by the Score game object
The heavy-lifting is done in the EventManager script (on the EventManager game object). The official documentation provides an explanation of the construction of this script; the usage is as follows:
EventManager.StartListening(string eventName, UnityAction listener)
Use when a game object is enabled (OnEnable method); once called, when events of type eventName occur, the listener (a method of the game object) is called.
EventManager.StopListening(string eventName, UnityAction listener)
Use when a game object is disabled (OnDisable method); once called, will stop the behavior enabled by StartListening
Use to trigger an event of type eventName.
Because these events do not carry any payload, we need an alternative way of sharing state (global) between game objects. The simplest solution is to create a public static class with public static fields.
Assets / Global.cs
The ball observes the START event.
Assets / Ball.cs
The start button triggers START events; and also observes START events.
Assets / Start.cs
Triggering Example with State Change
The pillar updates the score and triggers COLLISION events.
Assets / Pillar.cs
Just looking at this simple example; there are some problems:
- Having to both update the score and triggering COLLISION events separately in the pillar game object is fragile, i.e., it would be easy to make a mistake to only do only one of the two
- It is difficult to ascertain which events exists throughout the application, e.g., one would have to search the code to find all the calls to TriggerEvent to determine the event types.
One solution is to create public static methods on the Global class to trigger events; i.e., never do you directly call TriggerEvent from a game object.
While I am a bit frustrated that it took this long to find this event and state management pattern, I am happy to have found it now. Hope you likewise find this pattern useful.