Implementing Singleton Pattern in Game Development

The Singleton pattern ensures a class has only one instance throughout a application's lifecycle and provides a global access point to it. This design is beneficial when multiple operations must coordinate with a shared resource, such as a file system manager where concurrent create and delete actions could conflict if separate instances were unaware of each other.

Two primary implementation approaches exist:

Lazy Initialization The instance is created only when first requested.

using UnityEngine;

public class LazySingleton : MonoBehaviour
{
    private static LazySingleton _sharedInstance;
    private static readonly object _lockHandle = new object();

    public static LazySingleton Shared
    {
        get
        {
            if (_sharedInstance == null)
            {
                lock (_lockHandle)
                {
                    if (_sharedInstance == null)
                    {
                        GameObject container = new GameObject("SingletonContainer");
                        _sharedInstance = container.AddComponent<LazySingleton>();
                    }
                }
            }
            return _sharedInstance;
        }
    }
}

The double-checked locking pattern shown addresses thread safety concerns in multi-threaded environments while minimizing performance overhead. The outer null check avoids unnecessary locking once the instance exists.

Eager Initialization The instance is created during class initialization.

using UnityEngine;

public class EagerSingleton : MonoBehaviour
{
    private static EagerSingleton _globalInstance;

    public static EagerSingleton Global
    {
        get { return _globalInstance; }
    }

    private void Awake()
    {
        if (_globalInstance == null)
        {
            _globalInstance = this;
            DontDestroyOnLoad(gameObject);
        }
        else
        {
            Destroy(gameObject);
        }
    }
}

When attaching this component to multiple GameObjects, only the first created instance persists; subsequent duplicates self-destruct during Awake().

Generic Singleton Base Class A reusable implemantation for MonoBehaviour-derived classes:

using UnityEngine;

public abstract class MonoSingleton<T> : MonoBehaviour where T : MonoBehaviour
{
    private static T _primaryInstance;

    public static T Primary
    {
        get { return _primaryInstance; }
    }

    protected virtual void OnEnable()
    {
        if (_primaryInstance == null)
        {
            _primaryInstance = this as T;
        }
        else
        {
            Destroy(this);
        }
    }
}

Derived classes inherit singleton behavior while allowing custom Awake/Start logic through method overriding.

Design Considerations

Memory vs. Performance: Lazy initialization conserves memory by deferring object creation until needed, while eager initialization eliminates runtime instantiation overhead.

Initialization Timing: Lazy initialization can cause performance spikes if instantiation occurs during critical gameplay moments. Eager initialization ensures resources are loaded during predictable startup phases.

Global State Management: Singleton accessibility can obscure dependencies and increase coupling between systems. Consider dependency injection alternatives when testability and modularity are prioriteis.

Thread Safety: The double-checked locking pattern provides thread-safe lazy initialization without constant synchronization overhead after instance creation.

Unity-Specific Constraints: MonoBehaviour-derived singletons require GameObject instantiation rather than direct constructor calls. The DontDestroyOnLoad() method preserves instances across scene transitions.

Tags: Design Patterns singleton unity C# game development

Posted on Sun, 24 May 2026 16:21:36 +0000 by Eddyon