Avoiding a twice-loading scene in SceneManager.LoadSceneAsync

Using Unity 2018.3.14f1

I am making a multi-scene game where a player enters a trigger and a scene loads asynchronously.

This scene has some substantial light-mapping data, so the scene takes about seven seconds to load asynchronously*.

One of my testers was able to accidentally make the level load twice by leaving the trigger while the scene was loading and then re-entering it.

As you’ll see below, I have a script that checks that when the player leaves the trigger, the scene will unload if it is already loaded. Another function does the opposite.

Problem is, if the scene is still in the middle of an asynchronous load and the player leaves the trigger, the scene won’t know to unload because the scene isn’t actually fully loaded - it’s in some sort of ambiguous state between loaded and unloaded. Then when the player enters the trigger again, the SceneManager still thinks its unloaded, so the scene starts its async load a second time.

*This seems to happen during the first time the game is played. After that, all the relevant data is in memory and async loading happens much faster. That’s part of why this has been hard to test / catch.

What’s the way forward here? I’m thinking an AsyncOperation technique in my coding work is the way forward, but I could use some perspective.

thanks!
-CMD

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

public class SceneLoadMaster : MonoBehaviour {


    //broadcast that the level is, in fact, loaded

   // scriptable object for all my scenes
    public SceneDatabaseSO sceneDatabase;

   // events attached to other scripts that let this script know to load a new secne.
    void OnEnable()
    {
        SceneLoadProxy.SceneLoadEvent += SceneLoad;

        SceneLoadProxy.SceneUnLoadEventBypass += SceneUnload;
        SceneLoadBlockerFadeControl.SceneUnLoadAfterBlocker += SceneUnload;
 
        // SceneManager.sceneUnloaded += SetSceneStateToUnloaded;
    }

    void OnDisable()
    {
        SceneLoadProxy.SceneLoadEvent -= SceneLoad;

        SceneLoadProxy.SceneUnLoadEventBypass -= SceneUnload;
        SceneLoadBlockerFadeControl.SceneUnLoadAfterBlocker -= SceneUnload;

        // SceneManager.sceneUnloaded -= SetSceneStateToUnloaded;
    }

   // the scene loads by receiving a level string from a Scriptable Object attached to a scene trigger.
  // if that string matches a scene name on the SceneManager, we load it.
    public void SceneLoad(SOLevelString l){
     
        if (l)
        {
            Scene scene = SceneManager.GetSceneByName(l?.value);
            if (!scene.isLoaded)
            {
                print("loading... "+l+" because it's not loaded yet.");
                SceneManager.LoadSceneAsync(l.value, LoadSceneMode.Additive);
            }
        }
        else
        {
            print ("there's no level to load.  what are you doing?!");
        }
    }

   // the opposite happens here.
    public void SceneUnload(SOLevelString l){

        Scene scene = SceneManager.GetSceneByName(l?.value);
     
        if (l)
        {
            if (scene.isLoaded)    //
                {
                    SceneManager.UnloadSceneAsync(l.value);
                    return;
                }
            else 
                {
                    print("The level is already unloaded!");
                }
        }
        else
        {
            print ("there's no level to unload.  what are you doing?!");
        }
    }

}

Will the “isDone” parameter help?

Maybe create a boolean “startedLoading”. Set it to true once your SceneLoad starts. Set it to false once your SceneLoad ends (isDone is true). Then, when you’ve figured out when to set that variable name, simply don’t start SceneLoad if startedLoading is true.

Kinda rough around the edges, but could do the trick.

1 Like