Singleton Pattern

Summary

The singleton is a design pattern from the Gang of Four’s Design Patterns catalogue, whose stated intent is to “ensure a class has one instance, and provide a global point of access to it” (as quoted by Nystrom, Game Programming Patterns, “Singleton” chapter). Robert Nystrom points out that this is really two ideas bundled together: restricting a class to a single instance, and making that instance globally reachable. Most code that uses a singleton only wants the second part.

In Unity, a singleton is usually a MonoBehaviour with a static property called Instance. The first copy to wake up stores itself in Instance; any later copy destroys itself; and any script can then write AudioManager.Instance.PlaySfx(clip) without an Inspector reference. Unity’s own patterns guide describes manager-level objects such as a game manager or a file manager as natural candidates, while noting that the singleton is “one of the most maligned design patterns” because its ease of use invites overuse (Unity Technologies, Level Up Your Code with Game Programming Patterns, see source-unity-game-programming-patterns).

Use it sparingly, for a small number of genuinely global services (audio, a persistent game manager, save data), and only once a plain Inspector reference has become impractical. The unity-gamemanager-pattern page shows the singleton applied to a GameManager; this page covers the pattern itself, its risks and its alternatives.

Implementation

A minimal Unity singleton

using UnityEngine;
 
public class AudioManager : MonoBehaviour
{
    public static AudioManager Instance { get; private set; }
 
    [SerializeField] private bool persistAcrossScenes = true;
    [SerializeField] private AudioSource sfxSource;
 
    private void Awake()
    {
        if (Instance != null && Instance != this)
        {
            Debug.LogWarning($"Duplicate AudioManager on {name} destroyed.", this);
            Destroy(gameObject);
            return;
        }
 
        Instance = this;
        if (persistAcrossScenes)
            DontDestroyOnLoad(gameObject);   // only works on a root GameObject
    }
 
    private void OnDestroy()
    {
        if (Instance == this) Instance = null;
    }
 
    public void PlaySfx(AudioClip clip) => sfxSource.PlayOneShot(clip);
}

Reading it line by line:

  1. public static AudioManager Instance { get; private set; } is a property that belongs to the class rather than to any object (see csharp-oop-fundamentals on static). Anyone can read it; only this class can set it.
  2. The duplicate guard in Awake keeps the first instance and destroys any other. Without it, reloading a scene that contains the manager produces two managers, and anything they count is counted twice.
  3. DontDestroyOnLoad keeps the GameObject alive when a new scene loads. The Scripting API notes that it “only works for root GameObjects or components on root GameObjects”, so the manager must not be a child of another object (Unity Technologies, Scripting API: Object.DontDestroyOnLoad, Unity 6.3). Leave persistAcrossScenes off for a manager that should belong to one scene.
  4. OnDestroy clears Instance so that nothing keeps talking to a destroyed manager.

Any script can now use it without wiring:

public class CoinSound : MonoBehaviour
{
    [SerializeField] private AudioClip pickupClip;
 
    private void OnTriggerEnter2D(Collider2D other)
    {
        if (!other.CompareTag("Player")) return;
        AudioManager.Instance.PlaySfx(pickupClip);
    }
}

Unity’s sample project for its patterns guide contains three versions of this idea: SimpleSingleton (the guard only), Singleton<T> (a generic base class with lazy creation) and PersistentSingleton<T> (the same plus DontDestroyOnLoad), with an AudioManager and a GameManager as example users (Unity Technologies, game-programming-patterns-demo repository on GitHub, 3_Singleton folder, read 2026-10-25).

Generic Singleton<T>

If you have several managers, repeating the guard in each becomes tedious. A generic base class, public class Singleton<T> : MonoBehaviour where T : Component, holds the guard once, and each manager inherits from it: public class GameManager : Singleton<GameManager>. The full, corrected implementation (with the else if (_instance != this) guard and Unity 6’s FindAnyObjectByType) is on unity-gamemanager-pattern, under Generic Singleton; Unity’s guide presents the same approach (Unity Technologies, Level Up Your Code with Game Programming Patterns, see source-unity-game-programming-patterns).

