Singleton in Unity – Love or hate?

Those who know me, know that I have a rough relationship with the Singleton Design Pattern. Mostly I don’t like using it very often or even at all. This approach has a lot of benefits, starting with cleaner code architecture, less coupling, and in general fewer problems. 🥰

When to use it?

So when you would implement singleton? When you would like to have only one instance of a specific object. Example of what could be a custom resource manager where you store or load files that you downloaded from the web or any other data storage.

And still, with only storing, saving, loading data you might end up with the messy code. So be causes with it! Don’t overuse this design pattern! 😤

But for those that are brave enough to tame Singleton, fasten your seatbelt!

Implementation

Implementing this design pattern in Unity doesn’t require from us using any type of locks or other multithread safety measures. But you will need to answer the question of how you are going to use a singleton object or if you are going to jump through multiple scenes in your game.

The easiest version will be a singleton that will exist on a single scene. In addition, configurable as any other game object on your scene.

using UnityEngine;

/// <summary>
/// One of the simplest implementation of Singleton Design Pattern.
/// </summary>
public abstract class Singleton<T> : MonoBehaviour where T : MonoBehaviour
{
    // Reference to our singular instance.
    private static T instance;
    public static T Instance => instance;

    /// <summary>
    /// Unity method called just after object creation - like constructor.
    /// </summary>
    protected virtual void Awake()
    {
        // If we don't have reference to instance than this object will take control
        if (instance == null)
        {
            instance = this as T;
        }
        else if (instance != this) // Else this is other instance and we should destroy it!
        {
            Destroy(this);
        }
    }

    /// <summary>
    /// Unity method called before object destruction.
    /// </summary>
    protected virtual void OnDestroy()
    {
        if (instance != this) // Skip if instance is other than this object.
        {
            return;
        }

        instance = null;
    }
}

This singleton will be initialized with your scene as you can see it in the code.

The second implementation will be the lazy approach, so we don’t create singleton until we need it.

using UnityEngine;

/// <summary>
/// This is lazy implementation of Singleton Design Pattern.
/// Instance is created when someone call Instance property.
/// </summary>
public abstract class LazySingleton<T> : MonoBehaviour where T : MonoBehaviour
{
    // Flag used to mark singleton destruction.
    private static bool singletonDestroyed = false;

    // Reference to our singular instance.
    private static T instance;
    public static T Instance
    {
        get
        {
            if (singletonDestroyed) // If game is closing and we already destroyed instance, we shouldn't create new one!
            {
                Debug.LogWarningFormat("[Singleton] Singleton was already destroyed by quiting game. Returning null");
                return null;
            }

            if (!instance) // If there is no object already, we should create new one.
            {
                // Creating new game object with singleton component.
                // We don't need to assign reference here as Awake() will be called immediately after coponent is added.
                new GameObject(typeof(T).ToString()).AddComponent<T>();
            }

            return instance;
        }
    }

    /// <summary>
    /// Unity method called just after object creation - like constructor.
    /// </summary>
    protected virtual void Awake()
    {
        // If we don't have reference to instance and we didn't destroy instance yet than this object will take control
        if (instance == null && !singletonDestroyed)
        {
            instance = this as T;
        }
        else if (instance != this) // Else this is other instance and we should destroy it!
        {
            Destroy(this);
        }
    }

    /// <summary>
    /// Unity method called before object destruction.
    /// </summary>
    protected virtual void OnDestroy()
    {
        if (instance != this) // Skip if instance is other than this object.
        {
            return;
        }

        singletonDestroyed = true;
        instance = null;
    }
}

You can see here that we are only going to create our singleton when we call Instance property.

In both of the above implementations, singleton will be destroyed after you change the scene. But what if you don’t want to destroy a singleton that way? What if you’re want to have it as long as a game is running?!
For that we just need one little change to our lazy singleton.

using UnityEngine;

/// <summary>
/// This is lazy implementation of Singleton Design Pattern.
/// Instance is created when someone call Instance property.
/// Additionally, with this implementation you have same instance when moving to different scene.
/// </summary>
public class PersistentLazySingleton<T> : MonoBehaviour where T : MonoBehaviour
{
    // Flag used to mark singleton destruction.
    private static bool singletonDestroyed = false;

    // Reference to our singular instance.
    private static T instance;
    public static T Instance
    {
        get
        {
            if (singletonDestroyed) // If game is closing and we already destroyed instance, we shouldn't create new one!
            {
                Debug.LogWarningFormat("[Singleton] Singleton was already destroyed by quiting game. Returning null");
                return null;
            }

            if (!instance) // If there is no object already, we should create new one.
            {
                // Creating new game object with singleton component.
                // We don't need to assign reference here as Awake() will be called immediately after coponent is added.
                new GameObject(typeof(T).ToString()).AddComponent<T>();

                // And now we are making sure that object won't be destroy when we will move to other scene.
                DontDestroyOnLoad(instance);
            }

            return instance;
        }
    }

    /// <summary>
    /// Unity method called just after object creation - like constructor.
    /// </summary>
    protected virtual void Awake()
    {
        // If we don't have reference to instance and we didn't destroy instance yet than this object will take control
        if (instance == null && !singletonDestroyed)
        {
            instance = this as T;
        }
        else if (instance != this) // Else this is other instance and we should destroy it!
        {
            Destroy(this);
        }
    }

