SceneManager Active Scene is previous scene after loading and running new scene?

So I load a new scene and have a coroutine that should do some work once the next scene is loaded, the scene is loaded and I can see that in play mode and the editor says it’s the new scene. Yet when I call FindObjectsOfType it returns objects from the previous scene. So I set it to waituntil active scene is x scene and logged the active scene and the active scene from SceneManager.GetActiveScene() is always the previous scene that was unloaded?

I load the scene this way:

public static IEnumerator LoadAsyncSceneFromArgument(string s)
    {
        AsyncOperation asyncLoad = SceneManager.LoadSceneAsync(s, LoadSceneMode.Single);

        // Wait until the asynchronous scene fully loads
        while (!asyncLoad.isDone)
        {
            yield return null;
        }
        SceneManager.SetActiveScene(SceneManager.GetSceneByName(s));
    }

This is the method that never gets the new scene which is clearly the active scene as reflected by play mode, scene mode and the hierarchy tab:

public IEnumerator RunMultiSceneCutscene(List<CutsceneToRun> c)
    {
        cutscenesToRun = c;
        // Using a for loop to iterate through the list of cutscenes
        for (int index = 0; index < cutscenesToRun.Count; index++)
        {
#if UNITY_EDITOR
            Debug.Log($"MULTICUTSCENE {cutscenesToRun[index].cutsceneName} in scene {cutscenesToRun[index].sceneName}");
#endif
            bool done = false;
            var cutsceneData = cutscenesToRun[index];  // Access the current cutscene by index

            // Check if this is the last cutscene in the list
            if (index == cutscenesToRun.Count - 1)
            {
                activeSaveData.LoadTemporaryPlayerPosition = true;
            }

            // if (cutsceneData.sceneName != currentScene)
            if (cutsceneData.sceneName != SceneManager.GetActiveScene().name)
            {
#if UNITY_EDITOR
                Debug.Log($"MULTICUTSCENE Requesting scene change to {cutsceneData.sceneName} from {currentScene}");
#endif
                AreaExit areaExit = FindObjectOfType<AreaExit>();
                yield return areaExit.ExitSceneWithoutLoadScreenUsingSaveData(cutsceneData.sceneName);
#if UNITY_EDITOR
                Debug.Log($"MULTICUTSCENE Scene change to {cutsceneData.sceneName} from {currentScene} complete");
#endif
                done = true;
                #if UNITY_EDITOR
                Debug.Log($"MULTICUTSCENE Waiting for scene to load");
#endif
                yield return new WaitUntil(() => isSceneLoaded == true);
#if UNITY_EDITOR
                Debug.Log($"MULTICUTSCENE Scene loaded");
#endif
            }
            // else
            // {
#if UNITY_EDITOR
            Debug.Log($"MULTICUTSCENE Running cutscene {cutsceneData.cutsceneName} in scene {cutsceneData.sceneName} in scene: {SceneManager.GetActiveScene().name}");
#endif
            yield return new WaitUntil(()=>SceneManager.GetActiveScene().name == cutsceneData.sceneName);
                var cutScenes = FindObjectsOfType<CutScene>();
                CutScene cutScene = null;
                for (int i = 0; i < cutScenes.Length; i++)
                {
#if UNITY_EDITOR
                    Debug.Log($"MULTICUTSCENE Checking cutscene {cutScenes[i].name}");
#endif
                    if (cutScenes[i].name == cutsceneData.cutsceneName)
                    {
                        cutScene = cutScenes[i];
                    }
                }
                
                if (cutScene == null)
                {
                    Debug.LogError($"Cutscene {cutsceneData.cutsceneName} not found in scene {cutsceneData.sceneName}");
                }
                else
                {
                    // cutScene.ExecuteCutSceneWithCallback(() => done = true);
                    yield return cutScene.ExecuteCutSceneCoroutineWithCallback(() => done = true);
                }
            // }
#if UNITY_EDITOR
            Debug.Log($"MULTICUTSCENE Waiting for done to be marked complete");
#endif
            yield return new WaitUntil(() => done == true);
#if UNITY_EDITOR
            Debug.Log($"MULTICUTSCENE Done marked complete");
#endif
        }

        yield return null;
    }

In the hierarchy the new scene LD04 is shown and it’s also displayed and running in scene and play modes.

image

Meanwhile, the method is logging that the active scene is LD03 (the previous scene)

And the coroutine just hangs at the waituntil scene name equals active scene name:
yield return new WaitUntil(()=>SceneManager.GetActiveScene().name == cutsceneData.sceneName);

You probably need to wait a frame or two after the scene is loaded for the previous scene to be completely unloaded. Scene integration takes multiple frames.

