UnitySingleton.cs 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. /*************************************************************
  2. * Unity Singleton Class (c) by ClockStone 2011 *
  3. *
  4. * Allows to use a script components like a singleton
  5. *
  6. * Usage:
  7. *
  8. * Derive your script class MyScriptClass from
  9. * SingletonMonoBehaviour<MyScriptClass>
  10. *
  11. * Access the script component using the static function
  12. * MyScriptClass.Instance
  13. *
  14. * use the static function SetSingletonAutoCreate( GameObject )
  15. * to specify a GameObject - containing the MyScriptClass component -
  16. * that should be instantiated in case an instance is requested and
  17. * and no objects exists with the MyScriptClass component.
  18. *
  19. * ***********************************************************/
  20. using System;
  21. using UnityEngine;
  22. #pragma warning disable 1591 // undocumented XML code warning
  23. public class UnitySingleton<T>
  24. //where T : SingletonMonoBehaviour<T>
  25. where T : MonoBehaviour
  26. {
  27. #if UNITY_FLASH
  28. static UnityEngine.Object _instance;
  29. #else
  30. static T _instance;
  31. #endif
  32. static internal Type _myType = typeof( T ); // not working for Flash builds. Requires SetSingletonType() for correct Flash support
  33. static internal GameObject _autoCreatePrefab;
  34. static private int _GlobalInstanceCount = 0;
  35. static private bool _awakeSingletonCalled = false;
  36. static public T GetSingleton( bool throwErrorIfNotFound, bool autoCreate )
  37. {
  38. if ( !_instance ) // Unity operator to check if object was destroyed,
  39. {
  40. UnityEngine.Object component = null;
  41. #if UNITY_FLASH
  42. if( _myType == null )
  43. {
  44. Debug.LogError( "Flash builds require SetSingletonType() to be called!" );
  45. return null;
  46. }
  47. #endif
  48. var components = GameObject.FindObjectsOfType( _myType );
  49. foreach ( var c in components )
  50. {
  51. //var cpt = ( (Component)c ).GetComponent( _myType );
  52. var singletonCpt = (ISingletonMonoBehaviour)( c );
  53. if ( singletonCpt.isSingletonObject )
  54. {
  55. component = (UnityEngine.Object)singletonCpt;
  56. break;
  57. }
  58. }
  59. if ( !component )
  60. {
  61. if ( autoCreate && _autoCreatePrefab != null )
  62. {
  63. GameObject go = (GameObject)GameObject.Instantiate( _autoCreatePrefab );
  64. go.name = _autoCreatePrefab.name; // removes "(clone)"
  65. var newComponent = GameObject.FindObjectOfType( _myType );
  66. if ( !newComponent )
  67. {
  68. Debug.LogError( "Auto created object does not have component " + _myType.Name );
  69. return null;
  70. }
  71. }
  72. else
  73. {
  74. if ( throwErrorIfNotFound )
  75. {
  76. Debug.LogError( "No singleton component " + _myType.Name + " found in the scene." );
  77. }
  78. return null;
  79. }
  80. }
  81. else
  82. {
  83. _AwakeSingleton( component as T );
  84. }
  85. #if UNITY_FLASH
  86. _instance = component;
  87. #else
  88. _instance = (T) component;
  89. #endif
  90. }
  91. return (T)_instance;
  92. }
  93. private UnitySingleton( )
  94. { }
  95. #if UNITY_FLASH
  96. static internal void _Awake( UnityEngine.Object instance )
  97. #else
  98. static internal void _Awake( T instance )
  99. #endif
  100. {
  101. _GlobalInstanceCount++;
  102. if ( _GlobalInstanceCount > 1 )
  103. {
  104. Debug.LogError( "More than one instance of SingletonMonoBehaviour " + typeof( T ).Name );
  105. } else
  106. _instance = instance;
  107. _AwakeSingleton( instance as T);
  108. }
  109. static internal void _Destroy()
  110. {
  111. if ( _GlobalInstanceCount > 0 )
  112. {
  113. _GlobalInstanceCount--;
  114. if ( _GlobalInstanceCount == 0 )
  115. {
  116. _awakeSingletonCalled = false;
  117. _instance = null;
  118. }
  119. }
  120. else
  121. {
  122. // can happen if an exception occurred that prevented correct instance counting
  123. // Debug.LogWarning( "_GlobalInstanceCount < 0" );
  124. }
  125. }
  126. static private void _AwakeSingleton( T instance )
  127. {
  128. if ( !_awakeSingletonCalled )
  129. {
  130. _awakeSingletonCalled = true;
  131. instance.SendMessage( "AwakeSingleton", SendMessageOptions.DontRequireReceiver );
  132. }
  133. }
  134. }
  135. interface ISingletonMonoBehaviour
  136. {
  137. bool isSingletonObject { get; }
  138. }
  139. /// <summary>
  140. /// Provides singleton-like access to a unique instance of a MonoBehaviour. <para/>
  141. /// </summary>
  142. /// <example>
  143. /// Derive your own class from SingletonMonoBehaviour. <para/>
  144. /// <code>
  145. /// public class MyScriptClass : SingletonMonoBehaviour&lt;MyScriptClass&gt;
  146. /// {
  147. /// public MyScriptClass()
  148. /// {
  149. /// MyScriptClass.SetSingletonType( typeof( MyScriptClass ) ); // workaround for Flash
  150. /// }
  151. /// public void MyFunction() { }
  152. /// protected override void Awake()
  153. /// {
  154. /// base.Awake();
  155. /// }
  156. /// void AwakeSingleton()
  157. /// {
  158. /// // all initialisation code here. Will get called from Awake() by singleton.
  159. /// // Can get called before Awake() if an instance is accessed in an Awake() function which
  160. /// // was called earlier
  161. /// }
  162. /// }
  163. /// </code>
  164. /// <para/>
  165. /// access the instance by writing
  166. /// <code>
  167. /// MyScriptClass.Instance.MyFunction();
  168. /// </code>
  169. /// </example>
  170. /// <typeparam name="T">Your singleton MonoBehaviour</typeparam>
  171. /// <remarks>
  172. /// Makes sure that an instance is available from other Awake() calls even before the singleton's Awake()
  173. /// was called. ( Requires AwakeSingleton() !)
  174. /// </remarks>
  175. public abstract class SingletonMonoBehaviour<T> : MonoBehaviour, ISingletonMonoBehaviour
  176. //where T : SingletonMonoBehaviour<T>
  177. where T : MonoBehaviour
  178. {
  179. /// <summary>
  180. /// Gets the singleton instance.
  181. /// </summary>
  182. /// <returns>
  183. /// A reference to the instance if it exists, otherwise <c>null</c>
  184. /// </returns>
  185. /// <remarks>
  186. /// Outputs an error to the debug log if no instance was found.
  187. /// </remarks>
  188. static public T Instance
  189. { get { return UnitySingleton<T>.GetSingleton( true, true ); } }
  190. /// <summary>
  191. /// Checks if an instance of this MonoBehaviour exists.
  192. /// </summary>
  193. /// <returns>
  194. /// A reference to the instance if it exists, otherwise <c>null</c>
  195. /// </returns>
  196. static public T DoesInstanceExist( )
  197. {
  198. return UnitySingleton<T>.GetSingleton( false, false );
  199. }
  200. /// <summary>
  201. /// Activates the singleton instance.
  202. /// </summary>
  203. /// <remarks>
  204. /// Call this function if you set an singleton object inactive before ever accessing the <c>Instance</c>. This is
  205. /// required because Unity does not (yet) offer a way to find inactive game objects.
  206. /// </remarks>
  207. static public void ActivateSingletonInstance() //
  208. {
  209. UnitySingleton<T>.GetSingleton( true, true );
  210. }
  211. /// <summary>
  212. /// Sets the object to be instantiated automatically if no instance of the singleton is found.
  213. /// </summary>
  214. /// <param name="autoCreatePrefab">The prefab to be instantiated automatically.</param>
  215. /// <remarks>
  216. /// Either the game object itself or one of its child objects must contain the singleton component
  217. /// </remarks>
  218. static public void SetSingletonAutoCreate( GameObject autoCreatePrefab )
  219. {
  220. UnitySingleton<T>._autoCreatePrefab = autoCreatePrefab;
  221. }
  222. /// <summary>
  223. /// Only required for Flash builds. If this function is not called by the class deriving from
  224. /// SingletonMonoBehaviour in the constructor the singleton can not be found by GetSingleton(...)
  225. /// </summary>
  226. /// <param name="type"></param>
  227. static public void SetSingletonType( Type type )
  228. {
  229. UnitySingleton<T>._myType = type;
  230. }
  231. protected virtual void Awake() // should be called in derived class
  232. {
  233. if ( isSingletonObject )
  234. {
  235. #if UNITY_FLASH
  236. UnitySingleton<T>._Awake( this );
  237. #else
  238. UnitySingleton<T>._Awake( this as T );
  239. #endif
  240. //Debug.Log( "Awake: " + this.GetType().Name );
  241. }
  242. }
  243. protected virtual void OnDestroy() // should be called in derived class
  244. {
  245. if ( isSingletonObject )
  246. {
  247. UnitySingleton<T>._Destroy();
  248. }
  249. }
  250. /// <summary>
  251. /// must return true if this instance of the object is the singleton. Can be used to allow multiple objects of this type
  252. /// that are "add-ons" to the singleton.
  253. /// </summary>
  254. public virtual bool isSingletonObject
  255. {
  256. get
  257. {
  258. return true;
  259. }
  260. }
  261. }