A game about forced loneliness, made by TACStudios
1using System.Collections.Generic; 2using UnityEngine; 3using UnityObject = UnityEngine.Object; 4 5namespace Unity.VisualScripting 6{ 7 /// <remarks> 8 /// Does not support objects hidden with hide flags. 9 /// </remarks> 10 public static class Singleton<T> where T : MonoBehaviour, ISingleton 11 { 12 static Singleton() 13 { 14 awoken = new HashSet<T>(); 15 16 attribute = typeof(T).GetAttribute<SingletonAttribute>(); 17 18 if (attribute == null) 19 { 20 throw new InvalidImplementationException($"Missing singleton attribute for '{typeof(T)}'."); 21 } 22 } 23 24 private static readonly SingletonAttribute attribute; 25 26 private static bool persistent => attribute.Persistent; 27 28 private static bool automatic => attribute.Automatic; 29 30 private static string name => attribute.Name; 31 32 private static HideFlags hideFlags => attribute.HideFlags; 33 34 private static readonly object _lock = new object(); 35 36 private static readonly HashSet<T> awoken; 37 38 private static T _instance; 39 40 public static bool instantiated 41 { 42 get 43 { 44 lock (_lock) 45 { 46 if (Application.isPlaying) 47 { 48 return _instance != null; 49 } 50 else 51 { 52 return FindInstances().Length == 1; 53 } 54 } 55 } 56 } 57 58 public static T instance 59 { 60 get 61 { 62 lock (_lock) 63 { 64 if (Application.isPlaying) 65 { 66 if (_instance == null) 67 { 68 Instantiate(); 69 } 70 71 return _instance; 72 } 73 else 74 { 75 return Instantiate(); 76 } 77 } 78 } 79 } 80 81 private static T[] FindObjectsOfType() 82 { 83#if UNITY_2023_1_OR_NEWER 84 return UnityObject.FindObjectsByType<T>(FindObjectsSortMode.None); 85#else 86 return UnityObject.FindObjectsOfType<T>(); 87#endif 88 } 89 90 private static T[] FindInstances() 91 { 92 // Fails here on hidden hide flags 93 return FindObjectsOfType(); 94 } 95 96 public static T Instantiate() 97 { 98 lock (_lock) 99 { 100 var instances = FindInstances(); 101 102 if (instances.Length == 1) 103 { 104 _instance = instances[0]; 105 } 106 else if (instances.Length == 0) 107 { 108 if (automatic) 109 { 110 // Create the parent game object with the proper hide flags 111 var singleton = new GameObject(name ?? typeof(T).Name); 112 singleton.hideFlags = hideFlags; 113 114 // Instantiate the component, letting Awake assign the real instance variable 115 var _instance = singleton.AddComponent<T>(); 116 _instance.hideFlags = hideFlags; 117 118 // Sometimes in the editor, for example when creating a new scene, 119 // AddComponent seems to call Awake add a later frame, making this call 120 // fail for exactly one frame. We'll force-awake it if need be. 121 Awake(_instance); 122 123 // Make the singleton persistent if need be 124 if (persistent && Application.isPlaying) 125 { 126 UnityObject.DontDestroyOnLoad(singleton); 127 } 128 } 129 else 130 { 131 throw new UnityException($"Missing '{typeof(T)}' singleton in the scene."); 132 } 133 } 134 else if (instances.Length > 1) 135 { 136 throw new UnityException($"More than one '{typeof(T)}' singleton in the scene."); 137 } 138 139 return _instance; 140 } 141 } 142 143 public static void Awake(T instance) 144 { 145 Ensure.That(nameof(instance)).IsNotNull(instance); 146 147 if (awoken.Contains(instance)) 148 { 149 return; 150 } 151 152 if (_instance != null) 153 { 154 throw new UnityException($"More than one '{typeof(T)}' singleton in the scene."); 155 } 156 157 _instance = instance; 158 159 awoken.Add(instance); 160 } 161 162 public static void OnDestroy(T instance) 163 { 164 Ensure.That(nameof(instance)).IsNotNull(instance); 165 166 if (_instance == instance) 167 { 168 _instance = null; 169 } 170 else 171 { 172 throw new UnityException($"Trying to destroy invalid instance of '{typeof(T)}' singleton."); 173 } 174 } 175 } 176}