0
\$\begingroup\$

Here is the relevant code:

private void Awake()
{
    StartCoroutine(LoadScenes());
}

private IEnumerator LoadScenes()
{
    this.sceneLoad1 = SceneManager.LoadSceneAsync(Scene1, LoadSceneMode.Additive);

    while (!this.sceneLoad.isDone)
    {
        Debug.Log("Loading scene 1...");
        yield return null;
    }

    this.sceneLoad2 = SceneManager.LoadSceneAsync(Scene2, LoadSceneMode.Additive);

    while (!this.sceneLoad.isDone)
    {
        Debug.Log("Loading scene 2...");
        yield return null;
    }
}

private void Start()
{
    SceneManager.GetSceneByName(Scene1)
                .GetRootGameObjects()  
                .Where(root => root.GetComponent<God>() != null)
                .Select(root => root.GetComponent<God>()) 
                .Single()
                .SpreadGospel();
}

(Scene1 and Scene2 are const strings.)

The program works fine when running in the Unity editor. However, when I make a development build, I see the follow error message:

    InvalidOperationException: Operation is not valid due to the current state of the object
  at System.Linq.Enumerable.Single[God] (IEnumerable`1 source) [0x00057] in /Users/builduser/buildslave/mono/build/mcs/class/System.Core/System.Linq/Enumerable.cs:1968 
  at MySolution.GodInitializer.SpreadGospel () [0x00001] in D:\repos\MySolution\SourceCode\SourceCode\GodInitializer.cs:100 
  at MySolution.GodInitializer.Start () [0x00012] in D:\repos\MySolution\SourceCode\SourceCode\GodInitializer.cs:77 

... which indicates that the scene wasn't fully loaded before I tried to access the God singleton.

What changes should I make to fix the problem?

\$\endgroup\$
  • \$\begingroup\$ This looks like an unnecessarily complicated way to write FindObjectOfType<God> - or you could just have God itself provide a GetInstance() getter populated in its own Awake. \$\endgroup\$ – DMGregory Aug 3 '18 at 13:46
1
\$\begingroup\$

Your code in Start assumes that Scene1 has already loaded. But you have no guarantee that's true. Instead you can write something like this:

private IEnumerator LoadScenes()
{
    this.sceneLoad1 = SceneManager.LoadSceneAsync(Scene1, LoadSceneMode.Additive);

    while (!this.sceneLoad.isDone)
    {
        Debug.Log("Loading scene 1...");
        yield return null;
    }

    // NOW it's safe to look for God.
    OnScene1Loaded();

    this.sceneLoad2 = SceneManager.LoadSceneAsync(Scene2, LoadSceneMode.Additive);

    while (!this.sceneLoad.isDone)
    {
        Debug.Log("Loading scene 2...");
        yield return null;
    }
}

void OnScene1Loaded() {
    // Find God & spread gospel.
}

Now you know OnScene1Loaded won't get called until Scene1 has finished loading.

| improve this answer | |
\$\endgroup\$
0
\$\begingroup\$

Coroutines are not async in any way, they simply run when the object is Updated (similar to just performing the work in Update). Start is in turn called before the first update. Meaning that the scenes cannot be loaded properly since the coroutines did not finish yet (they did not even start yet).

I am not sure what you do in God initializer and why your code works in the editor, but it seems to me you are assuming some things which are not correct. E.g., the scene could not be fully loaded.

If your code in Start depends on some scene being initialized I would simply initialize it sequentially in Start (or Awake). Even if Coroutines were async or if your coroutine did run right when you "Start" it, your code suggests, that scene loading should be done async - therefore you can't rely on it being done unless you synchronize somehow (check whether the scene was loaded).

For more information about the execution order please refer to the documentation.

| improve this answer | |
\$\endgroup\$

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.