Skip to content

Instantly share code, notes, and snippets.

@Ashwinning
Last active March 31, 2020 15:18
Show Gist options
  • Save Ashwinning/5a1d5858959af0396b04 to your computer and use it in GitHub Desktop.
Save Ashwinning/5a1d5858959af0396b04 to your computer and use it in GitHub Desktop.
A copy of the post on Singletons in Unity/C# as it appeared on UnityPatterns.com

Singletons

Original post by David Laskey on UnityPatterns

As you develop in Unity, you’ll often find that it’s handy to have just single instances of certain objects. For example, let’s say we have a single GameObject, “MusicManager”, that’s in charge of our music. It might look like this:

using UnityEngine;
 
public class MusicManager : MonoBehaviour 
{
    public void Play()
    {
        //Play some audio!
    }
}

So now, let’s say some other GameObject wants to start playing the music. It could call a function on MusicManager like so:

//Let's play some music...
Object.FindObjectOfType<MusicManager>().Play();

While this method works just fine, it’s pretty ugly looking code. Also, if there is no MusicManager object in our scene, this will cause an error! So to make the code safe, it should probably do a null check, like so:

var musicManager = Object.FindObjectOfType<MusicManager>();
if(musicManager != null)
        musicManager.Play();

That’s better! But now, every time we want to call this function, we have to write all this, which can get pretty tedious.

To remedy this problem, we have a handy tool called the Singleton pattern. In Unity there are a few different ways to implement Singletons, based on your needs, so we’ll look at a few different methods.

The Lazy Singleton

This type of singleton is the quick and dirty method. It’s no less valid than the rest, it just gets its name from avoiding a check to see if it’s been initialized yet.

public class MusicManager : MonoBehaviour 
{
    //We make a static variable to our MusicManager instance
    public static MusicManager instance { get; private set; }
 
    //When the object awakens, we assign the static variable
    void Awake() 
    {
        instance = this;
    }
 
    public void Play()
    {
        //Play some audio!
    }
}
 
//...
//Now in another class, we can call Play() by using the static variable!
public class LevelController : MonoBehaviour
{
    void PlayMusic()
    {
        MusicManager.instance.Play();
    }
}

Here we still aren’t doing a null check, this is because we always assume that the instance will be accessed after the Awake() call to set it. This is why this is called the Lazy Singleton.

❗ When using Lazy Singletons, never try to access the instance in another Awake() call, always put it in a function that is guaranteed to be called later, such as Start().

The Standard Singleton

This version is an improved version of the Lazy Singleton which ensures that the instance reference is never null.

public class MusicManager : MonoBehaviour 
{
    //Here is a private reference only this class can access
    private static MusicManager _instance;
 
    //This is the public reference that other classes will use
    public static MusicManager instance
    {
        get
        {
            //If _instance hasn't been set yet, we grab it from the scene!
            //This will only happen the first time this reference is used.
            if(_instance == null)
                _instance = GameObject.FindObjectOfType<MusicManager>();
            return _instance;
        }
    }
 
    public void Play()
    {
        //Play some audio!
    }
}

This singleton will only ever give you a null error if you forgot to put it in the scene. But what happens if you want your Singleton to exist in every single scene, so you don’t have to remember to put it in every time?

The Persistent Singleton

Sometimes you need your singletons to last between scenes (for example, in this case you might want to play music during a scene transition). One way to do this is to call DontDestroyOnLoad() on your singleton.

public class MusicManager : MonoBehaviour 
{
    private static MusicManager _instance;
 
    public static MusicManager instance
    {
        get
        {
            if(_instance == null)
            {
                _instance = GameObject.FindObjectOfType<MusicManager>();
 
                //Tell unity not to destroy this object when loading a new scene!
                DontDestroyOnLoad(_instance.gameObject);
            }
 
            return _instance;
        }
    }
 
    void Awake() 
    {
        if(_instance == null)
        {
            //If I am the first instance, make me the Singleton
            _instance = this;
            DontDestroyOnLoad(this);
        }
        else
        {
            //If a Singleton already exists and you find
            //another reference in scene, destroy it!
            if(this != _instance)
                Destroy(this.gameObject);
        }
    }
 
    public void Play()
    {
        //Play some audio!
    }
}
❗ DontDestroyOnLoad() will maintain all scripts that are placed on the same GameObject as your singleton. For this reason it’s usually a good idea to put a singleton on it’s own GameObject alone.

Other Notes:

  • DontDestroyOnLoad() only needs to be used on objects inheriting MonoBehaviour. A static reference in a normal class will maintain its data across scenes.
  • In Unity, if you have a reference to a GameObject or MonoBehaviour that has been destroyed, it will equal null. The singleton examples above use this to their advantage.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment