using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using UnityEditor; using UnityEngine; using Object = UnityEngine.Object; namespace LanLib.Singleton.EditorUtilities { static class SingletonManager { private static Dictionary _lutSingletonCreation = null; static void DestroyGameObject(GameObject go) { if (Application.isPlaying) { Object.Destroy(go); } else { Object.DestroyImmediate(go); } } static void RemoveAllSingletons(Func predicate) { if (Selection.activeGameObject?.GetComponent()) { Selection.activeGameObject = null; } var instances = Resources .FindObjectsOfTypeAll() .Where(predicate) .ToArray(); foreach (var inst in instances) { DestroyGameObject(inst.gameObject); } } static Dictionary GetSingletonTypes() { if (_lutSingletonCreation != null) return _lutSingletonCreation; var baseType = typeof(SingletonAutoLoadAttribute); var thisAssemblyName = baseType.Assembly.GetName().ToString(); return _lutSingletonCreation = AppDomain.CurrentDomain.GetAssemblies() .Where(assembly => assembly.GetReferencedAssemblies().Any(r => r.ToString() == thisAssemblyName) && assembly.GetName().ToString() != thisAssemblyName) .SelectMany(assembly => assembly.GetCustomAttributes(false).OfType()) .Select(att => (att.type, att.createCondition)) .ToDictionary(d => d.type, d => d.createCondition); } [InitializeOnLoadMethod] #if !UNITY_EDITOR [RuntimeInitializeOnLoadMethod] #endif static void AutoCreateOnDomainReload() { foreach (var (t, con) in GetSingletonTypes()) { if (con.Contains(SingletonCreateCondition.ReloadDomain)) { t.BaseType.GetProperty("Instance", bindingFlags).GetValue(null); } } } [InitializeOnLoadMethod] static void AutoCreateOnEnterPlayMode() { EditorApplication.playModeStateChanged += e => { if (e == PlayModeStateChange.EnteredPlayMode) { foreach (var (t, con) in GetSingletonTypes()) { if (con.Contains(SingletonCreateCondition.EnterPlay)) { t.BaseType.GetProperty("Instance", bindingFlags).GetValue(null); } } } }; } [InitializeOnLoadMethod] static void CleanOnRecompileIfNeeded() { RemoveAllSingletons(inst => inst.shouldDestroyOnReloadDomain()); } [InitializeOnLoadMethod] static void CleanOnPlayModeChangeIfNeeded() { EditorApplication.playModeStateChanged += e => { if (e == PlayModeStateChange.ExitingPlayMode) { RemoveAllSingletons(inst => inst.shouldDestroyOnExitPlayMode()); } else if (e == PlayModeStateChange.ExitingEditMode) { RemoveAllSingletons(inst => inst.shouldDestroyOnExitEditMode()); } else if (e == PlayModeStateChange.EnteredPlayMode) { var instances = Resources .FindObjectsOfTypeAll() .ToArray(); foreach (var inst in instances) { inst.DestroyOnSceneUnload = inst.DestroyOnSceneUnload; } } }; } static bool shouldDestroyOnReloadDomain(this SingletonBase singleton) { var t = singleton.GetType(); var isAutoCreate = GetSingletonTypes().ContainsKey(t) && GetSingletonTypes()[t].Contains(SingletonCreateCondition.ReloadDomain); return singleton.DestroyCondition.Contains(SingletonDestroyCondition.ReloadDomain) && !isAutoCreate; } static bool shouldDestroyOnExitPlayMode(this SingletonBase singleton) => singleton.DestroyCondition.Contains(SingletonDestroyCondition.ExitPlay); static bool shouldDestroyOnExitEditMode(this SingletonBase singleton) => singleton.DestroyCondition.Contains(SingletonDestroyCondition.ExitEdit); static BindingFlags bindingFlags = 0 | BindingFlags.Static // | BindingFlags.Instance | BindingFlags.Public // | BindingFlags.NonPublic ; } }