Skip to main content

Singleton Pattern

Written by: Akram Taghavi-Burris | © Copyright 2025

Status: In Development

The Singleton pattern is a widely used design pattern for ensuring that a class has only one instance throughout the lifetime of the application. This pattern provides a way to control access to shared, global resources without the need for multiple instances, which can help avoid unexpected behavior or performance issues due to redundant processing.

Using Singletons

In game development a common use case for a singleton is in the creation of a single instance of a manager or controller class (e.g. AudioManager, GameManager, or PlayerControler) where creating multiple instances could lead to conflicts or inefficiencies. By implementing the Singleton pattern, you can ensure that no matter how many times the object is requested or instantiated, only one instance will exist. Use of Singleton Pattern in Unity

Example of how the Singleton pattern can be used inside a class:

GameManager.cs
public class GameManager : MonoBehaviour
{
//Creates the global access to the instance
public static GameManager Instance { get; private set; }

void Awake()
{
// If no GameManager exists, assign this instance and mark it as persistent across scenes
if (Instance == null)
{
Instance = this; //make this the game manager
DontDestroyOnLoad(gameObject); // Prevents the instance from being destroyed when changing scenes
}
// Else if a GameManager already exists and it's not this one, destroy this instance to maintain the Singleton
else
{
Destroy(gameObject); //if there is a game manager destroy this object

}//end if (Instance == null)

}//end Awake()

//other game manager behaviors goes here
}
There Can Only Be One

Singletons in programming can be likened to the Highlanders from the 1980s cult classic film and lesser-known TV series, Highlander. The story revolves around immortal beings who exist among humans, each with their own unique powers and abilities. Central to the narrative is the belief that "There can be only one." This implies that these immortals must engage in fierce battles, culminating in the beheading of their rivals until only one remains standing. The last immortal standing is said to receive The Prize, a mysterious reward that grants them ultimate power.

In a similar vein, Singletons represent the "immortal" objects in your game. Just as the Highlanders cannot coexist peacefully, your Singleton class ensures that only one instance of the class survives throughout the entire game. Any additional instances created are destroyed, just as an immortal would face beheading if they crossed paths with another.

So, when you're implementing a Singleton, think of it as a programming immortal, living on through various scenes and game states, while ensuring that any additional instances meet their inevitable end! After all, in the realm of coding, there can be only one.

Singleton Base Class

Since there are many instances that require Singleton behavior, rewriting the behavior can be time consuming, while copying and pasting the Singleton code can lead to redundancy and potential bugs. To increase efficiency a base Singleton class that any class can inherit from. This simplifies implementation and makes the codebase more maintainable and scalable.

By using a base class for the Singleton pattern, you:

  • Reduce Code Duplication: Avoid rewriting the Singleton logic for each class.
  • Improve Maintainability: If you need to change the Singleton logic, you only modify the base class.
  • Enhance Readability: By centralizing the Singleton logic, the inherited classes remain clean and focused on their specific responsibilities.

Example Singleton base class with detailed comments:

Singleton<T>
// Generic Singleton base class that any MonoBehaviour can inherit from
public class Singleton<T> : MonoBehaviour where T : MonoBehaviour
{
// Static instance that holds the reference to the Singleton
private static T _instance;

// Public property to access the Singleton instance
public static T Instance
{
get
{
return _instance;
}
}

// Unity's Awake method, called when the script instance is being loaded
private void Awake()
{
// Check for singleton duplication
CheckForSingleton();

}//end Awake()

// Ensures that only one instance of the Singleton exists
void CheckForSingleton()
{
// If no instance exists, assign this instance and mark it as persistent across scenes
if (_instance == null)
{
_instance = this as T;
DontDestroyOnLoad(gameObject); // Prevents the instance from being destroyed when changing scenes
}
// If an instance already exists and it's not this one, destroy the new instance to maintain the Singleton
else if (_instance != this)
{
Destroy(gameObject); // Ensure that only the original Singleton instance persists
}//end if (_instance == null)

// Log the current instance for debugging purposes
Debug.Log(_instance);

}//end CheckForSingleton()
}

How It Works:

  • Generic Singleton Class: The Singleton<T> class is a generic base class, where T is a placeholder for the specific type that will inherit from it (e.g., GameManager, AudioManager). In C#, generics allow you to define classes or methods with a placeholder for a data type, which is substituted at runtime. This enables you to reuse the same Singleton logic across multiple classes, ensuring that each class has a single instance without duplicating the same code. By using Singleton<T>, you centralize the Singleton functionality, making it easier to maintain and apply to various MonoBehaviour types.

  • Static Instance: The _instance field is static, ensuring that it holds the single instance across all scenes. The Instance property provides global access to this instance.

  • Awake Method: In Unity, the Awake method is called when the script is loaded. Here, it's used to trigger the CheckForSingleton method, which ensures only one instance of the class exists.

  • CheckForSingleton Method: This method checks whether the _instance is already set.

    • If it's not, it assigns the current object as the Singleton and marks it as DontDestroyOnLoad, ensuring it persists across scene changes.
    • If an instance already exists and it's not the current object, the new instance is destroyed to prevent multiple instances.
    • DontDestroyOnLoad: This ensures the Singleton instance is not destroyed when the scene changes, keeping the reference intact for the entire game session.
  • Debug Logging: The Debug.Log(_instance) statement logs the current instance, which can be useful for verifying the Singleton behavior during development.

Else Check

