Menu
CoddyTech

A Generic Singleton

Part of the Game Architecture in Unity section of Coddy's Unity journey. Lesson 34 of 50.

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

A town and a forest, joined by purple portals. Each scene has its own Wallet and MusicPlayer object, so either one plays on its own, and the coins call Wallet.Instance.Add(1). Today each manager is section 3's singleton, so every load replaces the wallet with an empty one. Give both managers a generic base that keeps them through scene loads:

  • In PersistentSingleton.cs, write public abstract class PersistentSingleton<T>, a MonoBehaviour for any MonoBehaviour type T, with a public static Instance of type T that only the class sets, and a protected virtual void Awake().
  • Its Awake keeps the first of its kind: that one becomes Instance and survives scene loads. Any later one destroys its own object and changes nothing.
  • Wallet and MusicPlayer derive from it, and their own Instance fields go.
  • MusicPlayer keeps an Awake of its own that logs Now playing: Adventure. It must run after the base's Awake, and only for the one that stays, so the music starts once per game.

The arrows walk the Player. Press Play, pick up the coin, go to the forest and back, then Check.

Try it yourself

using UnityEngine;

// PersistentSingleton<T>: one instance that lasts through scene loads
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Game Architecture in Unity