embryonalcodeinjection-630x455

Embrional Code Injection in Unity and How to Use it to (Lazily) Initialize Disabled GameObjects

Disclaimer: this content is for advanced Unity developers.

Is this article not clear? Please contact me on twitter @riccardostecca

The Problem in One Sentence
You want to initialize some events (like a button’s OnClick) in an object that is not active and you banged your head against the fact that in Unity/C# you can access that UnityAction field but Unity will not call AddListener for you and you’ll end up like you have never called it.

In Unity C#, when it comes to initializing several objects in different places distributed among Unity’s hierarchy and your own class diagram, it can become tricky, in particular when Unity does its stubborn but justifiable thing of not executing code in any components of a disabled object. To expand the problem a bit, let’s think of a UI Panel containing a whole (sub) tree of buttons, sub-panels, toggles, and so on. Let’s call the parent of this UI Panel U. Somewhere else in your hierarchy there’s a typical Manager that, in its Start function, initializes a big bunch of buttons, toggles, etc in the UI. Let’s call this manager M.
Assume we only have a few buttons that we want to initialize from M. Quite commonly M also takes care of initializing the buttons’ OnClick event by filling it with one or more listeners.
For example, in the manager M we could have something like this:
m_OptionsButton.OnClick.AddListener(() => { Debug.Log(“Pressed Options.”); });

This adds a listener to the OnClick callback exposed by Unity. Note that we’re using the anonymous functions notation to add a listener in this case. Lambda functions are very powerful and handy to write code on the fly where a function (delegate) is passed as parameter. This article assumes you have some knowledge on C#’s anonymous/lambda functions.

“Cool!” you say. You test and everything works fine, the manager populates all UI’s elements properly at Start, all buttons’ OnClick events are initialized, all buttons behave properly and with no shenanigans you call it a day. Then someone (director/designer/client) tells you “Hey! this UI must start as disabled. Users don’t want to see that UI at start but only when they actively open it.” So, in Edit mode, you disable the UI and set some user interactions to enable it at run time, you test, aaaaaand your day is ruined by a storm of NullReferenceExceptions. Not fun.

What causes this?
By far the most important thing to keep in mind is that Unity allows you to set values of a disabled object but it will not execute any of its code that is called from outside.
So, for instance, if GameObject O has a component C that has a public Button fieldcalled thatButton, the following code, called from another class would be useless:

C c = O.GetComponent().thatButton.OnClick.AddListener(()=>{ Debug.Log(“clicked”); };

The devil is hidden in the OnClick.AddListener we mentioned earlier as an example (Another example using a Toggle would be OnValueVhanged.AddListener). The OnClick delegate belongs to the disabled object! thus Unity will NOT satisfy the AddListener call. It will instead just fail silently and leave your button, toggle or other UI element not properly initialized.
This happens because when anonymous functions are passed to be executed to another object’s delegate field, the body is executed in that object rather than the one that’s adding the listener.

So what do we do?
The first step that comes to mind is to find a way to initialize all UI elements belonging to U and its descendants the moment U is enabled.
Here’s where a technique I call Embrional Code Injection (ECI) comes into play. Yeah, pretty bold. I just like how it sounds exaggerated but descriptive at the same time.

Let’s put down some definitions.
Embryo: an object that starts as disabled and needs its members, including events, to be populated when it gets enabled.
Code Injection: the act of adding instructions through delegates, Action, UnityAction or UnityEvent to another object that represents method (or points at one or more functions). (To clarify, this is one type of code injection which differs from other definitions you get from tools like Zenject).

Our escape plan is carried out through the Awake callback. The reason for that is that Awake is the only callback in Unity that executes for all objects even though they’re disabled.
Thus what we can do is to use Awake to tell Unity to remember to do something when the GameObject is enabled, which means using the MonoBehaviour’s OnEnable callback.
Let’s declare a public UnityAction.

public UnityAction m_registerOnEnableAction;

This is the point where other classes will add their own code. Remember, we can access a disabled object’s components’ fields. UnityAction is nothing more than an abstraction of C#’s delegates; all this in fact can be implemented with pure delegates. Delegates, and consequently UnityActions, are method placeholders, meaning that they can call several methods that subscribed to them. More on delegates here.
We called the variable m_registerOnEnableAction: this should suggest that our subscribers will be called when Unity calls the MonoBehaviour’s Enable method.

We now want something to populate the events when MonoBehaviour’s OnEnable is called. So we create a private UnityEvent.

private UnityEvent OnEnableEvent;

We can assign OnEnableEvent in the Awake.

if(m_registerOnEnableAction != null)
{
OnEnableEvent = new UnityEvent();
OnEnableEvent.AddListener(m_registerOnEnableAction);
}

and finally use the UnityEvent’s Invoke method in the OnEnable callback.

// Remember to call base.OnEnable() from descendant classes
protected void OnEnable()
{
if(OnEnableEvent != null)
OnEnableEvent.Invoke();
}

I wrote a generic MonoBehaviour class that implements this method with all the main MonoBehaviour’s callbacks. This should not only work as an example but it’ll also show you a generalization that you can use as it is where needed.

So how do we use it? If any classes want to add their events to a disabled class we can even use MonoBehaviourWithEmbrionalCodeInjection like so.

public MonoBehaviourWithEmbrionalCodeInjection eci; // instance
eci.m_registerOnEnableAction += ()=>{ Debug.Log(“☺”); };

This will register as an action that will be assigned later, when the object it Enabled. Of course this can be done for other MonoBehaviour callbakcs.

Furthermore, we can be cleaner, meaning we can use the MonoBehaviourWithEmbrionalCodeInjection class, unregister our actions and even destroy the MonoBehaviourWithEmbrionalCodeInjection component which, at this point, can really be temporary.

Caveats
Code execution order! You must be aware that UnityAction, UnityEvent and, in general, delegates are not ordered arrays of function pointers, you cannot know in which order the subscribed listeners are called.
MonoBehaviourWithEmbrionalCodeInjection can be extended with care and descendant classes should always call base.xyz() where xyz is a MonoBehaviour’s callback like Start.
If overused, this can result in spaghetti code. Use only where it makes things cleaner.

SUMMARY

In this article we saw a method to escape the trap of Unity’s disabled game objects’s not being able to call any code. We found a way to still add listeners to various UI events like Button.OnClick or Toggle.onValueChanged or Dropdown.onValueChanged.
Hopefully this will save you some time.
Happy coding!