Persisting Objects Across Scenes: A Complete Guide to Unity's DontDestroyOnLoad

Created: 2025-12-10Last updated: 2026-07-13

Learn how to use DontDestroyOnLoad to keep objects alive across scene transitions, and how to manage them safely with the Singleton pattern.

"My BGM cut off when I switched scenes." "My score got reset." — When you load a scene, every object in the old scene gets destroyed. That's Unity's default behavior. But sooner or later you'll have objects like a BGM player or a game manager that you want to survive the transition.

That's exactly what DontDestroyOnLoad() is for. In this article, we'll cover the basic usage, the "duplicate object" trap that catches every beginner, and the classic fix: combining it with the Singleton pattern.

Illustration of DontDestroyOnLoad: only the BGM player survives when the scene changes

What You'll Learn

  • Why objects disappear on scene changes, and what DontDestroyOnLoad does about it
  • The basic usage (just call it in Awake)
  • Why the classic "duplicate object" pitfall happens
  • A safe persistence implementation using the Singleton pattern

Sponsored

Basic Concept of DontDestroyOnLoad

DontDestroyOnLoad is a static method on the Object class. When you pass a GameObject to this method, that object moves to a special internal scene called the "DontDestroyOnLoad scene" and is no longer affected by subsequent scene transitions.

Unlike regular scenes, the DontDestroyOnLoad scene is never destroyed until the game exits. This makes it easy to manage objects that need to exist for the entire lifetime of the game, such as BGM players and game managers.

Diagram of how DontDestroyOnLoad works: when switching from Scene A to Scene B, regular objects are destroyed, but objects moved to the DontDestroyOnLoad scene survive

Code Example 1: Basic Usage

The simplest usage is to call DontDestroyOnLoad(this.gameObject) inside the Awake() method of a script attached to the object you want to persist.

The following example is a script meant to be attached to an object that plays background music.

using UnityEngine;

public class BgmPlayer : MonoBehaviour
{
    void Awake()
    {
        // Keep this GameObject alive across scene transitions
        DontDestroyOnLoad(this.gameObject);

        // Add BGM playback logic here
        Debug.Log("BGM player created and DontDestroyOnLoad applied.");
    }
}

As long as a GameObject with this script is placed in the scene, it will survive any number of scene transitions without being destroyed.

Common Pitfall for Beginners: Duplicate Objects

The most common mistake beginners make with DontDestroyOnLoad is duplicate object creation.

For example, suppose the BgmPlayer above is placed in Scene A, and the same BgmPlayer is also placed in Scene B. When you move from Scene A to Scene B, here's what happens:

  1. DontDestroyOnLoad is applied to Scene A's BgmPlayer, making it persistent.
  2. When Scene B loads, the new BgmPlayer placed in Scene B is created.
  3. As a result, two BgmPlayer objects now exist in the game, and you end up with problems like the BGM playing twice at once.
Diagram of the duplicate object pitfall: the BGM player persisted from Scene A survives while a second one placed in Scene B is created, causing the BGM to play twice

Code Example 2: Safe Management with the Singleton Pattern

To solve this duplication problem and guarantee that exactly one instance of your persistent object exists in the game, DontDestroyOnLoad is typically combined with the Singleton pattern.

The Singleton pattern is a design pattern (a well-established design recipe) that "guarantees only one instance of a class ever exists."

The following GameManager script is a practical example combining DontDestroyOnLoad with the Singleton pattern.

using UnityEngine;

public class GameManager : MonoBehaviour
{
    // Static instance for external access
    public static GameManager Instance { get; private set; }

    void Awake()
    {
        // Check if an instance already exists
        if (Instance != null && Instance != this)
        {
            // If one already exists, destroy this newly created one to prevent duplicates
            Destroy(this.gameObject);
            return;
        }

        // If no instance exists yet, register this one as the Instance
        Instance = this;

        // Keep it alive across scene transitions
        DontDestroyOnLoad(this.gameObject);

        Debug.Log("GameManager initialized and persisted.");
    }

    // Example: a game state method accessible from anywhere
    public void AddScore(int amount)
    {
        // Score addition logic goes here
        Debug.Log("Score increased by " + amount + " points.");
    }
}

The key point of this code is that it checks whether an instance already exists inside Awake(), and if one does, it immediately destroys the newly created copy of itself. This guarantees that no matter how many times you load scenes, only one GameManager ever exists.

Think of it as a gatekeeper check: if the first one is already inside, any second one that shows up later gets turned away at the gate (Destroy).

Diagram of the Singleton gatekeeper check: if an instance already exists, the second one created by a scene load is immediately destroyed by the check in Awake, so exactly one instance is always maintained

From any other script, you can now safely access this manager at any time with GameManager.Instance.AddScore(100);.

Sponsored

Hands-On: One BGM Track Across Title, Game, and Results

