Posted on 13 Jan 2014

Singletons in Unity

Singletons?! What're they?

A singleton is a class that can't be directly instantiated, and should have a single instance that is accessible via a method or property of the class. There are a few methods of making sure that the single instance of the class has been created. Two popular methods are eager-loading and lazy-loading.

With eager-loading the instance is always created immediately, and that instance is returned every time the access method/property is called.

public class EagerLoadedSingleton {
    static readonly EagerLoadedSingleton instance = new EagerLoadedSingleton();

    private EagerLoadedSingleton() { }

    public static EagerLoadedSingleton Instance {
        get {                
            return instance;
        }
    }
}

Lazy-loading doesn't create an instance until it is accessed via the method/property. It will either return the already created instance, or it creates the instance right then and returns it.

public class LazyLoadedSingleton {
    static LazyLoadedSingleton instance;

    private LazyLoadedSingleton() { }

    public static LazyLoadedSingleton Instance {
        get {
            if(instance == null) {
                this.instance = new LazyLoadedSingleton();
            }
            return instance;
        }
    }
}

Both are valid ways to implement a singleton, but for the rest of this post we are going to use lazy-loading. Let's take a look at how we want to implement this in a Unity project!

Using a base singleton class in Unity

We are going to create a base class that all of our classes that we want to be singletons inherit from. Let's step through this!

// Abstract singleton class to inherit from.
public abstract class UnitySingleton<T> : MonoBehaviour where T : UnitySingleton<T> {
    static T instance;

    bool persist = false;

#region Basic getters/setters
    public bool Persist {
        get { return persist; }
        protected set { persist = value; }
    }
#endregion

As you can see this is an abstract generic class which inherits from MonoBehavior. It is constrained to only allow types of itself(or types derived from it) as the generic type. The instance of the singleton is defined as static T, i.e. the generic type passed to the class. The persist variable determines whether or not this object should persist when loading new scenes and is meant to be set in the child class' Init method.

    public static T Instance {
        get {
            // This would only EVER be null if some other MonoBehavior requests the instance
            // in its' Awake method.
            if(instance == null) {
                Debug.Log("[UnitySingleton] Finding instance of '" + typeof(T).ToString() + 
                          "' object.");
                instance = FindObjectOfType(typeof(T)) as T;
                // This should only occur if 'T' hasn't been attached to any game
                // objects in the scene.
                if(instance == null) {
                    Debug.LogError("[UnitySingleton] No instance of " + typeof(T).ToString()
                                   + "found!");
                    return null;
                }
                instance.Init();
            }
            return instance;
        }
    }

Now here is the property for accessing the instance of the singleton class. I will let the comments speak for themselves, and just note that as long as nothing funky is going on it should almost always have a value and just return it.

    void Awake() {
        Debug.Log("[UnitySingleton] Awake");
        if(instance == null) {
            Debug.Log("[UnitySingleton] Initializing Singleton in Awake");
            instance = this as T;
            instance.Init();
            if(persist)
                DontDestroyOnLoad(gameObject);
        }
    }

    virtual protected void Init() { }

    // Make sure no "ghost" objects are left behind when applicaton quits.
    void OnApplicationQuit() {
        instance = null;
    }
}

This Awake method will initialize our instance if it hasn't already been prompted to do so by another MonoBehavior's Awake requesting it first(as the comment just explained above in the Instance property). Following the Awake method you'll see a virtual Init method. This is the method you will override in your child class, and is where you should do any initialization you may have normally done in Awake when using a normal MonoBehavior class.

Finally, note the OnApplicationQuit method. The reason this method is here is just to make sure that when the application is quitting it deals with any re-initialized instance variables from other objects trying to access it during the cleanup.

Here is all of the code for UnitySingleton:

// Abstract singleton class to inherit from.
public abstract class UnitySingleton<T> : MonoBehaviour where T : UnitySingleton<T> {
    static T instance;

    // Whether or not this object should persist when loading new scenes.
    // This should be set in the child classes Init() method.
    bool persist = false;

    public static T Instance {
        get {
            // This would only EVER be null if some other MonoBehavior requests the instance
            // in its' Awake method.
            if(instance == null) {
                Debug.Log("[UnitySingleton] Finding instance of '" + typeof(T).ToString() + 
                          "' object.");
                instance = FindObjectOfType(typeof(T)) as T;
                // This should only occur if 'T' hasn't been attached to any game
                // objects in the scene.
                if(instance == null) {
                    Debug.LogError("[UnitySingleton] No instance of " + typeof(T).ToString()
                                   + "found!");
                    return null;
                }
                instance.Init();
            }
            return instance;
        }
    }

#region Basic getters/setters
    public bool Persist {
        get { return persist; }
        protected set { persist = value; }
    }
#endregion

    // This will initialize our instance, if it hasn't already been prompted to do so by
    // another MonoBehavior's Awake() requesting it first.
    void Awake() {
        Debug.Log("[UnitySingleton] Awake");
        if(instance == null) {
            Debug.Log("[UnitySingleton] Initializing Singleton in Awake");
            instance = this as T;
            instance.Init();
            if(persist)
                DontDestroyOnLoad(gameObject);
        }
    }

    // Override this in child classes rather than Awake().
    virtual protected void Init() { }

    // Make sure no "ghost" objects are left behind when applicaton quits.
    void OnApplicationQuit() {
        instance = null;
    }
}

Using this base class

It is pretty straight forward to use this base class. Let's say you want some sort of game manager class, and there should only ever be one instance of this game manager throughout the lifetime of the game. What a perfect situation for a singleton!

public class GameManager : UnitySingleton<GameManager> {
    private GameManager() { }

    protected override void Init() {
        Debug.Log("[GameManager] Init");
        Persist = true;
    }

    void Start() {
        Debug.Log("[GameManager] Start");
    }

    void Update() {
    }
}

That about does it! You can check out the repository for this here, and it is also listed on my projects page.