using System; using JetBrains.Annotations; using JohnsonUtils.Utilities; using UnityEngine; namespace JohnsonUtils.Common { public abstract class AbstractSingleton : MonoBehaviour where T : AbstractSingleton { protected static T SharedInstance; [PublicAPI] public static bool HasInstance => SharedInstance != null; protected virtual void Awake() { try { SharedInstance = (T) Convert.ChangeType(this, typeof(T)); } catch (InvalidCastException) { Debug.Assert(false, "Singleton's T type should be the derived class."); } } protected void DestroyInstance() { if (gameObject.GetNumberOfComponents() == 1) { Destroy(gameObject); } else { Destroy(this); } } protected virtual void OnApplicationQuit() { SharedInstance = null; DestroyInstance(); } } /// /// Singleton class that can be loaded and unloaded with scenes. /// public abstract class Singleton : AbstractSingleton where T : Singleton { public static T Instance { get { Debug.Assert(HasInstance, $"Trying to access a script of type {typeof(T).Name} that is not in the scene."); return SharedInstance; } } protected override void Awake() { if (HasInstance) { Debug.LogWarning($"New instance of type {typeof(T).Name} detected. " + "This new instance is becoming the default instance."); SharedInstance = (T) Convert.ChangeType(this, typeof(T)); return; } base.Awake(); } protected virtual void OnDestroy() { if (SharedInstance == this) { SharedInstance = null; } } } /// /// Singleton class that is lazy loaded once and is only unloaded when the game exits. /// public abstract class PersistentSingleton : AbstractSingleton where T : PersistentSingleton { [PublicAPI] public static T Instance { get { CreateIfDoesNotExist(); return SharedInstance; } } [PublicAPI] public static void CreateIfDoesNotExist() { if (!HasInstance && Application.isPlaying) { CreateInstance(); } } private static void CreateInstance() { var instanceGameObject = new GameObject(typeof(T).Name); instanceGameObject.AddComponent(); } protected override void Awake() { if (HasInstance) { Debug.Log($"Two or more instances of a singleton of type {typeof(T).Name} were found in the scene. " + "The new instance of the singleton trying to register will be removed."); DestroyInstance(); return; } DontDestroyOnLoadUtils.Add(gameObject); base.Awake(); } } }