Even if you are new to Unity, you will probably already heard about a mysterious entity in c# and Unity called Singleton. What You will probably not know is that there are both advantages and disadvantages to singletons in Unity and how to use correctly.
We already somewhat covered singletons but for the flax engine, as in one of our first tutorials, we implemented Singletons in the Flax as to show its similarities with Unity.
Introduction
There are plenty of opinions about it, some say that singletons are useful, but should never be used.
Some says, that you should use them, but that they are very difficult to manage and can increase the complexity of your code and how to debug it as it grows in size.
But, if you are trying to finish a game, singletons can be a useful and easy way to connect scripts without going nuts with linking references between the scripts in your project folder..
And it’s not just beginners, there are many Unity developers who like to use singletons, including ourselves.
So what is a singleton?
Is it helpful or harmful? Is it easy or is difficult and complex’
And can you use it in your project? Or should you avoid it at all costs?
The answer is not blowing in the wind my friend, and it is neither yes or no, it is, It depends…
In general, many of the problems that can arise when using singletons are not actually caused by the singletons themselves, but by the way they are coded and used in your project.
Some singleton implementation methods can be problematic, but what problems you end having will depend on your project and its fundamental design.
The reason for this is that in many cases, the consequences of using singletons only become apparent when the project grows or when you decide to make some changes its structure.
Unfortunately, this means that the risks of using singletons can be unclear at the beginning of the implementation process and only start to show its face later along the game development.
In this tutorial ( and video) we will try to show you the different ways you can use singletons to simplify game development, as well as the possibles risks you could be facing, so you can make the right decision about whether or not to use singletons in your game.
What is a singleton in Unity?
In general, a singleton in Unity is a class that exists in a scene and is only globally accessible once.
The idea is that any other script can access the singleton and connect objects to important parts of the game, such as the player or other game systems.
It’s also useful for connecting unrelated objects or scripts to global systems like the audio manager.
So how does it work?
Unity singletons are regular classes that can be added to objects in the game.
Singletons differ, however, in that the class contains a public static reference to an instance of its own type.
It looks like this.
public class MyClass : MonoBehaviour
{
public static MyClass instance;
}
Static references provide global access to the singleton, so this means that any script can access the singleton by its class name without having to refer to that variable first.
MyClass.instance;
This means that other scripts in the game can easily access the public methods and variables of the Singleton class.
However, since any script can access them, it is generally a good idea to protect instance variables with properties. This simply means that they can be read by other scripts, but can only be set within their own class.
public class MyClass : MonoBehaviour
{
public static MyClass instance;
public static MyClass MyInstance
{
get´{
if(instance == null)
instance=this;
return instance;
}
}
}
A reference to a script is static and the same for every instance of the class, but the instance it points to is not static. This means that the reference points to only one instance of the script, but there may be multiple instances of the singleton in the scene.
It is therefore important to check that the static reference matches the instance of the script and that there is only one instance of the singleton.
These are the two basic requirements for Singletons.
A globally accessible class, but with only one instance.
But why create a singleton and what can a singleton do?
Why use singletons?
Singletons are very useful because they allow parts of the game to be more easily connected.
Singletons can be used, for example, to make the player’s position visible to the enemy or the player’s values visible to the user interface.
For example, like this.
float healthValue = MyClass.MyInstance.Health;
You can also call a sound effect function from anywhere in your game.
For example, like this.
MyClass.MyInstance.PlaySound(SoundFx);
If a variable or function exists on the singleton and is publicly accessible, other scripts can use it without having to set up a reference to it first by passing a reference to it.
This just makes it easier to link the different parts of the game together.
But, as said earlier, the same way it can help you linking different classes to be used throughout your game, Singletons can also cause problems.
Using a singleton to solve one problem can lead to other problems later. So you need to be aware of what can happen when you use a singleton in your project.
So what is the worst thing that could happen to you and to your game?
What could go wrong with singletons?
In general, singular projects can be difficult to manage as they grow in size.
This can make it difficult to make changes or additions to the project, which in turn makes testing difficult.
If you are planning to use a singleton, you may have heard these objections.
What exactly are the problems with using a singleton?
The biggest risk of singletons lies in what makes them useful in the first place: global access.
For example, allowing all scripts to access a Game Manager singleton can be very useful.
How is this a problem?
Let’s take an example, you want to implement an audio manager to be used consistently across your game.
Trying to manually connect all objects that want to play sound to an audio manager script is usually undesirable, complex and heavy for the coder.
Likewise, trying to find an audio manager in a scene every time a new object is created that needs an audio manager can be very bad for performance and the functions that searches for objects in the memory are very demanding on CPU and memory accesses and will penalize you heavlly in a drop of the frame rate…
In this case creating a Singletons for the audio managers could be useful
Let’s say you create an Audio Manager singleton with a Play Sound Effect function.
It looks like this.
public void PlaySound(AudioClip clip)
{
audioSource.PlayOneShot(clip).
}
Now, if you want to play a sound, just call the audio manager and pass it the sound effect.
For example like this.
public AudioClip soundEffect;
void Start()
{
MyClass.MyInstance.PlaySound(SoundFx).
}
Simple and convenient…
But are there any problems with this method?
If affirmative, how a simple piece of code can lead to problems?
The problem with using singletons like this is that you have to change the way sound effects are triggered.
For example, if you start with this method, you can add sound effect triggers as needed and call sound effect functions from different scripts.
Depending on the size of your game, you may find yourself calling scripts in dozens or hundreds of different places, all in exactly the same way.
When you add sounds to your game, you may find that some sounds are louder than others and some sounds are more repetitive.
So you might consider adjusting the volume when a sound is triggered so that clips can play at different levels, or adding more variation to the sound when it’s triggered so that it doesn’t repeat, which will mean making some modifications to the functions in the Singleton, by adding also a couple of extra parameters to manage the sound volume and other sounds characteristics..
But, the problem is that all those functions, which are simple to code, are now spread all your code across the game, so you will have to find all of them and make the changes on by one to adjust the new parameters that you have included
It would probably be difficult to do this now, since all the sound triggers in the game are connected directly to the audio manager, which just passes the audio clip reference and calls the function as is.
This means that you have to modify all the scripts that call the audio manager to change the current behavior of the manager.
This is a very important tasks, especially if your game is quite large..
How could we avoid this complexity? Welll, by using instead Event Triggers.
Modular event triggers in Unity
When using singletons, it is easy to specify certain functions early on, making it difficult to change them later.
Events, however, are by design a convenient way to change the response to an event in the game without changing the trigger.
For example, you can create a simple event system and trigger a sound effect event for any object, but the response to that event (what sound is played, how it is handled, etc.) is done in the audio manager and can easily be changed without changing the trigger changes.
This is possible with singletons, but events are more forgiving if you want to change something later because the trigger can easily be removed from the response.
It’s not easy to create a singleton and then change its behavior, but that’s not necessarily a big problem.
For example, it is difficult, but not impossible, to change the function of a sound effect after the fact.
However, it makes sense to treat all public functions and variables that are accessible to other objects through a singleton as immutable.
By including the functions you think you need early, you minimize the risk of having to change those functions later.
Audio managers may be suitable for singletons because of their global access and presence in the scene.
In short, the main benefits of using singletons are consistent with the problems of the task.
But.
Just because a singleton seems like a good solution to a problem doesn’t mean it is.
And as before, how you use the singleton now will affect how the game develops later.
This is especially true when using single players.
Singleton player objects
A common use of singletons is to communicate certain characteristics of the player to other scripts in the game, such as the player’s position and current fitness.
If you think about it, this makes a lot of sense. Almost every object in the game will respond in some way to the player’s actions, so it seems like a good idea to give these objects easy access to the player and his scripts.
But what problems arise from using Singleton specifically in the player game object?
In general, deciding whether or not to use a player object singleton is similar to deciding whether or not to use a static variable.
Static variables (essentially global variables) are generally fine if you intend to have only one variable of that type in your game.
Singletons have similar weaknesses.
Scripts that need to refer to singletons refer to them directly, so each script is essentially closely tied to the player.
This means that it can be very difficult or impossible to add a second player later, depending on how tightly integrated the player’s singleton is with the scripts that need to access it.
In general, using singletons to manage single objects such as players is riskier than using singletons to manage the game system.
Also, using singletons in this way can lead to serious problems if it is not clear what kind of game you want to make or what features you want to add in the future, even if it is not a problem now.
What is the right way to use singletons in Unity?
There is no single right way to use a singleton, but the problems that can be caused by its use usually depend on the project and what you plan to do with it.
This is because the risk of using a singleton is more related to the way it is used and less to the singleton itself.
For example, if you use a singleton for a player object and have the option of adding more players later, you’re likely to run into big problems.
Also, global access to the file storage system could cause problems if two scripts try to save the game at the same time.
Also, if you create a singleton before you know what you want to do, it may be difficult to update it if you change something later.
But.
There will be cases where it makes a lot of sense to use a singleton.
Especially when it can make the difference between releasing a game and not.
Many people advise you to avoid singletons, and there are certainly good reasons not to use them, but if you understand the risks and use them carefully, they can be a very useful tool to help you manage your game more easily.
We have reached the end of this tutorial and it’s your turn, now to work and try it by yourself, as this is the best way to learn.
Also, we would like to hear your opinion as to keep improving our set of tutorials on youtube and in our blog.
Do you use singletons in your games?
Or does it make your project more difficult?