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();
_caption = _box.transform.Find("Title").GetComponent();
// ** 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;
}
}
}
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();
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.
Answer
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
toMsgBox.Awake
orMsgBox.OnEnable
Check whether
_caption
is null before accessing its members in theCaption
property's getters/setters, and populate it viaGetComponent
there if you need.Make
_caption
a public or serialized field ofMsgBox
, 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'sUpdate
(ie.yield return null
) to populate theMsgBox
instance afterStart
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.
No comments:
Post a Comment