Quick answer: The scene file contains the manager, so reloading the scene instantiates a fresh one while the old one survives via DontDestroyOnLoad. Use a static Instance guard in Awake: if an instance already exists, Destroy the new one immediately.
Here is how to fix Unity scene reload leaves DontDestroyOnLoad duplicates. You have a GameManager in your bootstrap scene, marked DontDestroyOnLoad. You return to the main menu and load the bootstrap scene again for new-game setup. Now there are two GameManagers. Your save system runs twice, player preferences double-apply, audio plays from both. Every scene reload adds another copy. This is the most classic Unity singleton bug — unavoidable if you do not guard it, and easy to guard once you know.
The Symptom
After reloading a scene that contains a GameManager (or any DontDestroyOnLoad singleton), multiple copies exist:
- Events fire multiple times (save triggers twice, UI updates twice)
- FindObjectsOfType returns more than one
- The Hierarchy in DontDestroyOnLoad section shows duplicate entries
- Static references may or may not point at the right instance
Every subsequent reload adds another duplicate, eventually leading to dozens of managers running in parallel.
What Causes This
Scene files contain the manager. When you saved a GameManager object in the bootstrap scene, it becomes part of the scene file. Every time the scene loads, Unity instantiates that object fresh. Your Awake calls DontDestroyOnLoad, which moves the new object to the persistent scene. But the old one from the previous load is still there — also DontDestroyOnLoad.
DontDestroyOnLoad is one-way. Once called, the object persists across all future scene loads. There is no “untag” API. Even if you unload the original scene, the object lives on.
No singleton guard. Without code in Awake that checks “am I the first instance?” and destroys duplicates, every scene reload compounds the problem.
The Fix
Step 1: Implement a singleton guard. In every DontDestroyOnLoad MonoBehaviour, add a static Instance field and check for duplicates in Awake.
using UnityEngine;
public class GameManager : MonoBehaviour
{
public static GameManager Instance { get; private set; }
void Awake()
{
if (Instance != null && Instance != this)
{
Destroy(gameObject);
return;
}
Instance = this;
DontDestroyOnLoad(gameObject);
}
}
The first instance to Awake sets Instance and lives. Every subsequent instance sees Instance already set, destroys itself, and returns before any side effects run. Scene reloads spawn a new manager that immediately destroys itself — correct behavior.
Step 2: Alternative: spawn programmatically. If you want zero chance of duplicates, do not place the manager in any scene. Spawn it via a runtime initializer:
public class GameManager : MonoBehaviour
{
public static GameManager Instance { get; private set; }
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
static void Bootstrap()
{
GameObject go = new GameObject("GameManager");
Instance = go.AddComponent<GameManager>();
DontDestroyOnLoad(go);
}
}
This runs once per game session, before any scene loads. The manager is never in a scene file, so scene reloads cannot create duplicates. Downside: no Inspector configuration — you have to configure via code, ScriptableObject, or JSON.
Step 3: Handle cross-scene references carefully. If your GameManager holds references to scene-specific objects (UI canvases, cameras), those become invalid after a scene load. Clear them in OnDisable or on SceneManager.sceneUnloaded, and re-acquire them on sceneLoaded.
void OnEnable()
{
SceneManager.sceneLoaded += OnSceneLoaded;
}
void OnDisable()
{
SceneManager.sceneLoaded -= OnSceneLoaded;
}
void OnSceneLoaded(Scene scene, LoadSceneMode mode)
{
// Re-find UI references, cameras, etc.
uiCanvas = Object.FindObjectOfType<UICanvas>();
}
Step 4: Clean up events on destroy. Static events held by the manager leak across sessions in edit mode (Domain Reload OFF) and can accumulate listeners if you do not clean up. In OnDestroy, unsubscribe listeners and clear Instance:
void OnDestroy()
{
if (Instance == this)
Instance = null;
}
Generic Singleton Base Class
For projects with many singletons, a generic base saves repetition:
public abstract class Singleton<T> : MonoBehaviour where T : Singleton<T>
{
public static T Instance { get; private set; }
protected virtual void Awake()
{
if (Instance != null && Instance != this)
{
Destroy(gameObject);
return;
}
Instance = (T)this;
DontDestroyOnLoad(gameObject);
}
protected virtual void OnDestroy()
{
if (Instance == this)
Instance = null;
}
}
public class GameManager : Singleton<GameManager>
{
// manager logic here
}
Any class extending Singleton<T> automatically gets the guard. Override Awake but call base.Awake first.
Enter Play Mode Options
Unity 2019+ has “Enter Play Mode Options” under Project Settings > Editor. Disabling Domain Reload and Scene Reload speeds up editor iteration but amplifies singleton duplicate bugs because Unity does not reset static state between play sessions. The guard pattern above handles this correctly as long as you clear Instance in OnDestroy.
“DontDestroyOnLoad is a promise of persistence. The guard is the promise that only one copy persists.”
Related Issues
For scene loading issues, see Unity Scene Loading Freezes Game. For ScriptableObject singletons, ScriptableObject Singleton Null After Build covers related patterns.
Guard in Awake, null Instance in OnDestroy. Two lines protect your singletons forever.