Singleton

From Unify Community Wiki
Jump to: navigation, search

Contents

Warning

While Singletons are great, they're also highly controversial thanks to their abused use along the years. Consider using the Toolbox instead!

Introduction

"Why Singleton?", you may ask. First, then, what's a singleton? It is a design pattern that restricts the Instantiation of a class to one object. And, if you're here, you probably want to use that basically for implementing Global Variables. For any other usage, just take this as a starting place.

The advantage of using singletons, in Unity, rather than static parameters and methods, are basically:

(1) Static classes are lazy-loaded when they are first referenced, but must have an empty static constructor (or one is generated for you). This means it's easier to mess up and break code if you're not careful and know what you're doing. As for using the Singleton Pattern, you automatically already do lots of neat stuff, such as creating them with a static initialization method and making them immutable.

(2) Singleton can implement an interface (Static cannot). This allows you to build contracts that you can use for other Singleton objects or just any other class you want to throw around. In other words, you can have a game object with other components on it for better organization!

(3) You can also inherit from base classes which you can't do with Static classes.

P.S.: Unfortunately there is no good way to remove the need of a "Instance keyword" right there, calling the singleton.

P.S.(2): This is made as MonoBehaviour because we need Coroutines. A lot of times it makes sense to leave one in a singleton, so it will persist between scenes.

Usage Example

MyClass.cs

public class MyClass : MonoBehaviour {
	void Awake () {
		Debug.Log(Manager.Instance.myGlobalVar);
	}
}

Manager.cs

public class Manager : Singleton<Manager> {
	protected Manager () {} // guarantee this will be always a singleton only - can't use the constructor!
 
	public string myGlobalVar = "whatever";
}

Implementation

Singleton.cs

using UnityEngine;
 
/// <summary>
/// Be aware this will not prevent a non singleton constructor
///   such as `T myT = new T();`
/// To prevent that, add `protected T () {}` to your singleton class.
/// 
/// As a note, this is made as MonoBehaviour because we need Coroutines.
/// </summary>
public class Singleton<T> : MonoBehaviour where T : MonoBehaviour
{
	private static T _instance;
 
	private static object _lock = new object();
 
	public static T Instance
	{
		get
		{
			if (applicationIsQuitting) {
				Debug.LogWarning("[Singleton] Instance '"+ typeof(T) +
					"' already destroyed on application quit." +
					" Won't create again - returning null.");
				return null;
			}
 
			lock(_lock)
			{
				if (_instance == null)
				{
					_instance = (T) FindObjectOfType(typeof(T));
 
					if ( FindObjectsOfType(typeof(T)).Length > 1 )
					{
						Debug.LogError("[Singleton] Something went really wrong " +
							" - there should never be more than 1 singleton!" +
							" Reopenning the scene might fix it.");
						return _instance;
					}
 
					if (_instance == null)
					{
						GameObject singleton = new GameObject();
						_instance = singleton.AddComponent<T>();
						singleton.name = "(singleton) "+ typeof(T).ToString();
 
						DontDestroyOnLoad(singleton);
 
						Debug.Log("[Singleton] An instance of " + typeof(T) + 
							" is needed in the scene, so '" + singleton +
							"' was created with DontDestroyOnLoad.");
					} else {
						Debug.Log("[Singleton] Using instance already created: " +
							_instance.gameObject.name);
					}
				}
 
				return _instance;
			}
		}
	}
 
	private static bool applicationIsQuitting = false;
	/// <summary>
	/// When Unity quits, it destroys objects in a random order.
	/// In principle, a Singleton is only destroyed when application quits.
	/// If any script calls Instance after it have been destroyed, 
	///   it will create a buggy ghost object that will stay on the Editor scene
	///   even after stopping playing the Application. Really bad!
	/// So, this was made to be sure we're not creating that buggy ghost object.
	/// </summary>
	public void OnDestroy () {
		applicationIsQuitting = true;
	}
}

Requirement

MonoBehaviourExtended.cs (from GetOrAddComponent)

static public class MethodExtensionForMonoBehaviourTransform {
	/// <summary>
	/// Gets or add a component. Usage example:
	/// BoxCollider boxCollider = transform.GetOrAddComponent<BoxCollider>();
	/// </summary>
	static public T GetOrAddComponent<T> (this Component child) where T: Component {
		T result = child.GetComponent<T>();
		if (result == null) {
			result = child.gameObject.AddComponent<T>();
		}
		return result;
	}
}
Personal tools
Namespaces

Variants
Actions
Navigation
Extras
Toolbox