-
Notifications
You must be signed in to change notification settings - Fork 6
SingletonBehavior
Base class for MonoBehaviours that needs to implement the singleton pattern.
[InitializeOnLoad]
[ExecuteInEditMode]
public abstract class SingletonBehaviour<T> :
MonoBehaviour
where T :
SingletonBehaviour<T>
The following example provides an implementation for a single, global scoreboard.
It offers a static accessor for the score and stores the actual score in an instance field.
This has the benefit, that the class works with the Unity serialization framework, you can edit the score in the inspector.
At the same time, the score is globally accessible and doesn't require a reference to the Scores instance.
public class ScoreManager : SingletonBehaviour<ScoreManager>
{
// Only use instance fields to store data.
[SerializeField]
private int score;
// Static accessor for the score
public static int Score
{
get
{
// Don't access the instance without ensuring access first
if (Scores.CanAccessInstance)
{
return Instance.score;
}
return -1;
}
set
{
if (Scores.CanAccessInstance)
{
Instance.score = value;
}
}
}
protected override void OnEnable()
{
// Always call the base implementation to ensure correct behavior
base.OnEnable();
// Make sure the score is not negative on game start.
if (score < 0)
{
score = 0;
}
}
}
Classes descending from SingletonBehaviour have a static Instance property providing a reference to the only existing instance of that class.
This property is guaranteed to return a valid instance, unless CanAccessInstance returns false.
If no instance exists, a new instance will be created.
Internal mechanisms ensure, that at any given time, at most one instance of any deriving class exists.
Manually creating a new instance is supported, however, if another instance already exists, the new instance will be destroyed immediatelly.
In case loading multiple scenes would cause multiple instances to exist, it is undefined which instance will get destroyed.
It is advised to use a global scene for singleton instances in this case.
When the game is shutting down, the game objects and components get unloaded in some undefined order.
Creating new instances is prohibited at that point and leads to memory leaks and incomplete cleanup.
When the game is shutting down, the existence of an singleton instance is undefined and accessing the Instance property can cause an InvalidOperationException.
To prevent that, you should always check the CanAccessInstance property before accessing the Instance property.
While many singletons can be represented using a static class, there are a few key difference which makes a singleton a better choice over static classes.
-
Serialization and inspector
Unity doesn't serialize static classes.
If you need your data to be stored or accessible in the inspector, you should use the singleton pattern. -
Editor
When starting and stopping games in the editor, Unity sometimes does not reload the assemblies.
In this case, static fields and properties will retain their old state, leading to undefined behavior.
However, MonoBehaviour instances will be properly reloaded, ensuring that SingletonBehaviours have a clean state. -
Scenes
It is possible to have individual singleton instance in individual scenes.
This is useful, if you need to load a specific state for some scenes.
Unless you load multiple scenes at once, the SingletonBehaviour works out of the box with this setup.
On the other hand, a static class doesn't know about scenes and needs to be initialized manually.
If you have determined that a class needs to implement the singleton pattern, there are a few points to consider.
- SingletonBehaviour is generic and requires you to provide the type of the deriving class as parameter. (See Example)
- Only use instance fields and properties for storage, use static properties only to access instance fields. (See Static vs Singleton)
- You can override OnEnable, OnDestroy and OnApplicationQuit, however you must also forward the call to the base implementation using
base.[MethodName]
. (See Example) - It is good practice to provide static properties and methods to access common functionality, as it removes the need to use the Instance property outside the class. (See Example)
-
CanAccessInstance
Gets a value indicating whether accessing the singleton instance is allowed or not. -
Instance
Gets the single class instance, if no such instance exists, a new instance will be created.
-
[protected virtual] OnApplicationQuit() : void
Called when Unity begins to shut down. -
[protected virtual] OnDestroy() : void
Called when the instance is being destroyed. -
[protected virtual] OnEnable() : void
Called when the instance is being enabled..
The SingletonBehaviour inherits from MonoBehaviour.
Like all MonoBehaviours, the SingletonBehaviour should only be accessed by the main thread.