Loading Level Async without switching the level immediately

Hi,

I was wondering if it was possible to load a level async a head of time, and then at a later point of my choosing switch to that level? Additive loading is not really an option here since our levels are all stand alone islands.

Because when using
asyncOperation = Application.LoadLevelAsync(LevelName);

Then it immediately switches level after it is done loading it. Imagine that the player is running down a corridor in game, and we start loading the next level with this function and there is something the player forgot in the level and turns around to go back. But now the loading has ended and it immediately switches level. We would rather have the loading happen in the background and when you reach the end of the corridor we tell the engine to switch level based upon if it is finished loading or not.

Is this possible?

This is possible now without additive scenes by using async.allowSceneActivation, however it is currently (4.0.1) somewhat dangerous.

Note: Unity will CRASH if you don’t let it complete any async load operation that you start. so…

  • Don’t exit play mode while an async load is waiting to be activated.
  • Don’t let players cancel scene transitions once the async load starts

Less important notes:

  • Your next scene’s initialization is probably the real problem, try to optimize init before worrying about async load.

  • The lag that appears when you first call LoadLevelAsync is only present in the editor.

     public string levelName;
     AsyncOperation async;
    
     public void StartLoading() {
     	StartCoroutine("load");
     }
     
     IEnumerator load() {
     	Debug.LogWarning("ASYNC LOAD STARTED - " +
            "DO NOT EXIT PLAY MODE UNTIL SCENE LOADS... UNITY WILL CRASH");
     	async = Application.LoadLevelAsync(levelName);
     	async.allowSceneActivation = false;
     	yield return async;
     }
    
     public void ActivateScene() {
     	async.allowSceneActivation = true;
     }
    

Just call StartLoading() when you are certain that a scene transition is incoming and then ActivateScene() when you are ready to swap.

Hopefully we can start figuring out safer ways to do this as more people start using allowSceneActivation. I’m willing to live with the danger of unity crashes to avoid the hassle of additive scene loading.

No, I dont think its currently possible, LoadLevelAsync is mostly for showing a loading screen or something like that…but if you want a smooth load, than you could make a gui lerp, that lerps the screen to black, than loads the level, and in the next scene lerps it back… the only way to to what you want is to use Application.LoadLevelAdditiveAsync, and than destroy the objects from previous scene…but its really complicated.

If you wanna use the lerp/fade then check Fade. I really like the extension and I use it for fading into videos, or through levels :).

My script for fading into movies. //use this link if the first one isn’t working

– David

You would definitely have to use something like Application.LoadLevelAdditiveAsync.

It’s not too terribly difficult if you structure your sub scenes well. My method is to pack an entire sub scenes contents under one GameObject. Then when I load a level using AdditiveAsync, I place that content under a root content GameObject in my base scene. Then when I want to destroy my old scene data I just destroy the content object and recreate it using new scene objects.

It would look something like this:
BaseScene

–GameManagerGameObject

–ContentRootGameObject

MyNewScene

–MyNewSceneRootGameObject

----MyNewSceneSubGameObjects

A loaded scene would look like this if you loaded MyNewScene additively:
BaseScene

–GameManagerGameObject

–ContentRootGameObject

----MyNewSceneRootGameObject

------MyNewSceneSubGameObjects

Then you just destroy ContentRootGameObject from your GameManagerObject, release unused assets, then repeat for each new level.

Here is the loading code I’m using currently:

private IEnumerator LoadLevel()
{
    if (string.IsNullOrEmpty(_levelToLoad)) yield return 0; // Nothing to load!

    Debug.Log("ManagerGame: Level[" + _levelToLoad + "] loading level");

    var go = GameObject.Find("ContentRoot");

    if (go != null)
    {
        DestroyImmediate(go);
        Resources.UnloadUnusedAssets();
    }
 
    AsyncOperation async = Application.LoadLevelAdditiveAsync(_levelToLoad);
    yield return async;

    go = new GameObject("ContentRoot");

    GameObject.Find(_levelToLoad).transform.parent = go.transform;

    Debug.Log("ManagerGame: Level[" + _levelToLoad + "] loading complete");
}

Hello, this is quite old, but useful for me now. I use it with SceneManager.LoadSceneAsync() because the other is obsolete. Also I think it’s not necessary to run it in a Coroutine, because it already is one. Also it’s not buggy anymore.

Here is my code… i can wait 1 second before and after

  private void Start()
           {
              StartCoroutine(LoadSceneAsync("GameScene"));
            } 
            
            IEnumerator LoadSceneAsync(string sceneName)
                {
                    yield return new WaitForSeconds(1);
                    var asyncLoad = SceneManager.LoadSceneAsync(sceneName);
                    asyncLoad.allowSceneActivation = false;
                    var hasWaitLoad = true;
            
                    while (!asyncLoad.isDone)
                    {
                        if (asyncLoad.progress == 0.9f)
                        {
                            if (hasWaitLoad)
                            {
                                hasWaitLoad = false;
                                yield return WaitLoad(asyncLoad);
                            }
                        }
            
                        yield return null;
                    }
                }
            
                IEnumerator WaitLoad(AsyncOperation asyncLoad)
                {
                    yield return new WaitForSeconds(1);
                    asyncLoad.allowSceneActivation = true;
                }