Unity — uEvent & ScriptableObjects

Samuel Asher Rivello
5 min readJul 17, 2020

Unity3D is a powerful suite of tools (Project IDE, Code IDE, and run-time) for game development. Unity supports several languages, but the community consensus is to use only C#.

I am a Unity Game & Tools Developer with over 20 years of game development experience. I am available for hire (Remote, Contract).

SamuelAsherRivello.com

The Observer Pattern

What is an observer pattern? It is a code set up that defines a one-to-many relationship such that when one object changes state, the others are notified and updated automatically.

Terms

  • Subject — The object being observed
  • Observer — The object listening to and reacting to any event about the Subject
  • Event — The notification saying “something changed”
  • Invoke — The action of sending the event
  • AddListener — The action to listening for the event

Key Concepts

  • Decoupling — The Subject does not ‘know’ that it is being observed. It says “something changed, here are the details if anyone cares!”.

UEvent

Based on years of architecture experience in Unity Projects (See my Architecture articles Part 1 and Part 2) I have seen communication between runtime objects as a notorious cause of scalability and maintainability issues within a codebase. Elegantly solving communication in a game project is worth its weight in gold.

Here is the latest and greatest library I created using the aforementioned Observer Pattern, SOLID, KISS, and the unique benefits of Unity’s ScriptableObject and Unity’s UnityEvent. It is called UEvent and contains the UEvent namespace. As the “U” hints, this library is designed specifically for use with the Unity platform.

Example #1 : UnityEvent

Let’s start with a vanilla Unity example without my custom library.

This is not a strict usage of the observer pattern because here the Subject’s Inspector UI ‘knows’ which Observer is listening. Still, this is a good start for decoupling. It is also a good starting point for comparison to the more complex examples further below.

Subject — Inspector

Subject — Code

namespace RMC.Core.UEvents.Examples
{
public class UEventDemoSubject : MonoBehaviour
{
[SerializeField]
private UEvent _onAwaked = new UEvent();
protected void Awake ()
{
_onAwaked.Invoke(null);
}
}
}

Observer— Inspector

Observer — Code

namespace RMC.Core.UEvents.Examples
{
public class UEventDemoObserver : MonoBehaviour
{
public void SayHelloWorld()
{
Debug.Log($"{this.GetType().Name} Hello World!");
}
}
}

Example #2 : UEventDispatcher

Here we replicate the use case from above with a few specific improvements.

  • Subject and Observer are decoupled
  • Subject has no serialized reference to the Observer, and vice-versa
  • The UEventDispatcher is a ‘global’ bus for all events in the project
  • The UEventData is the optional payload sent along with the event. Want to send custom data? Easy! Create a new class of typeIUEventData

Subject — Inspector

Subject — Code

namespace RMC.Core.UEvents.Examples
{
public class UEventDispatcherDemoSubject : MonoBehaviour
{
protected void Start()
{
UEventData uEventData = new UEventData();
UEventDispatcherSingleton.Instance.Invoke<UEvent>(uEventData);
}
}
}

Observer — Inspector

Observer — Code

namespace RMC.Core.UEvents.Examples
{
public class UEventDispatcherDemoObserver : MonoBehaviour
{
protected void Start()
{
UEventDispatcherSingleton.Instance.AddEventListener<UEvent>(
UEventDispatcherSingleton_OnSampleEvent);
}
private void UEventDispatcherSingleton_OnSampleEvent(
IUEventData uEventData)
{
Debug.Log($"{this.GetType().Name} OnSampleEvent()...\n
uEventData='{uEventData}'.");
}
}
}

Example #3 : UEventAsset

Ok, now let’s really crank up the power to 11.

Here we replicate the use case from above with a few more specific improvements

  • The UEvent concept is wrapped in a ScriptableObject of type UEventAsset

Each separate event concept now sits in the Unity Project Window ready to be reused via drag-n-drop. Awesome!

Mini-library of Events

With this pattern, a mini-library of events is created for unique use within the needs of the game project. Easy to organize, name, locate, and use. Nice!

Subject — Inspector

Subject — Code

namespace RMC.Core.UEvents.Examples
{
public class UEventAssetDemoSubject : MonoBehaviour
{
[SerializeField]
private UEventAsset _uEventAsset = null;
protected void Start ()
{
_uEventAsset.Invoke(new UEventData());
}
}
}

Observer — Inspector

Observer — Code

namespace RMC.Core.UEvents.Examples
{
public class UEventAssetDemoObserver : MonoBehaviour
{
[SerializeField]
private UEventAsset _uEventAsset = null;
protected void OnEnable()
{
_uEventAsset.AddListener(OnEvent);
}
protected void OnDisable()
{
_uEventAsset.RemoveListener(OnEvent);
}
private void OnEvent(IUEventData uEventData)
{
Debug.Log($"{this.GetType().Name} OnEvent()
uEventData='{uEventData}'. Null is ok.");
}
}
}

Why “UnityEvent”?

The UEvent solution leans heavily on the built-in Unity UnityEvent class.

Benefits

  • Consistency with Unity’s first-party patterns
  • Built-in rendering to the Unity Inspector
  • Concise, purposeful API (Mimicked in the UEvent library)

Alternatives exist such as the C# event keyword, a raw C# solution based on System.Object, or something else. While a 2016 study shows that UnityEvent operations are relatively slow, I find that the speed is acceptable. Since that time, its likely that speed improvements have been made with Unity updates. I have not retested the speeds recently.

Bonus: The UEvent library is architected with interfaces and refactoring out UnityEvent is possible if your team prefers an alternative.

References

Downloads

Complete source-code and examples are provided for you. Enjoy!

More By Samuel Asher Rivello

Available For Unity Hire: Remote, Contract

20 years of game development experience. SamuelAsherRivello.com

Comments?

Let me know! Twitter.com/srivello

--

--

Samuel Asher Rivello

Web3 Game Dev & Instructor - Unity Certified. 20+ years of game dev XP. Available: Remote, Contract. http://www.SamuelAsherRivello.com