That said… don’t use FindObjectOfType. Have a system that lets this AreaExit component subscribe itself to the system that cares about it. There’s better ways of doing this.

Where would I wait x frames for the unloading? Because I am waiting until the active scene name changes, so it hangs until the end of time. Which I would presume waits any frames needed until the comparison is true. The FindObjectsOfType doesn’t even get called since I added the WaitUntil right before it:

yield return new WaitUntil(()=>SceneManager.GetActiveScene().name == cutsceneData.sceneName);
   var cutScenes = FindObjectsOfType<CutScene>();

I am literally in the new scene running around and activating other scripts in the new scene yet this coroutine still says it’s the previous scene. And is still waiting for the active scene name to change.

I do a lot of additive scene loading and I have to respectfully differ with Spiney: I usually DO use a FindObjectOfType<MySceneAdaptor>(); pattern to learn when the scene is ready.

Once I find that object I can know that the scene it came from is loaded.

I prefer this to hooking delegates and snuggling up to the SceneManager.

As to the unload, usually once I tell a scene to unload, if I kept anything like the MySceneAdaptor, I null it out at the same time.

If you really need to know, you could do the same thing: throw up a black loading image and keep checking for the previous MySceneAdaptor to go null, although I rarely care about such things.

Mostly my scene adaptors are empty and other “interesting” stuff in the scene is responsible for registering itself: exits, enemy spawns, player spawns, etc.

So should I set the cutscenes variable outside the loop and wait until the cutscenes is null instead of waiting for the active scene name to change? I still don’t understand why the active scene name NEVER updates to the current scene?

I even added this line to the asynchronous loading to ensure it’s updating the active scene:

public static IEnumerator LoadAsyncSceneFromArgument(string s)
    {
        AsyncOperation asyncLoad = SceneManager.LoadSceneAsync(s, LoadSceneMode.Single);

        // Wait until the asynchronous scene fully loads
        while (!asyncLoad.isDone)
        {
            yield return null;
        }
        SceneManager.SetActiveScene(SceneManager.GetSceneByName(s));
    }

I also have a boolean that is set by the sceneLoaded and sceneUnloaded events:

SceneManager.sceneUnloaded += OnSceneUnloaded;
        SceneManager.sceneLoaded += OnSceneLoaded;
public void OnSceneUnloaded(Scene arg0)
    {
        isSceneLoaded = false;
    }
private void OnSceneLoaded(Scene arg0, LoadSceneMode arg1)
    {
        isSceneLoaded = true;
        instance.StartCoroutine(LoadSaveData(arg0, arg1));
    }

And I wait for it right above the line where the scene name never changes:

#if UNITY_EDITOR
                Debug.Log($"MULTICUTSCENE Waiting for scene to load");
#endif
                yield return new WaitUntil(() => isSceneLoaded == true);
#if UNITY_EDITOR
                Debug.Log($"MULTICUTSCENE Scene loaded");
#endif

So nobody knows why the SceneManager would be returning an unloaded scene as the active scene and I can somehow grab the objects from the previous scene even though it has been confirmed to be unloaded already?

Every time I have seen that behavior it simply turned out that I had a bug.

I’m not saying you have a bug, but it sorta looks that way. :slight_smile:

The best way to build confidence that public APIs tend to work when used correctly is to strip down the problem and build it up with a chain of a couple of scenes that replicate the conditions you think are causing it to fail.

A few possible outcomes:

  • as you are doing it you’ll realize “oh man, I forgot about that step…”
  • it will work and then you can bisect between the tiny demo and your game and find what you’re missing
  • it won’t work and now you’ll have a tiny demo you can:
    • test in a blank project
    • if it still fails, come post it here with your findings
    • if it still can’t be fixed, file a bug with Unity

I would assume that as well. What I don’t understand though… Is why every other script when calling:

SceneManager.GetActiveScene.name

I get the scene that is loaded in play mode, the scene mode, and the hierarchy…

But in the one coroutine I need the updated scene for, it gives me the old scene that is supposed to be unloaded and the engine is telling me is unloaded. And somehow I can still retrieve anything from that old unloaded scene.

I have zero errors so I don’t know what exactly to look for.

Could it have something to do with a silent error in an awake or start method in a script?

So I just decided to reimplement it using the update loop. For whatever reason the same code works exactly as intended in the update loop. Just in the coroutine the coroutine seems to just stop running after 5 yields waiting for the scene to load. I don’t get it. No errors, nothing just stops running right before the scene actually loads.

But the update loop version works flawlessly. Thanks for taking the time to read my post and offering some ideas guys.

1 Like