That version adds lazy instantiation: if Instance is read before any manager exists, it searches the scene and, failing that, creates a new GameObject with the component. This is convenient, but the created object has none of the Inspector settings a placed manager would have, so prefer placing the manager in your first scene and treat lazy creation as a fallback.

Resetting static state when domain reload is off

By default Unity reloads the scripting domain on entering Play mode, which resets every static field. If you turn this off for faster iteration (Project Settings > Editor > Enter Play Mode Settings), static fields such as Instance keep their values between Play sessions, and the Manual says you must then reset that state in code, for example with [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] (Unity Technologies, Enter Play mode with domain reload disabled, Unity 6.3 Manual):

    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
    private static void ResetStatics() => Instance = null;

Trade-offs

What Unity’s guide says in favour

For small games that do not need long-term extensibility, Unity’s guide lists three benefits: singletons are quick to learn, user-friendly (any component can reach the instance), and performant, because global access avoids repeated GetComponent or Find calls. It concludes: “If you decide to use singletons in your project, keep them to a minimum… Reserve the singletons for a handful of scripts that can benefit from global access” (Unity Technologies, Level Up Your Code with Game Programming Patterns).

Cautions

  • Hidden dependencies. A script that calls AudioManager.Instance depends on the audio manager, but nothing in the Inspector or the class’s fields shows it. Unity’s guide warns that global access can “hide many dependencies, making bugs much harder to troubleshoot”. Nystrom’s central objection is the same: a singleton is a global variable, “just encapsulated in a class”, and globals make code harder to reason about.
  • Tight coupling. Because every script can reach the singleton, many end up depending on it, and changing it affects all of them. Unity’s guide notes that most patterns try to decouple dependencies while singletons “do the opposite”; Nystrom puts it as “by controlling access to instances, you control coupling”.
  • Testing. Unit tests should be independent of one another, but a singleton carries state from one test to the next and can change many objects at once, so Unity’s guide lists testing difficulty as a key drawback.
  • Scene reloads. Reloading or returning to a scene that contains the manager creates a duplicate, which the guard must destroy. A persistent manager also outlives the scene it was configured in, so any Inspector references it held to objects in that scene stop working once the scene unloads (Unity’s == comparison treats destroyed objects as null). Keep persistent managers self-contained, or have scene objects register with them when they start.
  • Execution order. The order in which different objects’ Awake methods run is not guaranteed, but Start only runs after every Awake in the scene (Unity Technologies, Scripting API: MonoBehaviour.Awake and MonoBehaviour.Start). Reading Instance from another object’s Awake or OnEnable can therefore find it still null. Read it in Start or later, as unity-gamemanager-pattern explains, or set an explicit order under Project Settings > Script Execution Order.
  • Lazy creation at a bad moment. Nystrom notes that lazily initialising an expensive system in the middle of gameplay can cause a visible stutter; a manager placed in the first scene avoids this.
  • “Manager” creep. Nystrom suggests that many singleton managers exist only because responsibilities were pulled out of the objects they manage: “OOP is about letting objects take care of themselves.” If a manager just pokes at other objects’ data, consider moving that behaviour back into the objects.

Alternatives

NeedAlternativeNotes
A few scripts in one scene need the managerInspector reference ([SerializeField] private AudioManager audioManager;)The dependency is visible and can be swapped; see unity-inspector-references and getcomponent-vs-inspector-references. This is the simplest form of dependency injection.
Code needs a service but should not care where it comes fromPass it in (constructor or method parameter, or an Init(audioManager) call from a spawner)Nystrom’s first suggested alternative; makes dependencies explicit and testable.
Many listeners need to know something happenedEvents: C# events (observer-pattern) or scriptableobject-event-channelsListeners depend only on the event or channel asset, not on the manager; a channel asset also exists before any OnEnable runs.
Shared data across scenes (settings, score)ScriptableObject assets (unity-scriptableobjects)Data lives in an asset that any scene can reference in the Inspector.
A large project with many servicesDependency injection frameworks or a service locatorA DI container builds objects and hands each one its dependencies; a service locator (Nystrom has a chapter on it) is a single registry that provides services by type. Both are beyond a beginner project, but worth knowing as the professional direction.

