intermediate Utilities

Generic Singleton

Thread-safe, persistent singleton base class for managers like AudioManager, GameManager, etc.

Unity 2022.3+ · 1.2 KB · Singleton.cs

How to Use

1

Create your manager class inheriting from Singleton:

2

public class AudioManager : Singleton { }

3

Access anywhere with AudioManager.Instance

4

The singleton persists across scene loads automatically

Features

  • Generic base class — inherit to make any MonoBehaviour a singleton
  • Thread-safe access with lock object for multi-threaded scenarios
  • Auto-creates instance if none exists in the scene
  • DontDestroyOnLoad persistence across scene changes
  • Duplicate destruction — extra instances are automatically removed
  • Application quit guard prevents ghost instances during shutdown

When to Use This

Use whenever you need a single global manager that persists across scenes — GameManager, AudioManager, SaveManager, InputManager, etc. Perfect for any game architecture that requires centralized systems accessible from anywhere via a static Instance property. Especially useful in larger projects with multiple scenes.

Common Mistakes

Placing two GameObjects with the same Singleton subclass in a scene will cause one to be destroyed immediately — this is intended, but can be confusing. If you override Awake() in your subclass, you must call base.Awake() or the singleton setup breaks. Accessing Instance during OnDestroy or OnApplicationQuit may return null due to the isQuitting guard.

Source Code

Singleton.cs
C#
using UnityEngine;

/// <summary>
/// Generic singleton base class. Inherit from this to create a singleton manager.
/// Usage: public class GameManager : Singleton<GameManager> { }
/// </summary>
public abstract class Singleton<T> : MonoBehaviour where T : MonoBehaviour
{
    private static T instance;
    private static readonly object lockObj = new object();
    private static bool isQuitting;

    public static T Instance
    {
        get
        {
            if (isQuitting)
                return null;

            lock (lockObj)
            {
                if (instance == null)
                {
                    instance = FindAnyObjectByType<T>();

                    if (instance == null)
                    {
                        var go = new GameObject(typeof(T).Name);
                        instance = go.AddComponent<T>();
                    }
                }
                return instance;
            }
        }
    }

    protected virtual void Awake()
    {
        if (instance == null)
        {
            instance = this as T;
            DontDestroyOnLoad(gameObject);
        }
        else if (instance != this)
        {
            Destroy(gameObject);
        }
    }

    private void OnApplicationQuit()
    {
        isQuitting = true;
    }
}