// Unity using UnityEngine; namespace GUPS.AntiCheat.Singleton { /// /// A thread-safe singleton either active throughout the whole application or scene only. /// /// The type of the singleton. public abstract class Singleton : MonoBehaviour where T : Singleton { /// /// The singleton instance. /// private static T instance; /// /// Lock for thread safety. /// private static object lockHandle = new object(); /// /// Returns true if the singleton is persistent. /// public abstract bool IsPersistent { get; } /// /// Prevent creation of the singleton when the application is quitting. /// private static bool isQuitting = false; /// /// Returns an active singleton of this instance or creates a new one. /// public static T Instance { get { lock (lockHandle) { // The GameObject got destroyed, so the singleton is null. if (instance != null && instance.gameObject == null) { instance = null; } // If there is no singleton, create a new one. if (instance == null) { // Find all instances of the singleton. #if UNITY_2023_1_OR_NEWER var instances = FindObjectsByType(typeof(T), FindObjectsInactive.Include, FindObjectsSortMode.None); #else var instances = FindObjectsOfType(typeof(T)); #endif // If there is at least one instance, use always the first one. if (instances.Length > 0) { instance = instances[0] as T; } // If there is no instance, create a new one. if (instance == null) { Create(); } } return instance; } } } /// /// Returns true if a singleton exists. /// public static bool Exists { get { return instance != null; } } /// /// On awake, check if there is already a singleton. /// If there is one and it is not this, destroy the GameObject. /// protected virtual void Awake() { // If a singleton already exists and this is not the singleton, destroy it immediately. Else keep it. if (Exists) { if (this != instance && this.gameObject != null) { DestroyImmediate(this.gameObject); } } else { // Set the singleton. instance = this as T; // Rename the GameObject and mark it as 'do not destroy' if it is persistent. if (instance.IsPersistent) { // Rename the GameObject. instance.gameObject.name = "(PersistentSingleton) " + typeof(T).Name.ToString(); // Mark the GameObject as 'do not destroy'. DontDestroyOnLoad(instance.gameObject); } else { // Rename the GameObject. instance.gameObject.name = "(Singleton) " + typeof(T).Name.ToString(); } } } /// /// Create a GameObject adding T1 and set the singleton to the value T1. /// /// The type of the singleton. private static void Create() where T1 : T { // Already exists, just return. if (Exists) { return; } // If the application is not playing or quitting, return. if (!Application.isPlaying || isQuitting) { return; } // Create a GameObject. GameObject var_Singleton = new GameObject(); // Add the singleton component to it. instance = var_Singleton.AddComponent(); // Rename the GameObject and mark it as 'do not destroy' if it is persistent. if (instance.IsPersistent) { // Rename the GameObject. instance.gameObject.name = "(PersistentSingleton) " + typeof(T).Name.ToString(); // Mark the GameObject as 'do not destroy'. DontDestroyOnLoad(instance.gameObject); } else { // Rename the GameObject. instance.gameObject.name = "(Singleton) " + typeof(T).Name.ToString(); } } /// /// Called when the application quits. Prevents the singleton from being recreated on quit. /// protected virtual void OnApplicationQuit() { // Set the flag to true, so the singleton is not recreated on quitting. isQuitting = true; } } }