Now that you understand the mechanism, let's finish it in its most common form. Whether you're making an RPG, a puzzle game, or a visual novel, the goal is the same: cycle through Title → Game → Results → Title as many times as you like, and the BGM keeps playing — never cut off, never doubled, exactly one track.

Finished setup: one BgmPlayer survives a full loop through Title, Game, and Results, and the BGM never stops

We'll add the gatekeeper to the earlier BgmPlayer.

using UnityEngine;

[RequireComponent(typeof(AudioSource))]
public class BgmPlayer : MonoBehaviour
{
    public static BgmPlayer Instance { get; private set; }

    void Awake()
    {
        // Gatekeeper check: if the first one is already here, turn the second one away
        if (Instance != null && Instance != this)
        {
            Destroy(gameObject);
            return;
        }
        Instance = this;
        DontDestroyOnLoad(gameObject); // Must be called on a root GameObject (see failure #2 below)
    }
}

Setup steps:

  1. In the title scene, create an empty GameObject named "BgmPlayer" at the top level of the Hierarchy (root). Add an AudioSource (assign your BGM, enable Play On Awake and Loop) plus the script above
  2. Don't place it in the game or results scenes. The single instance carried over from the title handles everything (and even if you accidentally place one, the gatekeeper destroys the duplicate)
  3. Cycle Title → Game → Results → Title three times and verify that (a) the BGM never cuts off, and (b) the "DontDestroyOnLoad" scene that appears at the bottom of the Hierarchy during Play mode contains exactly one BgmPlayer

Separate "What Persists" from "What Resets"

A common mistake once you get comfortable with persistent objects is leaving per-run state sitting on the persistent manager. Your score and lives survive the return to the title screen, and you get the "New Game starts with last run's score" bug. Here's the dividing line:

  • OK to persist: BGM, settings like volume, "what has been unlocked" save data
  • Reset on New Game: score, lives, combos, and other per-run state. Always initialize them when a run starts
// If your persistent GameManager holds per-run state, don't forget to reset it at the start
public void StartNewGame()
{
    score = 0;
    lives = 3;
    SceneManager.LoadScene("Game");
}

Break It on Purpose, Then Fix It

There are three classic accidents with this topic. Try causing each one in a safe environment.

  1. Place it in every scene → doubled audio: Comment out the gatekeeper if, put a BgmPlayer in the game scene too, and cycle scenes — the BGM stacks into two muddy layers (and keeps stacking with every trip). Restore the gatekeeper and the second copy is destroyed the moment it's born, bringing you back to one track
  2. Attach it to a child object → doesn't persist: Make BgmPlayer a child of something and press Play — the Console shows a warning and the object doesn't persist. DontDestroyOnLoad only works on root GameObjects. If you really need to keep something that lives under a parent, detach it first with transform.SetParent(null) before calling it
  3. Forget to unsubscribe from a static event → handlers multiply: If a scene object subscribes to a static event like SceneManager.sceneLoaded += OnSceneLoaded and forgets to -= in OnDestroy, subscriptions pile up every time you revisit the scene, and the same handler runs two, three times (and throws errors against already-destroyed objects). The iron rule: whenever you subscribe to a static or persistent object's event, always unsubscribe on destroy

The final check is three round trips in Play mode. Title → Game → Results and back again — the BGM never hiccups, and the DontDestroyOnLoad section of the Hierarchy holds exactly one BgmPlayer. If a New Game ever greets you with last run's score, the culprit is an overloaded persistent object — revisit the reset in StartNewGame.

Bonus: Good to Know for Later

Once you're comfortable with persistent objects, these topics are a natural next step.

  • Going deeper on the Singleton pattern: It's convenient, but overusing it can tangle up your dependencies — it's a double-edged sword. Learn the correct implementation and the caveats in the Singleton pattern article.
  • How scene transitions work in the first place: The usage of LoadScene/LoadSceneAsync is covered in the SceneManager article.
  • If it's "just data," you may not need to persist an object at all: For data like scores and settings, instead of keeping a whole GameObject alive, you can hold it in a ScriptableObject or in save data. The ScriptableObject article and the save & load article are good references.

Summary

DontDestroyOnLoad is a powerful tool for managing persistent objects in Unity. Here are the key takeaways from this article:

  • Role of DontDestroyOnLoad: Use it to keep specific GameObjects alive across scene changes. It's well suited for things that persist across the whole game, like BGM players and game managers.
  • Basic usage: Call DontDestroyOnLoad(this.gameObject) inside the Awake() method of a script attached to the object you want to persist.
  • Biggest caveat: Using DontDestroyOnLoad on its own can create duplicate objects every time you switch scenes, which is a common source of bugs.
  • Practical solution: The recommended approach is to combine DontDestroyOnLoad with the Singleton pattern, which prevents duplication and guarantees a single instance throughout the game.
  • Access method: By making it a Singleton, you can safely and easily access the persistent object from anywhere in the game via ClassName.Instance.

With this knowledge in hand, scene management and object persistence in your Unity projects will be much more robust. Happy game developing!