Additive Load Scene Async is too demanding; how do I fix?

I’m working on getting my game to stream in all my content seamlessly.
I’m testing some triggers to call SceneManager.LoadSceneAsync to add my next scene into the game, but the process drops way too many frames; the game visibly hangs while the scene finishes loading.

First off, I’d like to know if anyone knows how I can fix this problem, or can suggest something I can look into.

I did find Unity - Scripting API: Application.backgroundLoadingPriority which I have set to low, but I still get noticeable stuttering when the scene loads.
I suspect that that I’m having “awake” and “start” called on too many objects at once, but I don’t know how to test this theory.

I’m thinking that what I need to do is divide my scene into smaller scenes so that things can load in smaller chunks. But if I do that, I don’t know how to prioritize what needs to be divided and how.
How can I find what is causing this stuttering?


And one final thought that just came to my mind: is it possible to pre-load scenes and assets? That is, load everything into memory but not actually have anything exist in the scene just yet, and then I call everything in when I need it?

Arrgh, I’ve divided my level into six different parts, but it still stutters when it loads.

What’s your actual code for loading levels? How that structured could effect the smoothness of your loading.

Personally I use addressables + regular async code to load scenes which works out very smooth.

Finally question, is this in build, editor, or both?

In editor. I’ve set up debug noticed for when an update takes longer than 0.05, which would be a less than 20 FPS. I’ve been using this and shifting around groups of assets to see how it impacts loading. There doesn’t seem to be any single part of my level that is causing problems, it’s just when it loads too much.

I really don’t want to have to divide my levels into this many pieces just to facilitate loading.
How are you using addressables with Async loading? Offhand that just sounds like extra work for tracking everything without actually slowing down the loading process.

This is my complete loading script. It gets attached to a trigger and activates when the player touches it.

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

public class LoadingZone : MonoBehaviour
{

    public string[] ScenesToLoad;
    public string[] ScenesToUnload;

    protected bool bLoadingInProgress;
    protected float loadingTime;
    protected float TotalLoadingTime;

    private void Start()
    {
        // @@@ This is really here for building.  When ready to ship remove this for optimization.
        foreach (string sce in ScenesToLoad)
        {
            if (SceneUtility.GetBuildIndexByScenePath(sce) < 0)
            {
                Debug.LogError("Scene not found!!  Check spelling for " + sce + " and ensure it is added to the build list!");
            }
        }
        foreach (string sce in ScenesToUnload)
        {
            if (SceneUtility.GetBuildIndexByScenePath(sce) < 0)    // alt: !SceneManager.GetSceneByName(sce).IsValid()
            {
                Debug.LogError("Scene not found!!  Check spelling for " + sce + " and ensure it is added to the build list! (Unload list)");
            }
        }
    }

    private void OnTriggerEnter(Collider other)
    {
        if (other.tag == "Player")
        {
            if (bLoadingInProgress) return;
            bLoadingInProgress = true;
            TotalLoadingTime = Time.time;
            StartCoroutine(LoadUnloadScenes());
        }
    }

    IEnumerator LoadUnloadScenes()
    {
        foreach (string SceneClose in ScenesToUnload)
        {
            Debug.Log("Starting unload sequence for " + SceneClose);

            AsyncOperation asyncUnload = SceneManager.UnloadSceneAsync(SceneClose);
            loadingTime = Time.time;

            while (!asyncUnload.isDone)
            {
                yield return null;
            }
            Debug.Log("Unloading " + SceneClose + " complete.  Total time: " + (Time.time - loadingTime));
        }

        foreach (string SceneName in ScenesToLoad)
        {
            if (SceneManager.GetSceneByName(SceneName).isLoaded)
            {
                Debug.Log(SceneName + " is already loaded!");
            }
            else
            {
                Debug.Log("Starting load sequence for " + SceneName);

                AsyncOperation asyncLoad = SceneManager.LoadSceneAsync(SceneName, LoadSceneMode.Additive);
                loadingTime = Time.time;

                // Wait until the asynchronous scene fully loads
                while (!asyncLoad.isDone)
                {
                    yield return null;
                }
                Debug.Log("Loading " + SceneName + " complete.  Total time: " + (Time.time - loadingTime));
                yield return null;
            }
        }

        Debug.Log("Finished all loading.  Total time: " + (Time.time - TotalLoadingTime));
        bLoadingInProgress = false;
    }

}

I haven’t actually tested anything on the unloading side yet, but I figured I would need it too.

This is the next big hurdle in my game; I need to figure out what kind of restrictions I face with loading new areas before I can really lay out my levels, because I need to know how I can transition between areas.

So have you built the game to see if the same issues persist? From what I remember, scene loading can be a little laggy in editor but smooth in a built game.

Often it’s not loading the scenes but the activation of them that causes the stutters. Activation includes all of the scripts running their Awake/OnEnable/Start methods and that needs to happen on the main thread and cannot be done asynchronously. Have you checked with the profiler where the time is spent when your loading stutters?

Strategies to reduce activation time include loading your scenes/prefabs with all game objects deactivated and then split activating the game objects over multiple frames or moving code that is running in Awake/OnEnable/Start into coroutines that split the work over multiple frames.

1 Like

I guess I’ll check that out next.

I do not know how to use the profiler that well.
That said, I don’t think I have many scripts in this scene. I’ve set up the enemies to spawn in later, so there’s only maybe a dozen or so objects in the scene with scripts attached to them.
Mostly it’s loading up the art assets; I’ve got some meshes and materials that are honestly bigger than they should be.

That… Is actually a really smart idea that I may need to borrow for a few areas.

It is much smoother in a built game. Although this discrepancy now has me wondering about how I can best test this on different systems. In the editor I have logs that reveal how long it took to load; I wonder what the slowest system I could reasonably expect is, and what kind of performance it might offer.

Also my built version has some constant screen tearing; it looks like a cheap VHS filter.

1 Like

At the same point in my builds (mobile and VR) can anyone advise on best practices? I have about 20 scenes that could benefit from scene restructuring/reduction of assets being loaded in run-time.

1 Like