Singletons In Unity – How To Implement Them The Right Way
December 10, 2022
Singleton is a design pattern that is used as a container which holds values that can be globally accessible across the whole project.
Singletons are very useful because they can transfer game data from scene to scene without the need to save and load data in the background.
There’s also a debate amongst game programmers should you be using the singleton pattern at all because if your game gets too large then it will be hard to manage and so on.
We don’t believe that is case because you’re not going to have 100 singletons in a project. You’re going to have one or a couple of them and they save you a lot of time because they provide an easy way for classes to communicate between each other.
Before we start, this tutorials is for beginners as well as intermediate and advanced developers.
We’ll cover everything from how to create a simple singleton to more complex operations you can perform with it so both beginners and advanced developers will benefit.
And you can always use the table of content on the left side to navigate the post and learn what you want.
Also, we do expect beginners who follow this post to know the basics of Unity like creating a script, attaching it on a game object, knowing how a class works and so on as we don’t plan to cover the complete beginner stuff.
But if you are a complete beginner, you can start learning by clicking on the link below.
C# Programming In Unity – Introduction To Variables
How To Create A Singleton?
As we already mentioned, a singleton is accessible globally, and it also needs to exist only once e.g. only one copy of the singleton can exist in the game.
Of course, this is referring to one copy of the singleton with one purpose e.g. only one copy of a the TagManager singleton which holds all the strings you’ll use in the game, one copy of the AudioPlayer singleton which plays the audio sounds in the game and so on.
The code to create a singleton looks like this:
using UnityEngine;
public class Singleton : MonoBehaviour
{
public static Singleton instance;
private void Awake()
{
if (instance == null)
instance = this;
}
}
The instance declared on line 5 is the instance of the class itself. Because we want to access it globally we need to make it static since static variables can be called from any class.
If you don’t know what are static classes you can learn about them here: Static Classes In Unity
On line 9 we are testing if the instance is equal to null meaning we don’t have a reference to the instance variable.
And if that is the case we set the instance to be equal to this which refers to the class holding the script where the keyword this is used – in this case the Singleton class.
Now for the sake of example, let’s image this Singleton class has a score variable that we’ll use to display score in the console when a button is pressed:
[HideInInspector]
public int score;
Here we have two scripts each with one function. Each function is going to add either 1 or 2 to the current value of the score variable and print it in the console:
using UnityEngine;
public class AddOneToScore : MonoBehaviour
{
public void AddToScore()
{
Singleton.instance.score++;
Debug.Log("The Score Is: " + Singleton.instance.score);
}
}
using UnityEngine;
public class AddTwoToScore : MonoBehaviour
{
public void AddToScore()
{
Singleton.instance.score += 2;
Debug.Log("The Score Is: " + Singleton.instance.score);
}
}
These scripts are attached on 2 game objects which are attached on appropriate buttons that we’ll press to increase the score value:
As you saw, depending on which button we pressed, we added either 1 or 2 to the score variable in the Singleton class and we printed that value.
One thing to note is, the score value was persistent across the two classes. And this is the main purpose of the Singleton pattern – allowing easy global access to variables between classes.
The Problem With The Current Singleton Setup
The current setup we have is actually not a singleton.
How?
Because we can have another game object with the same Singleton script attached on it and both will stay in the game:
And since the goal of the Singleton is to have only one instance or one copy of the object we need to destroy the other.
To do that, we need to add the following code:
using UnityEngine;
public class Singleton : MonoBehaviour
{
public static Singleton instance;
[HideInInspector]
public int score;
private void Awake()
{
if (instance == null)
instance = this;
else
Destroy(gameObject);
}
}
In the first Singleton example we saw, we only performed the check on line 12 to see if the instance is equal to null then we set the instance to be equal to this.
Here we have the else condition on line 14 which will destroy the game object which currently holds this script in case the instance is not equal to null.
This means, if we have two copies of the singleton in one scene, one will be destroyed:
This is how we make sure there’s only one copy of the Singleton class in the game.
But we still have one more problem and that is, if we load another scene, we will lose the Singleton:
While we have a reference to the Singleton in Scene 1, when we load Scene 2 we saw no trace of the Singleton object.
The Singleton object needs to be persistent across scenes because that’s how we carry data from one scene to another.
To do that, we just need to add one final line of code:
using UnityEngine;
public class Singleton : MonoBehaviour
{
public static Singleton instance;
[HideInInspector]
public int score;
private void Awake()
{
CreateSingleton();
}
void CreateSingleton()
{
if (instance == null)
instance = this;
else
Destroy(gameObject);
DontDestroyOnLoad(gameObject);
}
}
The code on line 22 will make sure the game object on which the Singleton class is attached to will not get destroyed when we move between the scenes.
We also created a function CreateSingleton so that we keep our code clean and organized which is the right way to code, contrary to what all other tutorials and courses show where they have code scattered all over the place, especially in the Awake function.
So always make sure to group your code which will make it more clean and readable and essentially this how you need to code if you want to get hired in a game studio.
Now our Singleton is persistent across scenes:
The code we added does not only make our Singleton class persistent across scenes, but it also prevents from having copies of the same Singleton by destroying the copy in the previous scene.
Because our original Singleton is in Scene 1, when we go to Scene 2 there is not Singleton game object except the one that has transfered from Scene 1.
But when we go back to Scene 1, the original Singleton object is there, but since we are calling the Destroy function on line 22 in the code above, the copy is destroyed and only one Singleton remains in the game.
Different Examples Of Setting Up A Singleton Pattern
Same as with anything in programming there are multiple ways how to do things and of course, there are multiple ways to set up a Singleton class.
You’ll probably encounter some examples online, so we’ll list the common ones here because not all of them are easy to read and in our personal opinion look a little complicated but since you’ll see them anyway, might as well know how they look like.
We’ll list the examples below and use comments in the code to explain what is going on.
using UnityEngine;
public class Singleton : MonoBehaviour
{
// declaring an instance of the singleton
// using the accessor method to create
// a public get and private set
// this will allow any class to get the instance variable
// but it will not allow anyone outside of this class
// to set the instance variable
public static Singleton instance { get; private set; }
private void Awake()
{
if (instance != null && instance != this)
Destroy(this);
else
instance = this;
}
}
using UnityEngine;
public class Singleton : MonoBehaviour
{
// declaring a private instance variable
private static Singleton instance = null;
// creating a public accessor that will get the instace
public static Singleton Instance
{
get
{
// test if the instance is null
// if so, try to get it using FindObjectOfType
if (instance == null)
instance = FindObjectOfType();
// if the instance is null again
// create a new game object
// attached the Singleton class on it
// set the instance to the new attached Singleton
// call don't destroy on load
if (instance == null)
{
GameObject gObj = new GameObject();
gObj.name = "Singleton";
instance = gObj.AddComponent();
DontDestroyOnLoad(gObj);
}
return instance;
}
}
private void Awake()
{
if (instance == null)
{
instance = this;
DontDestroyOnLoad(gameObject);
}
else
{
Destroy(gameObject);
}
}
}
The example above is very commonly used in tutorials and courses but sadly doesn’t follow good coding practices and writing clean code.
It’s just too many lines of code that can be written with way less and without the unnecessary adding of components and creating game objects.
Moving forward we have the generic singleton implementation:
using UnityEngine;
// <> denotes this is a generic class
public class GenericSingleton : MonoBehaviour where T : Component
{
// create a private reference to T instance
private static T instance;
public static T Instance
{
get
{
// if instance is null
if (instance == null)
{
// find the generic instance
instance = FindObjectOfType();
// if it's null again create a new object
// and attach the generic instance
if (instance == null)
{
GameObject obj = new GameObject();
obj.name = typeof(T).Name;
instance = obj.AddComponent();
}
}
return instance;
}
}
public virtual void Awake()
{
// create the instance
if (instance == null)
{
instance = this as T;
DontDestroyOnLoad(this.gameObject);
}
else
{
Destroy(gameObject);
}
}
}
If for example, you want to use the generic singleton class for the UI manager you would create the class like this:
public class UIManager : GenericSingleton
{
}
Let’s assume that the UIManager class has a function to load the game from the main menu:
public class UIManager : GenericSingleton
{
public void LoadGame()
{
}
}
This is how we would call the LoadGame function from another class:
UIManager.Instance.LoadGame();
This is a nice way to set up a generic Singleton class if you’re going to have multiple classes in your project.
Next, we’ll cover simple examples that’ll show you how to use the Singleton pattern in your project in the most efficient and simplest way possible without leaving a single chance to have any issues when the project gets bigger.
The Dangers Of The Singleton Pattern
As we already mentioned, a lot of game programmers will say that you should not use the Singleton pattern. Even more software developers will say that.
But again, it all comes back to how you’re using the pattern.
What most game devs do is saturate the singleton pattern. They put too much things inside and too many responsibilities for the Singleton.
Here’s one example when using the Singleton pattern as an audio manager:
public class AudioManager : MonoBehaviour
{
public static AudioManager instance;
private AudioSource audioPlayer;
[SerializeField]
private AudioClip footstepSound, shootingSound, coinSound, gameOverSound;
private void Awake()
{
audioPlayer = GetComponent();
if (instance != null)
Destroy(gameObject);
else
instance = this;
DontDestroyOnLoad(gameObject);
}
public void PlayFootstepSound()
{
audioPlayer.clip = footstepSound;
audioPlayer.Play();
}
public void PlayShootingSound()
{
audioPlayer.clip = shootingSound;
audioPlayer.Play();
}
public void PlayCoinSound()
{
audioPlayer.clip = coinSound;
audioPlayer.Play();
}
public void PlayGameOverSound()
{
audioPlayer.clip = gameOverSound;
audioPlayer.Play();
}
}
As crazy as this setup looks, sadly it’s very common to find in YouTube tutorials and paid courses.
The issue with this setup is that the Singleton class should not be responsible for holding references for the sounds it needs to play.
Also it should not have a separate function for every sound. There should be one function that can play the sound effect needed at that time.
The bottom line is, to make the Singleton efficient you need to use it for just one purpose and don’t give it more responsibility than it needs.
Next we are going to take a look at a few examples how to use the Singleton class the right way which will allow you to create games easier and have better project structure.
How To Use The Singleton Pattern The Right Way
Starting with the previous examples of having an audio manager Singleton, how can we re-write the code to make the Singleton more efficient and not introduce bugs later on.
Here’s the correct version of the audio manager Singleton:
public class AudioManager : MonoBehaviour
{
public static AudioManager instance;
private AudioSource audioPlayer;
private void Awake()
{
audioPlayer = GetComponent();
if (instance != null)
Destroy(gameObject);
else
instance = this;
DontDestroyOnLoad(gameObject);
}
public void PlaySound(AudioClip soundToPlay)
{
// example 1
audioPlayer.clip = soundToPlay;
audioPlayer.Play();
// example 2
audioPlayer.PlayOneShot(soundToPlay);
}
}
Here we follow the single responsibility principle where we created the audio manager and gave it only one responsibility which is to play the audio clip that we pass to its PlaySound function. That’s it.
The audio clips are going to be in other classes such as Player, Enemy, Coin, and so on.
Because it’s not the responsibility of the audio manager to have references of the audio clips it needs to play.
Now let’s say we have a few variables that need to be shared across the project like score, kill count, and so on.
This is how we would structure the Singleton class that will hold those variables:
public class GameplayManager : MonoBehaviour
{
public static GameplayManager instance;
private int score;
private int killCount;
private void Awake()
{
if (instance == null)
instance = this;
else
Destroy(gameObject);
DontDestroyOnLoad(gameObject);
}
public int GetScore()
{
return score;
}
public int GetKillCount()
{
return killCount;
}
public void IncreaseScore(int amount)
{
score += amount;
}
public void IncreaseKillCount(int amount)
{
killCount += amount;
}
}
Again, we see that the GameplayManager class has only one responsibility: holding the variables and having functions to change them and get their value.
So when the player kills an enemy in the game we simply call:
GameplayManager.instance.IncreaseKillCount(1);
And in the UI manager that will display the kill count score, would simply pass the variable:
UIManager.Instance.DisplayKillCount(GameplayManager.instance.GetKillCount());
Another thing we’ve seen tutorials, courses, and game devs in general do wrong is mix accessors with getters and setters like this:
UIManager.Instance.DisplayKillCount(GameplayManager.instance.GetKillCount());apublic class GameplayManager : MonoBehaviour
{
public static GameplayManager instance;
private int score;
private int killCount;
private void Awake()
{
if (instance == null)
instance = this;
else
Destroy(gameObject);
DontDestroyOnLoad(gameObject);
}
// using accessor
public int Score
{
get { return score; }
}
// using accessor
public int KillCount
{
get { return killCount; }
}
// using setter
public void IncreaseScore(int amount)
{
score += amount;
}
// using setter
public void IncreaseKillCount(int amount)
{
killCount += amount;
}
}
This is the wrong way to mix and match different setups in the code which can be confusing to read, especially for other programmers working on the same project.
So be careful how you set up the class structure.
The two examples above will be enough for you to create a Singleton for any purpose in your game.
Just remember, the Singleton should only be responsible for its tasks and be separated from other classes.
How To Use The Singleton Pattern To Reduce Bugs In Your Unity Project
Writing code is a form of art. One thing that we’ve noticed with game programmers, and programmers in general is that they just follow the “rules” someone laid out and don’t even try to think outside of those rules.
For example, using the Singleton pattern principle we’ve developed a simple way to have all your hard coded variables in one place which will ensure that you never introduce a bug in your code when using variables for built in functions.
Funny thing is that other game devs have contacted us and left comments on our YouTube channel how they introduced the TagManager principle in the game studio they work, and now the whole game studio is using that principle when coding their games.
Here’s an example of a class we call TagManager which holds all the strings we’re going to use in the project to compare objects to each other:
public static class TagManager
{
public const string ENEMY_TAG = "Enemy";
public const string PLAYER_TAG = "Player";
public const string COIN_TAG = "Coin";
}
When you want to compare objects to each other you simply use the TagManager class to pass the string parameters:
private void OnTriggerEnter2D(Collider2D other)
{
if (other.CompareTag(TagManager.PLAYER_TAG))
{
// collided with player
}
if (other.CompareTag(TagManager.ENEMY_TAG))
{
// collided with enemy
}
if (other.CompareTag(TagManager.COIN_TAG))
{
// collided with coin
}
}
What tutorials and other courses show you is this:
private void OnTriggerEnter2D(Collider2D other)
{
if (other.CompareTag("Player"))
{
// collided with player
}
if (other.CompareTag("Enemy"))
{
// collided with enemy
}
if (other.CompareTag("Coin"))
{
// collided with coin
}
}
If you compare the TagManager approach to the example above you’ll see how much more efficient the TagManager approach is because you can call it in multiple places and if by any chance you have an error, you only need to fix it inside the TagManager class.
Using the approach above you would have to go in every class where you have the appropriate tag to adapt the string to the tag change.
Even if you use something like this:
private string playerTag = "Player";
private string enemyTag = "Player";
private string coinTag = "Player";
private void OnTriggerEnter2D(Collider2D other)
{
if (other.CompareTag(playerTag))
{
// collided with player
}
if (other.CompareTag(enemyTag))
{
// collided with enemy
}
if (other.CompareTag(coinTag))
{
// collided with coin
}
}
Even though the example above is a lot better than using raw hardcoded strings, it’s still not efficient because if you’re using the tag in multiple classes, any changes to the tag you’ll have to also apply in all classes where you use that particular tag.
So be careful about the tutorials you’re following and always make sure to double check if what they’re teaching you is a good way or a bad way to code.
And most importantly don’t be afraid to come up with your own solutions like we did with the TagManager.
Always experiment and try new things and that’s how you grow as a game programmer and that’s how you’ll gain the skills to get hired in a game studio.
Where To Go From Here
The fundamentals of game development and game programming patterns are the most important skills you need to have to get hired in a game studio.
The Singleton pattern is just one of the many game programming patterns available that game studios are using to create their games.
We cover game programming patterns in-depth inside Game Dev Pro which you can check out by clicking the link below:
Game Dev Pro
You can also check out our other posts for coroutines, delegates, and events which are also important and advanced concepts to know if you plan to get hired in a game studio:
Delegates And Events In Unity
Coroutines In Unity