Menu
CoddyTech

A Generic Singleton

Part of the Game Architecture in Unity section of Coddy's Unity journey. Lesson 43 of 64.

Section 3's singleton is five lines that every manager repeats: a static Instance, an Awake that sets it, the duplicate check. A generic base class writes them once. Each manager derives from it, naming itself as the type:

public abstract class Singleton<T> : MonoBehaviour where T : MonoBehaviour
{
    public static T Instance { get; private set; }

    protected virtual void Awake()
    {
        if (Instance != null && Instance != this)
        {
            Destroy(gameObject);
            return;
        }
        Instance = this as T;
    }
}

public class ScoreManager : Singleton<ScoreManager>
{
    public int score;
    public void Add(int points) { score += points; }
}

public class SoundManager : Singleton<SoundManager>
{
    public void Play(string clip) { Debug.Log("Playing " + clip); }
}
// in any script
ScoreManager.Instance.Add(10);
SoundManager.Instance.Play("coin");

A static member of a generic class belongs to each type made from it: Singleton<ScoreManager> and Singleton<SoundManager> are two types with an Instance each, so the managers never share one. ScoreManager.Instance is already typed ScoreManager, which is what T is for. Inside the base, this is only known as a Singleton<T>, so this as T hands it over as the manager it really is.

The base is abstract: Unity puts only the managers on objects. A manager that needs Awake of its own must not write a plain void Awake(). That hides the base's method (the compiler warns about it), Unity calls only the manager's, and Instance is never set. Override it and call the base first:

protected override void Awake()
{
    base.Awake();
    score = 0;
}
challenge icon

Challenge

Medium

Give both managers a base that survives scene loads.

  1. In PersistentSingleton, write protected virtual void Awake(): the first becomes Instance and stays (DontDestroyOnLoad); a later one destroys itself.
  2. Wallet and MusicPlayer derive from it; delete their Instance and Wallet's Awake.
  3. MusicPlayer overrides Awake: base.Awake(), then, if it is the Instance, logs Now playing: Adventure.

Press Play, visit the forest and come back: 1 coin. Then Check.

Try it yourself

using UnityEngine;

public abstract class PersistentSingleton<T> : MonoBehaviour where T : MonoBehaviour
{
    public static T Instance { get; private set; }

    // Awake: keep the first one through scene loads, destroy any later one
}

All lessons in Game Architecture in Unity

Practice on your own: Unity playground