Singleton is a design pattern that ensures there's only one instance of a class at a time. For example, there should be one GameController; it would make no sense to have more of them. The SaveManager and GUI screens would be singletons. In Unity, the general purpose and manager objects will often stand alone and be accessed by many other objects. Does it benefit us to actually make them follow the pattern, or is it enough to simply remember not to create more of them?
Using singletons brings two distinct advantages. One of them is
ease of access. Normally, we would have to call the GameController like this:
GameObject.Find("GameController").GetComponent.<GameController>().DoSomething();
If it were a singleton, the task would be much easier:
GameController.GetInstance().DoSomething();
Hence it's more convenient to use and the object is more performance effective to retrieve.
The other benefit is a
safety guard. Imagine you have a pause menu script attached to a camera. Later, you want to move it to another object, so you attach it again to that other object, but forget to remove it from the camera. Now you have it twice in the scene, giving you a not that obvious bug – two screens overlapping each other. Singleton allows only one instance, so it would have detected such a mistake immediately, negated its effects and emitted a warning.
A basic (explicitly initialised) singleton implemented in UnityScript would look like this:
class SingletonPattern
{
static function GetInstance(): SingletonPattern
{
if (! instance) {
throw new System.Exception("No instance created!");
}
else {
return instance;
}
}
static private var instance: SingletonPattern;
function SingletonPattern()
{
if (instance) {
throw new System.Exception("Only one instance allowed!");
}
else {
instance = this;
}
}
function DoSomething() {}
}
new SingletonPattern();
SingletonPattern.GetInstance().DoSomething();
As you can see, there's a certain amount of boilerplate code involved. Unless you're a typing monkey, you would soon start to wonder how to separate the pattern and use it universally. In UnityScript, you'd have to employ a hash table to store the instances of children:
public class Singleton
{
static public function GetInstance(_type: System.Type): Object
{
var instance = instances[_type];
if (! instance) {
throw new System.Exception("No instance created!");
}
else {
return instance;
}
}
static protected function SetInstance(_instance: Singleton)
{
if (instances.ContainsKey(_instance.GetType())) {
throw new System.Exception("Only one instance of the same type allowed!");
}
else {
instances.Add(_instance.GetType(), _instance);
}
}
static private var instances: Hashtable = new Hashtable();
protected function Singleton()
{
// the instance could be set directly when the object is created,
// but it would limit child's options
// SetInstance(this);
}
}
class SingletonChild extends Singleton
{
function SingletonChild()
{
SetInstance(this);
}
function DoSomething() {}
}
new SingletonChild();
(SingletonChild.GetInstance(SingletonChild) as SingletonChild).DoSomething();
Huh, look at the end-usage – really ugly code. Unfortunately, it's a real pain to have to recast constantly. But there's a better solution... Generics! Switching to C# allows us to write the following version of an explicitly initialised singleton:
abstract public class Singleton<T> where T: Singleton<T>
{
static public T Instance
{
get {
if (instance == null) {
throw new System.Exception("No instance created!");
}
return instance;
}
protected set {
if (instance != null) {
throw new System.Exception("Only one instance allowed!");
}
instance = value;
}
}
static private T instance;
protected Singleton()
{
// the instance could be set directly when the object is created,
// but it would limit child's options
// Instance = (T) this;
}
}
class SingletonChild : Singleton<SingletonChild>
{
public SingletonChild()
{
Instance = this;
}
public void DoSomething() {}
}
new SingletonChild();
SingletonChild.Instance.DoSomething();
If we don't need a special initialisation of the child, it could be even simpler. Providing the child has a public parameterless constructor, we could instantiate it lazily, when it's accessed for the first time:
public class LazySingleton<T> where T: LazySingleton<T>, new()
{
static public T Instance
{
get {
if (instance == null) {
instance = new T();
}
return instance;
}
private set {}
}
static private T instance;
protected LazySingleton()
{
if (instance != null) {
throw new System.Exception("Only one instance allowed!");
}
}
}
class LazyChild : LazySingleton<LazyChild>
{
public LazyChild() {}
public void DoSomething() {}
}
LazyChild.Instance.DoSomething();
It is all nice and well, but in Unity the MonoBehaviour is our boss. We no longer instantiate objects, so we need to adapt. There's basically no way to prevent creation of several singleton objects. The best we can do is to disable other instances of the same script. The words put into code:
abstract public class MonoSingleton<T> : MonoBehaviour where T: MonoSingleton<T>
{
static public T Instance
{
get {
if (instance == null) {
throw new System.Exception("No instance created!");
}
else {
return instance;
}
}
private set {
if (instance != null) {
throw new System.Exception("An instance already exists!");
}
else {
instance = value;
}
}
}
static private T instance;
protected virtual void Awake ()
{
try {
Instance = (T) this;
}
catch (System.Exception) {
Debug.LogError("Attempted to create another instance of '" + typeof(T).FullName + "'!");
Destroy(this);
}
}
}
class MonoChild : MonoSingleton<MonoChild>
{
public void DoSomething() {}
protected override void Awake ()
{
base.Awake();
}
}
MonoChild.Instance.DoSomething();
Alas, you would run into a wall if you wanted to extend the MonoChild for some reason. First, you'd have to cast whenever you retrieved the instance, because it's already set to the MonoChild type. Second, you could use only the MonoChild or one of its children, because the singleton instance is inherited by all the descendants and it's enforced to be unique given the same base type (i.e. the MonoChild). This can be worked around by separating the members you want to extend and the implementation of the singleton, utilizing nested classes. We will call the singleton MonoBehaviour a Wrapper and the extendible logic a Payload. Their relationship may be confusing at first, but it's enough to realise that you can derive from a Payload at will, whilst a Wrapper will only envelope the scripts you'll intend to use in the scene and will not be extended further. The following dry example demonstrates the approach:
abstract public class MonoSingletonWrapper<T> : MonoBehaviour where T: MonoSingletonPayload, new()
{
public abstract class MonoSingletonPayload
{}
static public T Instance
{
get {
if (instance == null) {
throw new System.Exception("No instance created!");
}
else {
return instance;
}
}
private set {
instance = value;
}
}
static private T instance;
protected virtual void Awake ()
{
if (instance != null) {
Debug.LogError("Attempted to create another instance of '" + GetType().FullName + "'!");
Destroy(this);
}
else {
Instance = new T();
}
}
}
abstract class MonoBase<T> : MonoSingletonWrapper<T>.MonoSingletonPayload where T: MonoBase<T>, new()
{
public MonoBase() {}
public abstract void DoSomething();
}
sealed class MonoBaseChild1 : MonoSingletonWrapper<MonoBaseChild1.MonoChild1Payload>
{
public class MonoChild1Payload : MonoBase<MonoChild1Payload>
{
public MonoChild1Payload() {}
public override void DoSomething() {}
}
}
sealed class MonoBaseChild2 : MonoSingletonWrapper<MonoBaseChild2.MonoChild2Payload>
{
public class MonoChild2Payload : MonoBase<MonoChild2Payload>
{
public MonoChild2Payload() {}
public override void DoSomething() {}
}
}
MonoBaseChild1.Instance.DoSomething();
MonoBaseChild2.Instance.DoSomething();
Here, MonoBaseChild1 and MonoBaseChild2 are actually two separate objects, which logically derive from the MonoBase, are both singletons, and can coexist at the same time.
Of course, sometimes you can end up in a situation when the generic programming is simply not an option. A compiler for a mobile platform may not support certain generic constructs, or you may be limited to the UnityScript. In such cases, beside “baking” the singleton pattern introduced at the beginning, you can also turn to a static class initialised by a MonoBehaviour, or rather a MonoBehaviour that behaves like a static class:
public class StaticMono extends MonoBehaviour
{
function Awake()
{
var instances = FindObjectsOfType(GetType());
if (instances && instances.Length > 1) {
// removing the component here would remove all instances of the script,
// which could lead to a potential instability
throw new System.Exception("Created several instances of '" + GetType().FullName + "'!");
}
}
}
class StaticMonoChild extends StaticMono
{
static function DoSomething()
{
something += 5;
print(exposedVarCopy);
}
static private var something: int;
static private var exposedVarCopy: int;
var exposedVar: int;
function Awake()
{
super.Awake();
something = 10;
exposedVarCopy = exposedVar;
}
}
// if it's called before StaticMonoChild.Awake(), the behaviour is undefined
StaticMonoChild.DoSomething();
It does not provide an instance (though it may be looked up if need be), but in most cases it's not needed anyway. The only major drawback is invalid accessing of static members before they're initialised or after they're deinitialised. You can either check in each method whether the initialisation has run, which is annoying, or tweak the script's execution order and hope you were careful enough not to call anything too early, which is unsafe. I think the most viable option is to put all data fields in a private nested class and then create a private static instance of it in Awake(). This way a premature access will trigger a NullReferenceException, which is better than nothing. Variables exposed to the editor can then be copied from the outer shell to the internal state when it's created. Like this:
class StaticMonoSafer extends StaticMono
{
private class InnerState
{
function InnerState(_outer: StaticMonoSafer)
{
something = 10;
exposedVarCopy = _outer.exposedVar;
}
var something: int;
var exposedVarCopy: int;
}
static function DoSomething()
{
state.something += 5;
print(state.exposedVarCopy);
}
static private var state: InnerState;
var exposedVar: int;
function Awake()
{
super.Awake();
state = new InnerState(this);
}
}
// if it's called before StaticMonoSafer.Awake(), NullReferenceException is thrown
StaticMonoSafer.DoSomething();
All in all, this should be a provisional solution when no better options are available.
Disclosure: I intentionally omitted releasing static references to make the examples more compact. A final implementation should take care to free all the references as soon as they are not needed, because they block the memory occupied by singleton objects even after the objects are seemingly released. (Well, at least in some cases.)