Both React and Unity have similar technical hurdles that projects need to solve as they scale beyond simple prototypes. They are both component driven applications, with scripts littered through their systems which need to communicate their data with each other, in React a common solution is to use Redux, a state management framework which allows commonents to register their data with a central store and allow other components to subscribe to changes with that data.
I’m going to walk through creating a very similar setup in Unity.
The outcome here will be a Redux-like event driven system in Unity. I’m going to focus on UI Menu systems in Unity for this example, however you could extend this out for other systems.
The Problem
In their simple form1 Unity UI menus become unmanageable very quickly. If you have multiple scenes all using the same menus you need to choose a way to manage menus without reauthoring them, standardly you have a few options
Prefabs - Since the prefab upgrade in 2018 (prefabs in prefabs) they are mini scenes
Menus as scenes - You additive load in a scene that has the menu. This does cause some additional overhead and workarounds, as scenes expect cameras and there should also be a “main scene” which is targeted when you call certain scripts (instantiate).
However you decide to handle your stack of menus, ultimately they’re likely to be managed by some global manager2 which will create / show / hide / destroy them. As they get created on the fly they can’t have references into the scene objects they need to be able to interact with.
Your solution to this could be some form of dependency injection (like Zenject), a colony of singletons that are tied together with spaghetti or rely on running searches in your scenes as you startup your menus to connect everything together, which break everytime someone renames them or forgets to add that essential tag3.
An alternative
You can use ScriptableObjects as event managers and data stores that can react and inform components. ScriptableObjects live in your project outside of scenes and can hold data, run code and also have events which can be subscribed to.
Ryan Hipple’s talk on this is great, and well worth a watch if you’re considering using this architecture.
UnityAtoms is an excellent ready to go pre-rolled version. In concept this is very similar to how Redux is used in React, where it acts as a global state manager, passing data around to various components.
The aim is to have an event based system with data and change events which can be subscribed to.
I’m going to take an example of a player health component. Here is a simple scene with a player, a campfire and a health bar.
There is a PlayerHealth (UnityAtom) ScriptableObject which holds the players health value, a number between 0 - 100, note that in the Changed field is another ScriptableObject which sends out events when the value changes.
Here’s the paired HealthChanged event ScriptableObject
The ScriptableObject holds the health data, an integer between 0 - 100. Components in the scene (the fire, the player or some healing item) hold references to it and change it (just like any normal integer value), eg, damage or healing, and the event component then broadcasts the changed data to any component that has registered itself; maybe you have a UI that shows the health number or a post processing effect that flashes the screen white on healing or progressively getting darker between 10 and zero. Maybe the player controller also needs to alter the walk speed as health decreases, no more passing references around between game and ui managers, any component can just subscribe to the data that it needs in a single global place. Its available in editor to be linked in all scenes or prefabs.
Here you can see the campfire component IntValueChangerTrigger.cs which decrements the player health by 35 whe the player enters its trigger and also emits a particle effect. Incidentally the particle effect could have been connected up separately with its own event listener component.
This is the UI Canvas component which displays the health bar ExGlobalNavbarMenu.cs, connected up to receive any changes broadcast by the health change event. Having the health data as a scriptable object means that it’s not tied to a scene and prefabs can hold references to them. Here the ExGlobalNavbarMenu is a prefab that has been instantiated at runtime and its prefab has a serialised reference to the health data. This can really help organise your workflow, allowing your prefabs a path to communicate with other components in the scene without having to do any runtime reference injection.
UnityAtoms allows you to drag and drop these exposed references into components that need them, the references all get serialised ready for builds.
This is a blessing, but beware, it can also be a debug issue waiting to bite you. Imagine you’ve dragged in the incorrect ScriptableObject into your health field and now the sound slider is changing your players health.
There are a lot of ways this architecture can help organise your project’s data, my simple example project is on github at
https://github.com/makeplayhappy/reactive-menu-system
And there are a couple of great projects from Unity which use similar ScriptableObject Event driven workflows;
https://github.com/UnityTechnologies/PaddleGameSO
and the Unity Open Project #1
https://github.com/UnityTechnologies/open-project-1/tree/devlogs/2-scriptable-objects
There’s also a comprehensive PDF guide from Unity
https://resources.unity.com/games/create-modular-game-architecture-with-scriptable-objects-ebook?ungated=true
Addressables Beware!
One caveat is if you’re mixing ScriptableObjects with the addressables system in Unity you can end up having multiple versions of the same scriptable objects instantiated across addressable groups, so if you’re using addressables I recommend checking out the solution that the Unity Open Project “Chop Chop” used, having an initial addressable scene that handles loading in various scenes and keeping their scriptable objects alive.
For another example of this solution Chop Chop, a Unity backed community project used a very similar solution, you can checkout their setup here:
Lots of GameObjects with Canvases being enabled and disabled
I can recommend the https://github.com/YousicianGit/UnityMenuSystem system to manage your menus, a repurposed version is in the Unity UI Extensions project https://github.com/Unity-UI-Extensions/com.unity.uiextensions
Finding objects in scenes relies on their name or tag or caching their internal ids.