LoadLevelAdditive requires one frame to initialize scene objects.

Renamed from: Null but not null (equality override problem)?

I am dealing with a problem I just can't get to the bottom of. Actually a co-worker of mine (oddly enough) is dealing with the exact same issue on a totally unrelated area.

        GameObject sceneRoot = GameObject.Find(levelName);
        Debug.Log(sceneRoot + " "+(sceneRoot == null));

The output that this produces is

UnityEngine.GameObject True

How can this be? We have been researching this problem all day. In my case the object named "levelName" has been added to the scene graph via additive scene loading. I can see it on the scene graph as it gets added. The stack trace suggests the object is there and the null is coming from the inside of the game object. What would cause this odd behavior? Do these objects not get really initialized until the end of the frame or something?

Additional note: Trying to call GetComponent on this yields:

NullReferenceException
UnityEngine.GameObject.GetComponent[Transform] () [0x00000]

I get the NRE, but it's like its coming from withn GetComponent... my code should be at the top of the stack, not GameObject's.

Edit(8/5): This looks like the default behavior of Find upon more research. This is very strange to me:

GameObject go = null;
Debug.Log("Object "+go + " " + (go == null));
go = GameObject.Find("TestScene2");
Debug.Log("Object " + go + " " + (go == null));

Yields this:

Object  True
Object UnityEngine.GameObject True

So I can print it but it's null?

Edit(8/5 ... later in the day):

int frame = 1;
void Update()
{
    if (frame == 1)
    {
        Application.LoadLevelAdditive("TestScene2");
    }
    GameObject go = GameObject.Find("AnObjectInTestScene2");
    UnityEngine.Debug.Log(frame++ +" "+go + " " + (go == null));
}

Yields this:

1 UnityEngine.GameObject True
2 UnityEngine.GameObject False
...

So this tells me I cannot call Application.LoadLevelAdditive and use something in the scene graph immediately. Find only works on the frame after the load?

Workaround: After some more research and the comment from SpikeX I decided to use the Async version of LoadLevelAdditive, this way I am not relying on wall time to accomplish this. I just had to add some more complexity and gears to deal with the async. This would be a nice thing to get documented in the docs. The explicit presence of LoadLevelAdditiveAsync makes one believe that LoadLevelAdditive is truly sync, but it's truly not.

Your findings are correct, but as far as I know it's not a bug, it's just how Unity was designed to work. Take this test case I just tried:

using UnityEngine;
using System.Collections;

public class Test : MonoBehaviour {

    void Start()
    {
        Invoke("DoTest", 3); // just to add some delay
    }

    void DoTest()
    {
        // METHOD 1 - Doesn't work!

        //Application.LoadLevelAdditive("Scene 2");
        //GameObject go = GameObject.Find("Object B");
        //Debug.Log("GO is " + ((go == null) ? "null" : "not null"));

        // METHOD 2 - Does work :)

        StartCoroutine(TestTwo());
    }

    IEnumerator TestTwo()
    {
        Application.LoadLevelAdditive("Scene 2");
        yield return 0; // or yield return new WaitForEndOfFrame();
        GameObject go = GameObject.Find("Object B");
        Debug.Log("GO is " + ((go == null) ? "null" : "not null"));
    }
}

If you use Method 1 (which doesn't work, and is commented out), it prints `GO is null`. But if you use the current, uncommented method 2, it prints `GO is not null`. You may think this is strange behavior, but it's not.

So my understanding of how Unity works is, it takes everything you do in a script, in one frame (one Update()), and it batches it all together, and then executes that at the end of the frame. So when you tell it to load the level, and then immediately find the object, it can't, because it's added the "LoadLevelAdditive" call to its queue, but it executed the "FindObject" immediately (because it needs the reference for the script). The solution is to use a coroutine, and let one frame pass before trying to read any objects you loaded with LoadLevelAdditive.

Oh, and by the way, when you concatenate a null object with a string it just converts it to "", which is why you can still print it.

Regarding your null is not null issue, you might want to have a look at this (pretty old) forum posting of mine: Huh? varName != null is false, when varName is not null?

I ran into that issue when instantiating game objects / scripts via constructors (which is something you shouldn't do). And yeah, I think you're right: It has to do with equals ("==") being overridden for some "internal magic" ;-)