Skip to content
SEYIL Studios
Blog

Unity Scene Loading: Additive vs Single Scene Setup

When additive scene loading simplifies a Unity project and when it turns into reference chaos — with working code, the failure cases we actually hit, and a decision rule for small teams.

IIlhan Seyhan7 min read0 views
Unity Scene Loading: Additive vs Single Scene Setup

Unity gives you two obvious ways to structure a project: put everything a player sees into one scene, or split the game into a persistent core scene plus additively loaded pieces. Both work. Both fail in specific, predictable ways, and this post is about which failure you would rather debug.

Below: what single-scene actually costs, where additive loading earns its keep, the four reference bugs that show up every time, and a decision rule you can apply to a small project without a week of refactoring.

The single-scene default is not laziness

A single scene per screen — menu scene, gameplay scene, results scene — loaded with LoadSceneMode.Single is the default for a reason. Everything in the scene is authored together, so you can drag references in the Inspector and they serialize correctly. Entering play mode from that scene gives you the real state of the game. There is no load order to reason about.

The cost shows up in two places. First, duplication: if the HUD, audio mixer setup and settings manager live inside every gameplay scene, a change to any of them is a change to every scene file, and scene files merge badly in version control. Second, transitions: Single mode tears down the whole scene before the next one exists, so anything that must survive the transition needs DontDestroyOnLoad, and now you have hidden global state that no scene shows in the Hierarchy.

For a game with a handful of screens and no persistent world — a card game like Kart Üçlüsü is close to this shape — that cost is small and the simplicity is worth keeping.

What additive loading actually buys you

Additive mode adds a scene to the ones already loaded instead of replacing them (Unity docs). That gives you three concrete things:

  • A persistent core. One scene holds the systems that never die — audio, save, input, the loading screen canvas — and it is a real scene you can open and inspect, not an invisible DontDestroyOnLoad bucket.
  • Smaller, mergeable scene files. Level layout in one scene, lighting in another, UI in a third. Two people can work in parallel without touching the same .unity file.
  • Streaming. You can load the next level behind a fade while the current one is still on screen, instead of showing a hard cut.

A level-based game benefits more than a screen-based one. A tower defense such as Thornguard has the same HUD, the same wave system and the same audio setup across every map — only the map geometry and wave data change. That is exactly the split additive loading is good at.

A loader that does the four steps in the right order

The whole additive flow is four operations, and the order matters more than the code:

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

public class SceneFlow : MonoBehaviour
{
    string current;

    public IEnumerator Swap(string next)
    {
        if (!string.IsNullOrEmpty(current))
        {
            var unload = SceneManager.UnloadSceneAsync(current);
            while (!unload.isDone) yield return null;
        }

        var load = SceneManager.LoadSceneAsync(next, LoadSceneMode.Additive);
        while (!load.isDone) yield return null;

        SceneManager.SetActiveScene(SceneManager.GetSceneByName(next));
        current = next;

        yield return Resources.UnloadUnusedAssets();
    }
}

Two details that cause bugs if you skip them. SetActiveScene only works once the scene has finished loading, so it belongs after the wait, not next to the load call. And UnloadSceneAsync destroys the GameObjects but does not free asset memory — the docs tell you to call Resources.UnloadUnusedAssets for that (Unity docs). That call is slow, so run it behind the loading screen, never mid-gameplay.

One more trap that has nothing to do with code: a scene that is not in the build list will not load at runtime, even though it opens fine in the Editor. This is the classic "works in Editor, black screen on device" report.

Where it turns into reference chaos

Additive loading breaks the one thing that made single-scene comfortable: dragging references in the Inspector. Here are the failures in the order you will meet them.

1. Cross-scene references do not serialize

Drag an object from the Core scene into a field on an object in the Level scene and Unity will not keep it — saved scenes cannot hold references into other scenes. The fix is to stop referencing objects and start referencing assets. A ScriptableObject event channel covers most cases:

using UnityEngine;
using UnityEngine.Events;

[CreateAssetMenu(menuName = "Events/Int Event Channel")]
public class IntEventChannel : ScriptableObject
{
    public event UnityAction<int> Raised;

    public void Raise(int value) => Raised?.Invoke(value);
}

The level scene raises GoldChanged; the HUD in the core scene listens. Neither knows the other exists, and both fields point at an asset that serializes fine. Unsubscribe in OnDisable — ScriptableObjects outlive scenes, so a forgotten handler keeps a dead object alive.

2. Spawned objects land in the wrong scene

Instantiate puts the new object in the active scene, not in the scene whose script called it. Unload the level and your spawned enemies stay behind in Core, invisible in the Hierarchy unless you look. Be explicit:

var level = SceneManager.GetSceneByName("Level_03");
var enemy = Instantiate(enemyPrefab, spawnPoint.position, Quaternion.identity);
SceneManager.MoveGameObjectToScene(enemy, level);

The active scene also decides which lighting and skybox settings apply, which is why a level can look correct alone and washed out once loaded next to Core.

3. Awake runs before the world is ready

In a single scene, every Awake happens before any Start. With additive loading that guarantee stops at the scene boundary: a level script's Awake can run while a system in another scene is still initialising. Do not resolve dependencies in Awake. Have the loader call an explicit Initialize() on the level after the load completes, so there is one place where "everything exists now" is true.

4. Two copies of the singleton

Press play in the Level scene, the auto-bootstrap loads Core, then you also load Core from the menu flow — two audio managers, doubled sound. Guard the load, and keep the Editor convenience Editor-only:

#if UNITY_EDITOR
using UnityEngine;
using UnityEngine.SceneManagement;

public static class EditorBootstrap
{
    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
    static void EnsureCore()
    {
        if (SceneManager.GetSceneByName("Core").isLoaded) return;
        SceneManager.LoadScene("Core", LoadSceneMode.Additive);
    }
}
#endif

This is the piece people skip, and it is the piece that decides whether the architecture survives. If a designer cannot open a level scene and hit play, they will start putting managers back into the level scene, and you are back to duplication with extra steps.

A decision rule for a small project

Split by lifetime, not by category. Ask one question about each part of your game: does this need to survive a level change? Everything that says yes goes in Core. Everything else goes in the level scene. That usually produces exactly two additive scenes, not seven.

Concretely, we would go additive when at least two of these are true:

  • There are more than roughly five levels sharing identical systems.
  • More than one person edits scenes in the same week.
  • You need a transition without a hard cut.
  • The same core is reused across projects — the situation we described in Two Games, One Codebase.

Otherwise, keep single scenes. "We might need it later" is not one of the conditions; converting a single scene to Core plus Level later is a mechanical job, while untangling a seven-scene setup built for a three-screen game is not.

Then measure the transition

Additive loading moves cost around rather than removing it. Async loading still spikes when the scene activates and when the GC runs after unload, and on mid-range Android hardware that spike is where the visible hitch lives. Capture the transition in the Profiler before deciding it feels smoother — the method we use for that is in Unity Profiler on Mobile. If the load hitch is the same and the reference wiring got harder, the split did not pay for itself.

Share:

Comments

Be the first to comment.