Day 73 — The Singleton Design Pattern

Connor Fullarton
4 min readMay 27, 2021

--

Hey and welcome!

As promised I’ve got an article for you here that’s more focused on the singleton design pattern and its uses.

The best opportunities to use a singleton design is when you have a class that you’ll only ever have one of as we use this pattern to store the class in memory indefinitely allowing the class to be used globally. This makes the manager classes like Game Manager, UI Manager and the Audio Manager ideal for this as we’ll only ever have one of each and we’ll never be changing the logic in them during runtime as they act as a middleman between other scripts and objects.

Using the previous article as an example we had one script that used the GameManager in order to set the value of a property and then another script was then able to get that value from the GameManager.

Creating a script that can use the singleton design is pretty easy and only requires this bit of code to begin with:

private static UIManager _instance;
public static UIManager Instance
{
get
{
if (_instance == null)
{
Debug.LogError("The UIManager is null");
}
return _instance;
}
}
private void Awake()
{
_instance = this;
}

Doing all this creates an instance of the UIManager class that can be accessed globally. With that code in place you can then add in the logic that’s specific to that class. As a rule of thumb, when you’re creating your public Instance property you want to make sure that it only had get in it as you don’t want anything accidentally changing the instance of the class which get defined with _instance = this;.

Lazy Instantiation

One interesting thing we can do when we’re working with singletons is something called lazy instantiation.

At the moment when _instance equals null we’re just outputting an error into our console letting the user know that the game object is null. If we use the following code instead we can get something pretty interesting to work here:

GameObject go = new GameObject("UI Manager");
go.AddComponent<UIManager>();

What this code does is it creates its own instance of a UIManager game object if there isn’t one currently in the scene. This process here is what is known as lazy instantiation and is mostly just a fun thing you can do in singletons. It’s not really recommended to do things this way though since if you don’t have an object in the hierarchy then you won’t be able to assign any game objects to variables through it in the inspector.

MonoSingleton

Last thing to touch up on before we’re finished with singletons and that’s the concept of a MonoSingleton. The basic gist is that you have a MonoSingleton that acts as a template for the singleton design pattern which can then be inherited by other classes. This is particularly useful if you have a multitude of different manager classes that you want to turn into a singleton.

Create a C# script called MonoSingleton and add in the following code:

public abstract class MonoSingleton<T> : MonoBehaviour where T : MonoSingleton<T>
{
private static T _instance;
public static T Instance
{
get
{
if (_instance == null)
{
Debug.Log(typeof(T).ToString() + " is Null.");
}
return _instance;
}
}

private void Awake()
{
_instance = this as T;
Init();
}
public virtual void Init()
{
//optional to override
}
}

As you can see it’s following a pretty similar structure to a singleton here. The first important change is the first line where we’re defining this as an abstract class and then defining a generic data of type T. The abstract part just means that we’re setting this up to act as a template so that other classes can inherit from it.

T is going to be used to let our class know which type of class is trying to inherit from this one. To visualize that better, here’s what a script looks like when it’s inheriting from this one.

public class Player : MonoSingleton<Player>
{
public string name;
public override void Init()
{
base.Init();
Debug.Log("Player initialized");
}
public void DisplayName()
{
Debug.Log("Hello my name is: " + name);
}
}

If you check out the first line here you’ll see that Player isn’t inheriting from MonoBehaviour this time but is now inheriting from MonoSingleton and then we pass in the Player class into the brackets there which then becomes the value of T in our MonoSingleton script.

The Init() method we have here is optional and I’m just using it to log to the console whenever the separate classes have initialized during runtime, the override keyword is used to add in the logic we have here and put it into the Init() method in our MonoSingleton script and the base keyword is used to access it.

What’s important here is our DisplayName() method which I’m using in a separate regular script called OutputName:

void Start()
{
Player.Instance.name = "Dave";
Player.Instance.DisplayName();
}

As you can see here we can make a reference to our Player class globally now because it’s inheriting the Singleton Design Pattern from our MonoSingleton script!

This makes life a lot easier if we had to deal with 10 or more manager classes as this saves us on having to add all of the singleton code every time. Now we just tell it to inherit from our script that has that code instead!

Sorry for the text heavy article here, I couldn’t come up with much in the way of visuals other than the code when it came to explaining singletons but I hope you enjoy these as much as I do now!

--

--

Connor Fullarton
Connor Fullarton

Written by Connor Fullarton

Hey and welcome! My name is Connor and my goal here is to put out a daily post for a full year about my game development journey.

Responses (1)