The singleton pattern is one of the most famous patterns used widely in unity while developing games. We mostly see these being used with manager scripts like game managers, audio managers and UI managers.

So, in basic terms, a singleton is some object that gets generated only once throughout the game's lifecycle. An example would be a transition UI being made a singleton object on DontDestroy which lets its instance get accessed by any other object easily.

Singleton Use Cases

For a classic way to create a singleton, we first take a static instance of the class we want to make a singleton of. A basic singleton would look something like this:

private class UIManager : MonoBehaviour
{
	private static UIManager instance;
	
    void Awake()
    {
    	if(instance == null)
        {
        	instance = this;
        }
    }
}
A Basic Example of UIManager Singleton

So in the code above, we first create a private static instance of the UIManager class itself and then, in the Awake() function, we check if the instance is null. If the instance is null, we assign the current instance to the static instance.

So with this, any code can access it via: UIManager.instance.** . However, a better and safer approach for a singleton creation would also look like this:

private class UIManager : MonoBehaviour
{
	private static UIManager instance;
	
    public static UIManager GetInstance()
    {
    	if(instance == null)
        {
        	instance = new UIManager();
        }
        return instance;
    }
    
    private UIManager()
    {
    
    }
}
Better Singleton Approach

So, in the above code, we haven't done much change but created a getter property called GetInstance(), which provides us with an instance of the current created instance and if we haven't created an instance yet, it will create a new instance and provide it to us.

We can access this by UIManager.GetInstance().** . We have created a private constructor for this class to ensure that it isn't being instantiated from anywhere else in the codebase.

ℹ️
Singletons are criticized for developing spaghetti code as they can be accessed from anywhere in the codebase. This also makes it very hard to find out about its dependencies, making it confusing to make sure who called who, which can lead to a harder debugging process.
Make Sure Not to Use Too Many Singletons

Creating and Using Generic Singleton

Now, I suggest you minimize the use of singletons as much as possible in your projects. But for this blog, I will show you how to create a script that can handle generic singletons of any type.

public class Singleton<T> : MonoBehaviour where T : MonoBehaviour{
    private static T instance;
    public static T GetInstance(){
            if(instance==null){
                instance = FindObjectOfType<T>();
            }else if(instance != FindObjectOfType<T>()){
                Destroy(FindObjectOfType<T>());
            }
            DonotDestoryOnLoad(gameobject);
            return instance;
    }
}
A Generic MonoBehaviour Based C# Class

So, the above code is one of the most used scripts from my reusable utility toolkit while I am working on multiple projects. It solves the redundancy I would create while creating singletons for multiple classes.

Explanation

For example, if I had to create singletons of classes A, B and C, I would have to implement the logic to generate static instances in all these classes Awake()  or Start(). The class above inherits this class for any of my MonoBehaviour-based scripts and passes in the class name as the type of singleton. It will automatically generate a singleton of that class and manage it for me.

In this code, we first create a generic class Singleton that inherits from MonoBehaviour, which can be inherited by other classes. We also pass in T for the type, where T needs to be inheriting the MonoBehaviour class. Then, we create a static getter property with a Type T instance, which returns us with an instance of our singleton.

To get our singleton instance, we check if the instance is null or not. If the instance is null, we assign our instance by searching for an object with the specified type. If the instance is already present, we destroy that certain type to ensure that we don't have multiple instances of our singleton.

After that, we ensure the object is set to DontDestroy and goes through all our scenes without being destroyed.

Here's a usage example of the script:

private class UIManager : Singleton<UIManager>
{
    void Hello()
    {
    	//test
    }
}
Example Script

So, doing this will create a singleton of type UIManager throughout the game that will only have its single instance throughout the game's lifetime. It can be accessed using UIManager.GetInstance() from any code.

Conclusion

As you can see, using generic singletons can be very useful and make the code easier to edit and debug. You won't have to check through all the scripts to find their own instance. Even if you have to change your singleton, you can manage them through a single modular singleton class.

Winning at Life
Thank you for reading and learning with me. Please subscribe and leave comments if you have any regarding this article.