Object A is active at scene load and contains UnityEvent.
Object B is inactive at scene load and contains function referenced by UnityEvent in Object A.
Object A invokes its UnityEvent while Object B is still inactive.
In this situation, instead of Awake() being called on Object B and the function being invoked, there is instead a null-reference exception inside the UnityEvent (“Object reference not set to an instance of an object”) because Awake() was not invoked.
NullReferenceException: Object reference not set to an instance of an object ResolutionDisplayManager.Refresh () (at Assets/Scripts/Display/Options/ResolutionDisplayManager.cs:19) UnityEngine.Events.InvokableCall.Invoke (System.Object[ ] args) (at C:/BuildAgent/work/d63dfc6385190b60/Runtime/Export/UnityEvent.cs:110) UnityEngine.Events.InvokableCallList.Invoke (System.Object[ ] parameters) (at C:/BuildAgent/work/d63dfc6385190b60/Runtime/Export/UnityEvent.cs:563) UnityEngine.Events.UnityEventBase.Invoke (System.Object[ ] parameters) (at C:/BuildAgent/work/d63dfc6385190b60/Runtime/Export/UnityEvent.cs:714) UnityEngine.Events.UnityEvent.Invoke () (at C:/BuildAgent/work/d63dfc6385190b60/Runtime/Export/UnityEvent_0.cs:53) ViewportManager.Refresh () (at Assets/Scripts/Managers/ViewportManager.cs:76) ViewportManager.Start () (at Assets/Scripts/Managers/ViewportManager.cs:39)
Should I report this as a bug, or is this intended behaviour? According to the Unity manual: “(If a GameObject is inactive during start up Awake is not called until it is made active, or a function in any script attached to it is called.)” So it looks like this should be working, and when I start the game with Object B active instead of inactive, it does work.
The UnityEvent system doesn’t care if an object is Active or InActive and just sends the desired message. It looks like the error is in your code (Assets/Scripts/Display/Options/ResolutionDisplayManager.cs:19) where you are making an assumption that something is ‘valid’. You could check that the object is active before allowing the call to proceed. We are debating only allowing messages to be sent to active GameObjects / Behaviours.
Why can’t you just add to UnityEvent the same logic you already have that automatically runs Awake/Start on an inactive GameObject when one of its MonoBehaviours is called?
The use-case here is for having submenus in memory (resolution settings, in this case) that stay updated based on the nice event system (the thing detecting the change broadcasts to the things that care about the change), but the resolution menu is not the first menu the player sees so it starts inactive. And it’s silly to instantiate/destroy menus every time the user changes them.
I solved it in this instance by making the reference public and assigning it in the inspector instead of using GetComponent in Awake, but it would really be annoying to not be able to use init code with message receivers since we can’t use actual C# constructors.
If you make it so that inactive objects can’t receive messages, I’ll have to dump the entire UnityEvent system and its nice inspector and use delegates instead, and I would not enjoy that.
Actually, you know what? I just tested this with regular method calls with no events in use at all, and even then Awake/Start were not called. Direct method invocation from one MonoBehaviour to another within Update, and the inactive object did not have Awake or Start called but just had the method called directly.
So actually, this is a flaw in the documentation, and the manual page Unity - Manual: Order of execution for event functions which says “If a GameObject is inactive during start up Awake is not called until it is made active, or a function in any script attached to it is called.” is actually a lie. Or else it’s a bug that this isn’t happening.
Honestly, I don’t know what thing I should be reporting here. I wish there were a project-wide setting that says “call awake/start on inactive objects”.
This is a bit misleading, it should say:
“If a GameObject is inactive during start up Awake is not called until it is made active, or an Unity lifetime function in any script attached to it is called. (OnEnable, OnCollisionEnter)”
Okay, that makes sense now, so I’ll just have to rework my code to not be predicated on Awake/Start having been run.
But please don’t make UnityEvent require active objects: Let us have the option to use them instead of clunky delegates and we can be responsible for the setup code.
A follow-up question on this subject: once I activate a GameObject, does the Awake function run right away synchronously? I mean, can I after on the line after activating a GameObject run code which relies on it being initiated correctly (i.e. the Awake function having been run)?