A reasonable progression is: Inspector reference first; then events for broadcasting; then a singleton only for the one or two systems that truly must be global and persistent.

Other engines

Godot provides autoloads: scripts or scenes listed in Project Settings that the engine loads before the main scene and keeps for the whole run, reachable by name from any script. Unreal Engine offers subsystems (for example a Game Instance Subsystem) whose lifetime the engine manages. Both give global access without hand-written duplicate guards, but they carry the same coupling cautions.

Examples

  • Unity’s patterns sample project uses a generic singleton for an AudioManager (play a sound effect from any GameObject) and a GameManager (Unity Technologies, game-programming-patterns-demo, 3_Singleton/Scripts/ExampleUsage).
  • unity-gamemanager-pattern - a persistent GameManager that owns score and lives and raises events for UI
  • object-pool-pattern - Unity’s guide notes that a pool can be designed as a singleton so pooled objects are easy to request

Practice

Build and then stress-test an AudioManager singleton:

  1. Create two scenes, Level01 and Level02, and add both to the build profile’s scene list.
  2. Put an AudioManager GameObject (root level, with an AudioSource) in Level01 only, using the minimal implementation above.
  3. Add a coin in each scene whose script calls AudioManager.Instance.PlaySfx(...), and a key press that loads the other scene with SceneManager.LoadScene (in UnityEngine.SceneManagement).
  4. Play Level01, collect the coin, switch to Level02, collect the coin there, then switch back to Level01.

Success test:

  • coin sounds play in both scenes, and the Hierarchy shows the manager under DontDestroyOnLoad
  • returning to Level01 logs the “Duplicate AudioManager” warning once and leaves exactly one manager
  • commenting out the duplicate guard and repeating step 4 produces two managers; you can explain why
  • starting Play mode directly in Level02 gives a NullReferenceException; you can explain why and name two fixes

Extension:

  • refactor one coin to take the AudioManager through a [SerializeField] field instead of Instance, and note what that makes visible in the Inspector that the singleton version hid
  • replace the coin-to-audio call with a ScriptableObject event channel that the AudioManager listens to, so the coin no longer references the manager at all

Self-test

  1. What two ideas does the singleton pattern combine, and which one is usually the one people actually want?
  2. Why does a Unity singleton need a duplicate guard in Awake?
  3. What does DontDestroyOnLoad do, and what restriction does it have?
  4. Give two drawbacks of singletons named in Unity’s own patterns guide.
  5. Why can reading Instance in another object’s Awake fail, and where is it safer to read it?
  6. Name two alternatives to a singleton and when each fits.

Answers

  1. Restricting a class to a single instance, and global access to that instance. Most code only wants the global access.
  2. Because a scene can contain more than one copy (for example after reloading the scene that holds the manager); without the guard you get two managers and doubled behaviour.
  3. It keeps the object alive when a new scene loads. It only works on root GameObjects (or components on them).
  4. Any two of: it hides dependencies, making bugs harder to troubleshoot; it makes testing difficult; it encourages tight coupling.
  5. The order of Awake calls between objects is not guaranteed, so the manager’s Awake may not have set Instance yet. Start is safer, because it runs only after every Awake in the scene.
  6. Any two of: an Inspector reference, when a few scripts in one scene need the manager; passing the dependency in, when you want it explicit and testable; events or ScriptableObject event channels, when many listeners need to react to a change; ScriptableObject assets, for shared data across scenes; dependency injection or a service locator, in larger projects.

External references