In the example for implementing a singleton pattern to the GameManager class a simple if/else statement was used, in which if (Instance == null) then this object became the instance, else if an instance already exists, the game object is deleted. This approach is simple but there are edge cases (like reloading scenes, object duplication, or special conditions during initialization) where the same instance might be re-registered as the Singleton. The Singleton<T> base class, makes use of an else if (_instance != this), which doesn't just check if the instance is not is null, but checks specifically if this object is the not the instance. This method is more robust and better suited for scenarios where Unity might re-instantiate or reload objects unexpectedly.

Handling Persistence

While Singletons are often persistent, some use cases, such as an object pool (a collection of reusable objects for performance optimization), which may require a single global reference in which implementing a singleton patter would make sense. However, the pool may only exist within a single scene, making them non-persistent across the game.

In order to control whether a Singleton instance persists across scenes we introduce a boolean flag, isPersistent. When checked through the CheckForPresistance() method, we can set the Singleton instance to DontDestroyOnLoad() which ensures that the game object persists between scenes.

Parented Objects and Persistence

The DontDestroyOnLoad method only works if the object is a root object in the scene (i.e., it has no parent). If your Singleton instance has a parent object, it will not persist as expected. To ensure persistence in such cases, you must detach the object from its parent before calling DontDestroyOnLoad.

The highlights in the following code illustrates how we can modify the Singleton class to handle persistence with greater flexibility.

Updated Singleton<T>
   // Generic Singleton base class that any MonoBehaviour can inherit from
public class Singleton<T> : MonoBehaviour where T : MonoBehaviour
{
// Static instance that holds the reference to the Singleton
private static T _instance;

// Public property to access the Singleton instance
public static T Instance
{
get
{
return _instance;
}
}

//Variable for persistence
private bool _isPersistant = true;


// Unity's Awake method, called when the script instance is being loaded
private void Awake()
{
// Check for singleton duplication
CheckForSingleton();

}//end Awake()

// Ensures that only one instance of the Singleton exists
void CheckForSingleton()
{
// If no instance exists, assign this instance
if (_instance == null)
{
_instance = this as T;

//Check if Singleton is persistent
CheckForPresistance();
}

// If an instance already exists and it's not this one
else if (_instance != this)
{
//Destroy the new instance to maintain the Singleton
Destroy(_instance);

}//end if (_instance == null)

// Log the current instance for debugging purposes
Debug.Log(_instance);

}//end CheckForSingleton()

void CheckForPersitance()
{
// Check if persistence is required
if (isPersistent)
{
// Detach from parent if there is one
if (transform.parent != null)
{
transform.SetParent(null);
}

// Mark this GameObject as not to be destroyed
DontDestroyOnLoad(gameObject);

}//end CheckForPersitance()

}//end Singelton



How It Works:

  • isPersistent: This boolean flag determines whether the object should persist across scenes.
  • if (transform.parent != null): This checks if the object has a parent (i.e., it's a nested object). If so, the object must detach from its parent to ensure it remains persistent using the DontDestroyOnLoad() method.
  • Destroys Instances: The method ensures that only one instance of the object exists. If another instance is detected, the new one is destroyed to maintain the Singleton pattern.

This approach gives users the flexibility to choose whether their Singleton should persist across scenes and addresses potential issues with objects that are not root objects.

Use Namesapces

Since the Singleton base class is a general-purpose class that can be inherited by various other classes, it is best practice to place it in its own namespace. This helps prevent naming conflicts with other classes and keeps your singleton structure organized and easy to manage.

For example:

namespace MyCompany.General 
{
// Generic Singleton base class that any MonoBehaviour can inherit from
public class Singleton<T> : MonoBehaviour where T : MonoBehaviour
{
//Class logic here

}//end Singleton<T>

}//end namspae

In this example, MyCompany is the main namespace, while General serves as a sub-category to help organize your library structure. This approach ensures better maintainability and scalability in larger projects.

For more details on namespace organization, see Namespace Standards.

Common Use Cases

The Singleton pattern is particularly useful in situations where a class is responsible for managing global game states or systems that should only have a single instance throughout the game's lifecycle. By ensuring that only one instance exists, the Singleton pattern helps to centralize management and prevent conflicts or inefficiencies caused by multiple objects of the same type. Below are some common scenarios where you can use the Singleton pattern effectively:

  • GameManager: The GameManager is typically responsible for managing the overall game state (e.g., win conditions, player progress, etc.). Since the game state is central to the entire game, a Singleton ensures that there is only one instance of the GameManager throughout the game session.
public class GameManager : Singleton<GameManager> {

// GameManager logic here

}//end GameManager
  • AudioManager: An AudioManager controls all audio sources and sound effects within a game. If there were multiple instances of the AudioManager, they could conflict with each other, making it difficult to manage audio playback consistently. A Singleton ensures that there is only one instance managing the audio system.
public class AudioManager : Singleton<AudioManager> {

// Audio management logic here

}//end AudioManager
  • SceneManager: A SceneManager manages scene transitions and loading. By using a Singleton, scene management becomes centralized and easily accessible throughout the game, ensuring smooth and consistent scene handling across different game states.
public class SceneManager : Singleton<SceneManager> {

// Scene management logic here

}//end SceneManager
  • Object Pooling: When implementing an object pool to manage reusable game objects, having a Singleton instance of the pool helps to efficiently manage objects without the need to create new instances every time. This improves performance by reusing objects instead of constantly instantiating and destroying them.
public class ObjectPool : Singleton<ObjectPool> {

// Object pooling logic here

}//end ObjectPool