Singleton components

As mentioned previously, static classes have difficulty interfacing with Unity-related functionality and cannot directly make use of MonoBehaviour features, such as event callbacks, coroutines, hierarchical design, and Prefabs. Also, since there's no object to select in the Inspector window, we lose the ability to inspect the data of a static class at runtime through the Inspector window, which can make debugging difficult. These are features that we may wish to make use of in our global classes.

A common solution to this problem is to implement a component that acts as a singleton—it provides static methods to grant global access, and only one instance of MonoBehaviour is ever allowed to exist at any given time.

The following is the definition for a SingletonComponent class:

using UnityEngine;

public class SingletonComponent<T> : MonoBehaviour where T : SingletonComponent<T> {
private static T __Instance;

protected static SingletonComponent<T> _Instance {
get {
if(!__Instance) {
T[] managers = GameObject.FindObjectsOfType(typeof(T)) as T[];
if (managers != null) {
if (managers.Length == 1) {
__Instance = managers[0];
return __Instance;
} else if (managers.Length > 1) {
Debug.LogError("You have more than one " +
typeof(T).Name +
" in the Scene. You only need " +
"one - it's a singleton!");
for(int i = 0; i < managers.Length; ++i) {
T manager = managers[i];
Destroy(manager.gameObject);
}
}
}
GameObject go = new GameObject(typeof(T).Name, typeof(T));
__Instance = go.GetComponent<T>();
DontDestroyOnLoad(__Instance.gameObject);
}
return __Instance;
}
set {
__Instance = value as T;
}
}
}

This class works by creating GameObject containing a component of itself the first time it is accessed. Since we wish this to be a global and persistent object, we will need to call DontDestroyOnLoad() shortly after GameObject is created. This is a special function that tells Unity that we want the object to persist between scenes for as long as the application is running. From that point onward, when a new scene is loaded, the object will not be destroyed and will retain all of its data.

This class definition assumes two things. Firstly, because it is using generics to define its behavior, we must derive it to create a concrete class. Secondly, a method must be defined to assign the _Instance property (which, in turn, sets the private __Instance field) and cast it to/from the correct class type.

For example, the following is the minimum amount of code that is needed to successfully generate a new SingletonComponent derived class called EnemyManagerSingletonComponent:

public class EnemyManagerSingletonComponent : SingletonComponent< EnemyManagerSingletonComponent > {
public static EnemyManagerSingletonComponent Instance {
get { return ((EnemyManagerSingletonComponent)_Instance); }
set { _Instance = value; }
}

public void CreateEnemy(GameObject prefab) {
// same as StaticEnemyManager
}

public void KillAll() {
// same as StaticEnemyManager
}
}

This class can be used at runtime by having any other object access the Instance property at anytime. If the component does not already exist in our scene, then the SingletonComponent base class will instantiate its own GameObject and attach an instance of the derived class to it as a component. From that point forward, access through the Instance property will reference the component that was created, and only one instance of that component will exist at a time.

Note that this means we don't need to implement static methods in a singleton component class definition. For example, we could simply call EnemyManagerSingletonComponent.Instance.KillAll() to access the KillAll() method. 

Note that it is possible to place an instance of SingletonComponent in a Hierarchy window since it derives from MonoBehaviour. Although, be warned, the DontDestroyOnLoad() method would never be called, which would prevent the singleton component's GameObject from persisting when the next scene is loaded. We will perhaps need to call DontDestroyOnLoad() in the Awake() callback of the derived class to make this work, unless, of course, we actually want destructible singletons. Sometimes, it makes sense to allow such singletons to be destroyed between scenes so that it can start fresh each time; it all depends on our particular use cases.

In either case, the shutdown of a singleton component can be a little convoluted because of how Unity tears down scenes. An object's OnDestroy() callback is called whenever it is destroyed during runtime. The same method is called during application shutdown, whereby every component on each GameObject has its OnDestroy() callback called by Unity. The same activities take place when we end Playmode in the Editor, hence returning to edit mode. However, the destruction of objects occurs in a random order, and we cannot assume that the SingletonComponent object will be the last object destroyed.

Consequently, if any object attempts to do anything with the singleton component in the middle of their OnDestroy() callback, then they may be calling the SingletonComponent object's Instance property. However, if the singleton component has already been destroyed before this moment, then a new instance of SingletonComponent will be created in the middle of the application shutdown. This can corrupt our scene files, as instances of our singleton components will be left behind in the scene. If this happens, then Unity will throw the following error message:

"Some objects were not cleaned up when closing the scene. (Did you spawn new GameObjects from OnDestroy?)"

The obvious workaround is to simply never call into a SingletonComponent object during any MonoBehaviour component's OnDestroy() callback. However, there are some legitimate reasons we may wish to do so: most notable is that singletons are often designed to make use of the observer design pattern. This design pattern allows other objects to register/deregister with them for certain tasks, similar to how Unity latches onto callback methods, such as Start() and Update(), but in a more strict fashion.

With the observer design pattern, objects will typically register with the system when they are created, will make use of it during runtime, and then either deregister from it during runtime when they are finished using it or deregister during their own shutdown for the sake of cleanup. We will see an example of this design pattern in the upcoming section, A global messaging system, but if we imagine MonoBehaviour making use of such a system, then the most convenient place to perform shutdown deregistration would be within an OnDestroy() callback. Consequently, such objects are likely to run into the aforementioned problem, where a new GameObject instance for SingletonComponent is accidentally created during application shutdown.

To solve this problem, we will need to make three changes. Firstly, we need to add an additional flag to SingletonComponent, which keeps track of its active state and disables it at the appropriate times. This includes the singleton's own destruction, as well as application shutdown (OnApplicationQuit() is another useful Unity callback for MonoBehaviours, which is called during this time):

private bool _alive = true;
void OnDestroy() { _alive = false; }
void OnApplicationQuit() { _alive = false; }

Secondly, we should implement a way for external objects to verify the singleton's current state:

public static bool IsAlive {
get {
if (__Instance == null)
return false;
return __Instance._alive;
}
}

Finally, any object that attempts to call into the singleton during its own OnDestroy() method must first verify the state using the IsAlive property before calling Instance, as follows:

public class SomeComponent : MonoBehaviour {
void OnDestroy() {
if (MySingletonComponent.IsAlive) {
MySingletonComponent.Instance.SomeMethod();
}
}
}

This will ensure that nobody attempts to access the singleton instance during destruction. If we don't follow this rule, then we will run into problems where instances of our singleton object will be left behind in the scene after returning to Edit Mode.

The irony of the SingletonComponent approach is that we are using a Find() call to determine whether or not one of these SingletonComponent objects already exists in the scene before we attempt to assign the __Instance reference variable. Fortunately, this will only happen when the singleton component is first accessed, which is usually not a problem if there aren't too many GameObjects in the scene, but it's possible that the initialization of the singleton component may not necessarily occur during scene initialization and can, therefore, cost us a performance spike at a bad time during gameplay when an instance is first acquired and Find() gets called. The workaround for this is to have some God class confirm that the important singletons are instantiated during scene initialization by simply accessing the Instance property on each one.

Another downside to this approach is that if we later decide that we want more than one of these singletons executing at once or we wish to separate out its behavior to be more modular, then there would be a lot of code that needs to change.

The final approach we will explore will attempt to solve many of the problems revealed by the previous solutions and provide a way to gain all of their benefits, by combining ease of implementation, ease of extension, and strict usage that also reduces the likelihood of human error during configuration.