Search Unity

  1. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Question How to load scenes seamlessly

Discussion in 'Scripting' started by angeldevelopment, Dec 1, 2023.

  1. angeldevelopment

    angeldevelopment

    Joined:
    Sep 28, 2022
    Posts:
    220
    So I have a game that will essentially have portals that take the user to other scenes, but I want no delay with loading these scenes. So I check if the player is within range of a portal, and then begin loading it in the background via LoadSceneAsync(xxx, LoadSceneMode.Additive)

    First off, sometimes using this call results in the game freezing, not sure what thats about. What I have found is that scene loading consist of 2 steps.

    1. Loading all objects into memory
    2. Calling Awake() and Start()

    The problem is the second step, this causes the game to freeze for a good 2 seconds. Im wondering if the job system can be used to mitigate this. Perhaps I could run my scripts on a worker thread? Can Start() and Awake() be done on a separate thread? There has to be a way to accomplish this.

    Code:
    Code (CSharp):
    1.     IEnumerator LoadSceneAsyncAdditive(string sceneName) {
    2.         // Load scene additive
    3.         DebugPrint("Will begin LOADING scene: " + sceneName + " and add to LOADING list");
    4.         AsyncOperation op = SceneManager.LoadSceneAsync(sceneName, LoadSceneMode.Additive);
    5.         currentOp = op;
    6.         op.allowSceneActivation = false;
    7.         // Add to list
    8.         currentScenesLoading.Add(sceneName);
    9.         //
    10.         bool loaded = false;
    11.         //
    12.         while (!op.isDone) {
    13.             float progress = Mathf.Clamp01(op.progress / .9f);
    14.             //
    15.             if (progress >= .9f && !loaded) {
    16.                 loaded = true;
    17.                 // Finished loading
    18.                 DebugPrint("Finished LOADING scene: " + sceneName + "... will update lists");
    19.                 currentScenesLoading.Remove(sceneName);
    20.                 currentScenesLoaded.Add(sceneName);
    21.             }
    22.             yield return null;
    23.         }
    24.     }
    25.  
    26.     void SwitchScene(Portal fromPortal) {
    27.         //
    28.         string sceneName = fromPortal.scene;
    29.         // ensure scene is ready
    30.         if (!currentScenesLoaded.Contains(sceneName)) { Debug.LogError("Attempted to switch to a non loaded scene:" + sceneName); return; }
    31.         else {
    32.             //
    33.             DebugPrint("Will switch to scene: " + sceneName);
    34.             currentOp.allowSceneActivation = true;
    35.             SceneManager.SetActiveScene(SceneManager.GetSceneByName(sceneName));
    36.             //
    37.             currentScenesLoaded.Remove(sceneName);
    38.             DebugPrint("Did remove: " + sceneName + " from LOADED List");
    39.             // position player
    40.             player.position = fromPortal.spawnPosition;
    41.             player.rotation = Quaternion.Euler(fromPortal.spawnRotaiton);
    42.             // unload current scene
    43.             UnloadSceneFromMemory(currentScene);
    44.             currentScene = sceneName;
    45.         }
    46.     }
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,263
    The good news is you're not alone.

    There are THOUSANDS of articles on this forum alone of people trying to load content seamlessly.

    You can get yourself so far with the approach above and that might be good enough.

    Alternately there are open world streaming assets you can get from the asset store to ease the process.

    Either way, the answer will vary depending on your game, what you're loading, how often it loads, etc.

    It might be that the only solution acceptable to you will be to load all the scenes and simply turn content on and off as you move around.

    As far as this:

    it's a bit more involved than that. There's getting the original disk file streamed into memory (the I/O portion of things), there is decompressing that into something that can be used, there is any kind of custom scripting you write, and there is alway moving the data somewhere useful, such as video RAM or vertex buffers.

    In other words, anything you do will be a specifically-engineered solution, not simply a "Here's how you can do dis" kind of answer.
     
    angeldevelopment likes this.
  3. Lenakeiz

    Lenakeiz

    Joined:
    Jun 11, 2015
    Posts:
    34
    Given your problem and your description I think that the solution will require some manual work to identfy what are the elements that makes the script executions slow on scene loading. And reasons might be more than one, including the loaded assets and the initialization you do with the object loaded.

    Have you used the Profiler to see what happens when the framerate drops? I guess you would like to see if you have specific assets or scripts that cause that.

    If the cause is scripted related than you will have to think of ways for ensuring that the Awake() and Start() methods in your scripts are as lightweight as possible. Defer any heavy initialization to happen over several frames or in response to specific events. You could eventually try to change the Script Execution Order: You can specify the script execution order in Unity to control when Awake() and Start() are called for each MonoBehaviour. This can help spread out the initialization load over several frames. Also keep in mind that while LoadSceneAsync is running, you can already access the newly loaded scene's root game objects before activation, this could allow you to enable/disable the object you don t need right away (do you need everything at scene load?).

    In general, I don t think it s possible to use the Job system for calling the Awake() and Start() function, but what you can do is offload some of the computation-heavy tasks using the jobs.
     
    angeldevelopment and Kurt-Dekker like this.
  4. kdgalla

    kdgalla

    Joined:
    Mar 15, 2013
    Posts:
    4,574
    Have you used the profiler to determine precisely what is consuming the most time?

    Do you have a lot of objects with Start and Awake? Maybe they don't all need to be active as soon as the seen loads. Maybe you can activate some of them later. Maybe you can simply divide your scenes up further so that there are less objects loading at a time.

    There's a little bit of overhead when Unity calls one of these built-in methods. You might be able to save a little bit of time by moving a lot of Start methods in to a manager object. So then instead of 50 objects with their own Start, you have one Start method that initializes all of those objects.
     
    angeldevelopment likes this.
  5. angeldevelopment

    angeldevelopment

    Joined:
    Sep 28, 2022
    Posts:
    220
    So Im starting in my "main" scene and testing by "portaling" into 2 other scenes. One is a small forest scene made with Gaia (1 terrain @ 128 m) and an empty scene.

    Sometimes when pre-loading the scene (when the player is near the portal) there is a 2 second freeze. But I believe that this is an editor issue, upon profiling I found that this is caused by EditorLoop taking 98%. I have seen people say that the editor does not alway run LoadSceneAsync as it would in a build, so I believe that is an editor only issue. Weird that it only happens sometimes though.

    But for the main issue:

    When portaling into the empy scene it seems pretty instantanious. The Gaia scene however, has a 5 second delay.
    Upod profiling I found the cause to be GC.Collect. Im assuming the large number of terrain objects is causing this.

    Profile results:

    Screenshot 2023-12-01 at 4.14.19 PM.png Screenshot 2023-12-01 at 4.14.19 PM.png Screenshot 2023-12-01 at 4.22.22 PM.png
     
  6. angeldevelopment

    angeldevelopment

    Joined:
    Sep 28, 2022
    Posts:
    220
    I also feel like I have to use separate scenes because I want:

    1. Separate baked lighting data for each portal end point
    2. Separate skybox
     
  7. angeldevelopment

    angeldevelopment

    Joined:
    Sep 28, 2022
    Posts:
    220
    It seems to be from GC.Collect, Loading.AwakeFromLoad, TerrainLoaderManager.OnEnable, ReadObjectFromSerializedFile
     
  8. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    7,226
    Scene loading is always slower in the editor. Test and profile it in a build.
     
    angeldevelopment and Peter77 like this.
  9. angeldevelopment

    angeldevelopment

    Joined:
    Sep 28, 2022
    Posts:
    220
    Now Im having an issue where once I load, and unload a scene (but now set it active), and then load it again, it is not ready indefinitely. Also, where should Resources.UnloadUnusedAssets fit into this picture?
     
  10. angeldevelopment

    angeldevelopment

    Joined:
    Sep 28, 2022
    Posts:
    220
    So it is working very well in a build, all of the weird freezes and so forth dont occur, and the scene loading is very fast. But I am still having the issue where if I load and unload a scene, once I go to load it again, I cannot switch to it. Also, when I unload a scene, in the scene hierarchy, it never disappears. It just says that it is unloading.


    In the image, I load the scene, and then unload it, but it is still in the hierarchy. Maybe it just isnt refreshing, but, in a build, I can never switch to scene if i unload it, and then load it again. (And in the editor)
    Screenshot 2023-12-07 at 11.26.02 PM.png