3
\$\begingroup\$

I am trying to create a MessageBox in Unity, but I'm having some troubles in C#.. This is my code (I'll explain my problem later):

MsgBox.cs

public class MsgBox : MonoBehaviour
{
    private UIButton _btn_ok;  // NGUI Button
    private UILabel _caption;  // NGUI Label
    private GameObject _box;   // The message box container

    void Start()
    {
        _box = this.gameObject;
        _btn_ok = _box.transform.Find("btn_ok").GetComponent<UIButton>();
        _caption = _box.transform.Find("Title").GetComponent<UILabel>();

        // ** the line below works fine, but I don't want to change the caption here **

        // this.Caption = "This Works";
    }

    public string Caption
    {
        get
        {
            return _caption.text;
        }

        set
        {
            _caption.text = value;
        }
    }
}

My Saved Prefab Unity MessageBox

As you can see, my prefab have the MsgBox.cs attached to the Box component (wich is the _box variable).

But this code doesn't work:

Fail Code

function createBox()
{
    GameObject wnd = (GameObject)Instantiate(Resources.Load("Box"));
    MsgBox mb = wnd.GetComponent<MsgBox>();
    bool foo = true;

    if (mb == null)
    {
        Debug.Log("MsgBox is null");
        foo = false;
    }

    if (!(mb is MsgBox))
    {
        Debug.Log("mb is not MsgBox");
        foo = false;
    }

    if (foo)
    {
        Debug.Log("passed!");
        mb.Caption = "Hello!";
    }
}

Console Output

passed!
NullReferenceException: Object reference not set to an instance of an object MsgBox.set_Caption (System.String value) (at Assets/scripts/MsgBox.cs)

If I change this:

public string Caption
{
    get
    {
        return _caption.text;
    }

    set
    {
        _caption.text = value;
    }
}

to this:

public string Caption
{
    get
    {
        return _caption.text;
    }

    set
    {
        if (_caption == null)
            Debug.Log("Caption is null");
        else
            _caption.text = value;
    }
}

and call createBox, the console give me this:

Caption is null

Why? Why I can't change the caption from outside the MsgBox? Maybe the createBox() is being called first than Start()?

PS. Using Unity 5 64 bit. My platform is WebGL.
PS². English is not my native language, sorry.

\$\endgroup\$
  • 1
    \$\begingroup\$ btw as far as I remember, if you keep reference as public UILabel in prefab then it will retain the reference as it is the child of your prefab. You won't need to GetComponent in Start \$\endgroup\$ – Hamza Hasan Jan 26 '16 at 11:17
2
\$\begingroup\$

I notice that you're instantiating a new MsgBox instance and then immediately trying to use its members.

At this point, all you can guarantee has run are its Awake() and OnEnable() methods (in that order).

Start() is called just before its first Update(), which won't happen until after the function that spawns it has returned (or yielded).

Since Start() hasn't had a chance to run yet, _caption is still null.

This gives you several possible fixes, in approximate order of increasing complexity:

  • Move the initialization from MsgBox.Start to MsgBox.Awake or MsgBox.OnEnable

  • Check whether _caption is null before accessing its members in the Caption property's getters/setters, and populate it via GetComponent there if you need.

  • Make _caption a public or serialized field of MsgBox, and wire it up in the inspector so it's already populated when you instantiate the "Box" prefab.

  • Make createBox() a coroutine that yields after spawning the box, and resumes at the end of this frame's Update (ie. yield return null) to populate the MsgBox instance after Start has had a chance to run.

Really, the easiest thing is just to use Awake or OnEnable. The only reason I mention the other possible fixes is that if we get into the habit of putting everything in these methods, then it becomes very difficult to move something even earlier when we run into order of execution bugs. So, I try to be sparing and only use those when I need to. If this is the only entanglement for this class though, then there's little risk.

\$\endgroup\$
  • \$\begingroup\$ Moved from Start() to Awake() and finally worked like a charm. \$\endgroup\$ – Kiritonito Jan 26 '16 at 13:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.