Singletons in Unity and Many of Their Downfalls by Infallible Code

May 26, 2021

Singletons

Unity


Title:
Everything You Need to Know About Singletons in Unity

By:
Infallible Code


Youtube – Tutorial #1

Description:
Goes over the true properties of singletons in code and why they should be used sparingly in Unity game development.


Intro to Singletons

Singleton Pattern: class that is both globally accessible and can only be instantiated once

Common Example:

Audio Manager – because it is something many scripts will want access to, and you specifically want only one in your game at a time

Singleton Requirement #1: Global Access Specification

Singletons sound promising because they are an easy solution to “how do you access other scripts” or “how do you fill your class dependencies”. Using Singletons for access can hide the fact that a class depends on another, which can lead to difficult to track bugs and difficulties. This may lead to using the singleton even more than necessary because of ease of access, which creates more and more hidden dependencies.

Example #1

Non-Singleton Version

public class GameManager : MonoBehaviour
{
	[SerializeField] private Player player;
	
	privat void OnPlayerDied()
	{
		print($"Final Score: {player.Score}");
	}
}

Singleton Version

public class GameManager : MonoBehaviour
{
	privat void OnPlayerDied()
	{
		print($"Final Score: {Player.Instance.Score}");
	}
}

Because the Singleton pattern accesses a specific class directly, additions of derived classes will require more updates that may also be hard to find because of the hidden dependencies. Again, doing something similar to the Non-Singleton version of Example #1 makes it so that an additionally created derived class could fill the Player dependency with little to no edits.

Singleton Requirement #2: Restricted to a Single Instance

This is generally accomplised by creating a private constructor for the class, and then instantiating a single instance from the getter. This is already difficult in Unity as many classes you create in Unity will be MonoBehaviours, which cannot be instantiated in code with the new keyword. This is because Unity hides their constructor and allows anything to instantiate them through object.Instantiate().

So to help satisfy this, you need an extra check if another instance of the class already exists in the scene in case it was added directly in the Editor or through other object.Instantiate() calls. Once these are located, they may also require being destroyed to make sure there only exists one instance at any given time. This can also lead to strange results, as sometimes destruction prevention is used in Unity, such as with the DontDestroyOnLoad() method. Sometimes you may also get rid of and keep the “wrong” singleton if multiple somehow run into each other.

Singleton Criteria:

1) Absolutely required to exist only once
2) Must be accessible from every part of your code
3) Controls concurrent access to a resource

Summary

While I already knew that singletons should be used sparingly, having a better understanding of them fundamentally really helps put that into perspective just how sparingly they should be used. It also helps confirm that it can generally be a bad way of passing around data in Unity, and with a need that comes up so often and has so many options to be accomplished, it is good to generally remove an option from that list. It is also important to acknnowledge that in Unity specifically they are extra difficult to implement when it involves MonoBehaviours, as this again helps narrow down the process of passing around data.

via Blogger http://stevelilleyschool.blogspot.com/2021/05/singletons-in-unity-and-many-of-their.html