    /// <summary>
    /// Unity method called before object destruction.
    /// </summary>
    protected virtual void OnDestroy()
    {
        if (instance != this) // Skip if instance is other than this object.
        {
            return;
        }

        singletonDestroyed = true;
        instance = null;
    }
}

And this is the final form of Singleton Design Pattern for Unity. As mention earlier, we don’t really need to implement thread safety here as the code is executed on a single thread.

If you read my previous posts about the Factory Design Pattern or Object Pooling, you will already know that I prefer implementing design patterns as generic as possible for its speed of making new child scripts that use design pattern implementation.

But this time don’t get fooled by this ease! Save yourself a pain of digging bugs hidden or related to calling a single object in your project! 😉

Example

So what are good examples of implementation such a design pattern?

THIS IS EXACTLY THE QUESTION YOU SHOULD ASK! 🔥

I already mention such examples – all kind of a data storages. Starting on saving and loading player progress or his status through a game to loading localization files and building localization system around it!

This time I’ll barely touch the subject of storing data as I have the whole post planned around it! So stay tuned for that! 🤓

Let’s back to implementing simple data storage! We are going to use our last singleton implementation here, and we are going to override it with our storage class.

using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// Data storage that uses one of the singleton implementations.
/// Object is used to store and get data through game life.
/// </summary>
public class DataStorage : PersistentLazySingleton<DataStorage>
{
    // References to all stored data
    private Dictionary<string, object> storage = new Dictionary<string, object>();

    /// <summary>
    /// Method used to save data in storage.
    /// </summary>
    /// <param name="key">Key.</param>
    /// <param name="data">Data.</param>
    public void SaveData(string key, object data)
    {
        if (storage.ContainsKey(key)) // If something under key exist already we are printing warning.
        {
            Debug.LogWarningFormat("[{0}] Overriding value in: {1}.", typeof(DataStorage), key);
        }

        storage[key] = data;
    }

    /// <summary>
    /// Method used to verify if storage has data under provided key.
    /// </summary>
    /// <returns><c>true</c>, if storage contains data, <c>false</c> otherwise.</returns>
    /// <param name="key">Key.</param>
    /// <typeparam name="T">Expected data type.</typeparam>
    public bool HasData<T>(string key)
    {
        if (!storage.ContainsKey(key)) // If storage doesn't has key then return false.
        {
            return false;
        }

        return ((T)storage[key]) != null; // If storage has data but we need to verify type.
    }

    /// <summary>
    /// Method used to get data from storage.
    /// </summary>
    /// <returns>Data.</returns>
    /// <param name="key">Key.</param>
    /// <typeparam name="T">Expected data type.</typeparam>
    public T GetData<T>(string key)
    {
        if (!storage.ContainsKey(key)) // Check is storage has data under provided key.
        {
            Debug.LogWarningFormat("[{0}] No value under key: {1}. Returning default", typeof(DataStorage), key);
            return default(T); // Return default value for type.
        }

        return (T)storage[key];
    }

    /// <summary>
    /// Method used to remove data from storage.
    /// </summary>
    /// <param name="key">Key.</param>
    public void RemoveData(string key)
    {
        if (storage.ContainsKey(key)) // If data under provided key exist, we are removing it.
        {
            storage.Remove(key);
        }
    }
}

And with that, now we can access our storage from anywhere in our code, which you shouldn’t be doing! 😤
Remember, respect your code architect guidelines before you start using it everywhere!

So accessing our newly created data storage is as easy as it can get.
First, we are going to save some random number, and after a few seconds, we are going to print it in the console.

using UnityEngine;

/// <summary>
/// This class shows how you can call singleton without having to worry about providing reference.
/// </summary>
public class UsageExample : MonoBehaviour
{
    // Key to store data.
    private const string SAVE_KEY = "Secret Info";
    // Timer used to print less logs.
    private float timer;

    /// <summary>
    /// Unity method called before first frame.
    /// </summary>
    private void Start()
    {
        GenerateNewValue();
    }

    /// <summary>
    /// Unity method called every frame.
    /// </summary>
    private void Update()
    {
        timer -= Time.deltaTime;
        if (timer < 0)
        {
            PrintSecretValue();
        }
    }

    /// <summary>
    /// Method used to generate new data and save it in data storage.
    /// Also sets timer with new value.
    /// </summary>
    private void GenerateNewValue()
    {
        DataStorage.Instance.SaveData(SAVE_KEY, Random.Range(1, 100));
        timer = Random.Range(0.5f, 3f);
    }


    /// <summary>
    /// Method used to print saved value in data storage.
    /// After print, method calls GenerateNewValue().
    /// </summary>
    private void PrintSecretValue()
    {
        var value = DataStorage.Instance.GetData<int>(SAVE_KEY);

        Debug.LogFormat("Secret value is: {0}", value);

        GenerateNewValue();
    }
}

Because of that, this time results won’t be as amazing as in previous posts, but the code is more important here. 😜

So now it’s your time to run as far away as you can from this design pattern! 😄

As always the example is available through public repository here.

I hope you like it! And stay mindful about using singletons! 😇

4.5 2 votes
Article Rating
Subscribe
Notify of
guest
2 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
trackback

[…] This example of a singleton patterns is copied from this blog https://www.patrykgalach.com/2019/04/04/singleton-in-unity-love-or-hate/ […]

trackback

[…] Este ejemplo de un patrón singleton se ha copiado de este blog https://www.patrykgalach.com/2019/04/04/singleton-in-unity-love-or-hate/ […]

2
0
Would love your thoughts, please comment.x
()
x