← All patterns
Creational

Singleton

Guarantee a class has exactly one instance and give everyone a single, well-known way to reach it.

What Is It?

A Singleton restricts a class to one instance and exposes it through a static access point, usually a property called Instance. Whoever asks first causes it to exist; everyone after that receives the very same object.

In Unity the twist is that most singletons are MonoBehaviours living on a GameObject. That means the "one instance" rule has to survive scene loads (DontDestroyOnLoad) and has to cope with a designer accidentally dropping a second copy into another scene — the newcomer must destroy itself.

A typical example is a GameManager that tracks score, pause state and the current level. The player, the HUD and every enemy can call GameManager.Instance.AddScore(10) without anyone wiring references in the Inspector.

When Is It Used?

Use it for genuinely global services where a second copy would be a bug: an audio mixer controller, a save system, a platform/achievements bridge, or a top-level game state manager.

It is handy in prototypes and game jams where speed of wiring matters more than testability — one static property beats dragging references across twenty prefabs.

Avoid it as a default way to share data. If many systems reach into Instance for everything, you get hidden dependencies and hard-to-test code; consider dependency injection or ScriptableObject services once the project grows.

Interactive Demo

Click the controls and watch the objects collaborate. The console mirrors what the C# code below would log.

Code

Assets / Scripts / Creational/ Singleton ›ScoreClient.cs
using UnityEngine;

namespace Patterns.Creational.Singleton
{
    /// <summary>
    /// A client that needs the GameManager. No Inspector reference:
    /// it simply asks for GameManager.Instance.
    /// </summary>
    public class ScoreClient : MonoBehaviour
    {
        [SerializeField] private int pointsPerPickup = 10;

        private void OnEnable()
        {
            GameManager.Instance.ScoreChanged += OnScoreChanged;
        }

        private void OnDisable()
        {
            // Instance may be null while the application is quitting.
            var gm = GameManager.Instance;
            if (gm != null) gm.ScoreChanged -= OnScoreChanged;
        }

        private void OnTriggerEnter(Collider other)
        {
            if (other.CompareTag("Pickup"))
            {
                GameManager.Instance.AddScore(pointsPerPickup);
                Destroy(other.gameObject);
            }
        }

        private void Update()
        {
            if (Input.GetKeyDown(KeyCode.Escape))
                GameManager.Instance.SetPaused(!GameManager.Instance.IsPaused);

            if (Input.GetKeyDown(KeyCode.N))
                GameManager.Instance.LoadLevel("Level2");
        }

        private void OnScoreChanged(int score) => Debug.Log($"Score: {score}");
    }
}
3 files · namespace Patterns.Creational.SingletonC# · UTF-8 · LF

Advantages & Disadvantages

+ Advantages

  • Single access point — no Inspector wiring, any script can find the service.
  • Lazy creation means the object only exists once something actually needs it.
  • With DontDestroyOnLoad it naturally carries state (score, settings) across scenes.

− Disadvantages

  • Creates hidden global dependencies; it is hard to see which classes rely on it.
  • Unit testing is awkward because the static instance leaks between tests.
  • Initialization order issues: calling Instance from another Awake can hit a half-initialised object.

Tips

  1. 01Use a generic Singleton<T> base class so every manager gets the same duplicate-handling and persistence logic for free.
  2. 02In Awake, if Instance already exists and is not this, call Destroy(gameObject) immediately — this handles the "manager placed in every scene" case.
  3. 03Unity APIs are main-thread only, so a MonoBehaviour singleton rarely needs locking. For plain C# singletons accessed from worker threads, use Lazy<T> for thread-safe lazy initialisation.
  4. 04Clear the static reference in OnDestroy and, with Enter Play Mode Options (no domain reload), reset statics via [RuntimeInitializeOnLoadMethod].
  5. 05Guard against OnApplicationQuit ordering: other objects destroyed after the manager may call Instance and accidentally spawn a fresh one.