Scenes & Loading
Additive scenes and scene flow
A scene is a container for GameObjects. Most games use several: a menu, a level, maybe a persistent scene that survives everything else. Loading them well is the difference between a smooth transition and a three-second freeze.
Scenes must be in the build settings
A scene that is not listed in File → Build Settings cannot be loaded at runtime. It works in the editor while it is open, then fails in a build.
Loading, the wrong way and the right way
SceneManager.LoadScene blocks. Everything stops until the new scene is ready: no animation, no music, no input. For anything larger than a tiny scene, load asynchronously.
using UnityEngine;
using UnityEngine.SceneManagement;
using System.Collections;
public class SceneLoader : MonoBehaviour
{
public IEnumerator LoadLevel(string sceneName)
{
AsyncOperation op = SceneManager.LoadSceneAsync(sceneName);
while (!op.isDone)
{
// 0 to 0.9 is loading; it clamps there until activation
float progress = Mathf.Clamp01(op.progress / 0.9f);
progressBar.value = progress;
yield return null;
}
}
}// The whole game freezes until this finishes.
// Fine for a tiny scene, painful for a real level.
SceneManager.LoadScene("Level2");op.progress stops at 0.9, not 1. The last tenth is activation, which only happens once you allow it. Divide by 0.9 to get a sane 0–1 bar.Holding a scene until you are ready
Set allowSceneActivation = false to load everything but wait — useful for a "Press any key to continue" screen or to guarantee a minimum loading time so the bar does not flash past.
AsyncOperation op = SceneManager.LoadSceneAsync(sceneName);
op.allowSceneActivation = false;
// Fully loaded, just not swapped in yet
while (op.progress < 0.9f)
yield return null;
yield return WaitForPlayerInput();
op.allowSceneActivation = true; // now it switchesAdditive loading
By default a new scene replaces the current one. LoadSceneMode.Additive adds it alongside instead, which is how you build streaming worlds and persistent managers.
// Add a scene without unloading what is already there
SceneManager.LoadScene("Forest", LoadSceneMode.Additive);
// Remove it again
SceneManager.UnloadSceneAsync("Forest");
// New objects spawn into the active scene
SceneManager.SetActiveScene(SceneManager.GetSceneByName("Forest"));The persistent-scene pattern
A common structure is a small "Bootstrap" scene loaded first, containing managers, audio, and UI that must never be destroyed. Everything else loads additively on top and unloads freely.
This avoids DontDestroyOnLoad entirely, which sidesteps the duplicate-manager problem below and makes it obvious where global objects live — they are simply in the scene that is always loaded.
DontDestroyOnLoad and its trap
This marks an object to survive scene loads. It is useful for music players and managers, and it has one failure mode that catches everybody.
public class AudioManager : MonoBehaviour
{
public static AudioManager Instance { get; private set; }
private void Awake()
{
// Load the first scene again and a SECOND manager appears,
// because the surviving one is already there.
if (Instance != null && Instance != this)
{
Destroy(gameObject);
return;
}
Instance = this;
DontDestroyOnLoad(gameObject);
}
}DontDestroyOnLoad only works on root objects. Calling it on a child silently does nothing.Knowing when a scene is ready
private void OnEnable()
{
SceneManager.sceneLoaded += OnSceneLoaded;
}
private void OnDisable()
{
SceneManager.sceneLoaded -= OnSceneLoaded;
}
private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
{
if (scene.name == "Level2")
SpawnPlayerAtCheckpoint();
}What to take away
- Every loadable scene must be listed in Build Settings.
- Prefer
LoadSceneAsync;LoadScenefreezes the game. progresscaps at 0.9 until you allow activation.- Additive loading keeps managers alive without
DontDestroyOnLoad. - Guard persistent singletons against duplicating themselves.