Search Unity

UnityEvent + Disabled Objects + Awake Failure

Discussion in 'UGUI & TextMesh Pro' started by JAKJ, Sep 8, 2014.

  1. JAKJ

    JAKJ

    Joined:
    Aug 17, 2014
    Posts:
    185
    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.
     
  2. Tim-C

    Tim-C

    Unity Technologies

    Joined:
    Feb 6, 2010
    Posts:
    2,225
    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.
     
  3. JAKJ

    JAKJ

    Joined:
    Aug 17, 2014
    Posts:
    185
    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.
     
  4. JAKJ

    JAKJ

    Joined:
    Aug 17, 2014
    Posts:
    185
    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 http://docs.unity3d.com/Manual/ExecutionOrder.html 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".
     
  5. Tim-C

    Tim-C

    Unity Technologies

    Joined:
    Feb 6, 2010
    Posts:
    2,225
    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)"
     
    robal1991 likes this.
  6. JAKJ

    JAKJ

    Joined:
    Aug 17, 2014
    Posts:
    185
    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.
     
  7. RDeluxe

    RDeluxe

    Joined:
    Sep 29, 2013
    Posts:
    117
    I agree with this. I'm in the same case, using Action (delegates) instead of events, but the problem is the same. (it's also mainly for UI elements)

    Is there any way to make a script attached to inactive objet register to an Action in another script ?
     
    Jessy likes this.
  8. Delodax

    Delodax

    Joined:
    Nov 5, 2014
    Posts:
    2
    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)?