using System.Linq; using System.Reflection; using UnityEngine; using UnityEngine.Assertions; using UnityEngine.SceneManagement; namespace LanLib.Singleton { [ExecuteInEditMode] public class Singleton : SingletonBase where T : Singleton { private const BindingFlags BindingFlags = System.Reflection.BindingFlags.Default | System.Reflection.BindingFlags.FlattenHierarchy | System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic; private static T _instance; public static T Instance { get { if (!_instance) { #if UNITY_EDITOR _instance = Resources.FindObjectsOfTypeAll().FirstOrDefault(inst => inst); #else _instance = FindObjectOfType(); #endif } if (!_instance) { _instance = (T)typeof(T).GetMethod("CreateInstance", BindingFlags)?.Invoke(null, null); } Assert.IsNotNull(_instance); return _instance; } } protected static T CreateInstance() { return new GameObject(typeof(T).Name) { hideFlags = HideFlags.HideAndDontSave ^ HideFlags.NotEditable }.AddComponent(); } protected virtual void Awake() { if (_instance == null) { _instance = this as T; if (Application.isPlaying) { DestroyOnSceneUnload = DestroyOnSceneUnload; } } else { DestroySelf(); } } } public abstract class SingletonBase : MonoBehaviour { private SingletonDestroyCondition _destroyCondition = SingletonDestroyCondition.ReloadDomain; public virtual SingletonDestroyCondition DestroyCondition { get => _destroyCondition; private set { var flgSceneUnload = (value ^ _destroyCondition) & SingletonDestroyCondition.SceneUnload; if (flgSceneUnload != 0) { var hasFlag = 0 != (value & SingletonDestroyCondition.SceneUnload); DestroyOnSceneUnload = hasFlag; } _destroyCondition = value; } } public bool DestroyOnSceneUnload { get => DestroyCondition.Contains(SingletonDestroyCondition.SceneUnload); set { if (value) { SceneManager.MoveGameObjectToScene(gameObject, SceneManager.GetActiveScene()); DestroyCondition |= SingletonDestroyCondition.SceneUnload; } else { DontDestroyOnLoad(gameObject); DestroyCondition &= ~SingletonDestroyCondition.SceneUnload; } } } internal void DestroySelf() { if (Application.isPlaying) { Destroy(gameObject); } else { DestroyImmediate(gameObject); } } } }