The short answer: load Unity scenes without freezing by replacing synchronous SceneManager.LoadScene calls with LoadSceneAsync, keeping a lightweight loading interface alive, delaying scene activation until the transition is ready, and reducing the work performed by Awake, OnEnable, and Start in the destination scene.
Asynchronous loading prevents one large blocking scene-load call, but it is not a promise that every frame will be perfectly smooth. Unity still has to activate the scene and initialize its objects. A reliable transition therefore treats loading and activation as separate problems, then profiles both on the actual phone or VR headset.
Why does a scene change freeze?
A synchronous scene load asks Unity to complete the change before normal gameplay continues. The current frame can remain on screen while files, objects, and dependencies are prepared. Unity’s current LoadSceneAsync documentation instead returns an AsyncOperation and loads the scene in the background while the current scene continues to run.
That solves only the first half. When the new scene activates, enabled objects run their initialization callbacks. Hundreds of scripts performing searches, instantiating content, reading files, or building data at once can still create a visible hitch. The loader must cover that transition, and the destination scene must avoid an overloaded first frame.
Single or Additive loading?
| Mode | What Unity does | Good fit |
|---|---|---|
LoadSceneMode.Single |
Unloads the currently loaded scenes and replaces them with the destination | Menu-to-game transitions and self-contained levels |
LoadSceneMode.Additive |
Adds the destination alongside scenes already loaded | Persistent systems, streamed sections, lighting layers, or a dedicated loading scene |
Start with Single unless the project genuinely needs multiple scenes at once. Additive loading is powerful, but the game must also decide which scene is active, track ownership, and unload old sections deliberately.
A practical asynchronous scene loader
This compact coroutine keeps its loading canvas alive across a Single-mode transition. Put it in a small bootstrap or menu scene, connect a CanvasGroup and UI Slider, and make sure later scenes do not create duplicate loaders.
using System.Collections;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public sealed class SceneLoader : MonoBehaviour
{
[SerializeField] private CanvasGroup loadingOverlay;
[SerializeField] private Slider progressBar;
private bool isLoading;
private void Awake()
{
DontDestroyOnLoad(gameObject);
SetOverlay(false);
}
public void LoadScene(string sceneName)
{
if (!isLoading)
StartCoroutine(LoadRoutine(sceneName));
}
private IEnumerator LoadRoutine(string sceneName)
{
isLoading = true;
SetOverlay(true);
// Give the loading UI one frame to render before work begins.
yield return null;
AsyncOperation operation = SceneManager.LoadSceneAsync(
sceneName, LoadSceneMode.Single);
operation.allowSceneActivation = false;
while (operation.progress < 0.9f)
{
progressBar.value = Mathf.Clamp01(operation.progress / 0.9f);
yield return null;
}
progressBar.value = 1f;
yield return null;
operation.allowSceneActivation = true;
while (!operation.isDone)
yield return null;
SetOverlay(false);
isLoading = false;
}
private void SetOverlay(bool visible)
{
loadingOverlay.alpha = visible ? 1f : 0f;
loadingOverlay.blocksRaycasts = visible;
loadingOverlay.interactable = visible;
}
}
Production code should also validate scene names and use a singleton or bootstrap-owned service so repeated menu visits cannot create another persistent copy. Disable transition buttons while isLoading is true; two competing scene requests are difficult to recover from cleanly.
Why does progress stop at 90 percent?
This is expected behavior, not a failed load. Unity’s allowSceneActivation reference states that progress stops at 0.9 and isDone remains false while activation is blocked. Dividing the reported progress by 0.9 gives the loading bar a useful zero-to-one range before activation.
Do not hold the operation at 90 percent indefinitely while starting other asynchronous scene operations. Unity documents that the AsyncOperation queue is stalled while activation is blocked, so a later unload can wait behind it. Use the pause only long enough to finish a fade, show a ready prompt, or align the visual transition.
Reduce the activation spike
If the screen still pauses as progress reaches the end, profile the first frames of the destination scene. The cause is often initialization rather than disk loading. Work through these fixes in order:
- Remove expensive object searches and repeated setup from every component.
- Preassign references in the Inspector or through a small composition service.
- Spread nonessential setup across several frames with a coroutine.
- Pool repeated gameplay objects instead of instantiating a large wave immediately.
- Split genuinely large environments into additive sections only after profiling proves it is useful.
- Test a development build on the slowest supported device.
A transition hitch caused by managed allocation is a different problem. Blekol’s guide to preventing garbage-collection spikes in Unity shows how to identify recurring GC.Alloc work instead of blaming the scene loader.
Keep loading screens lightweight on mobile and VR
A loading screen should be cheaper than the scenes around it. Use a simple background, short message, small animation, and minimal scripts. On VR hardware, keep the view stable and avoid leaving a frozen gameplay frame under head movement. A dedicated low-cost environment or full-screen fade is usually more comfortable than showing a half-initialized world.
For a mobile puzzle game such as Cubus for Android and iOS, the same service can control transitions from menu to puzzle boards without duplicating loading logic. A larger VR experience such as Periodic Table VR can use controlled transitions before presenting a dense interactive scene.
How does this relate to Addressables?
SceneManager.LoadSceneAsync is a good default for scenes built into the player. Addressables becomes relevant when a scene or its content needs an addressable lifecycle, platform variants, or remote delivery. The earlier comparison of Unity Addressables versus Resources explains that ownership decision. Whichever path you choose, keep one system responsible for starting, tracking, and releasing the operation.
Scene-loading checklist
- Use
LoadSceneAsyncfor visible transitions. - Render the loading UI before beginning the load.
- Normalize pre-activation progress from 0–0.9.
- Release
allowSceneActivationpromptly. - Prevent duplicate requests and duplicate persistent loaders.
- Keep destination-scene initialization small.
- Profile activation on real Android, iOS, and VR hardware.
The central rule is simple: asynchronous loading hides file preparation, while disciplined scene initialization prevents the final hitch. Treat both stages as part of one transition, and the same architecture can support responsive mobile games and comfortable VR experiences. Explore more practical development notes and games at Blekol Games.
Featured image: photo by Mohammad Rahmani on Unsplash.


Leave a Reply
You must be logged in to post a comment.