/**************************************************************************** * Copyright (c) 2015 ~ 2024 liangxiegame MIT License * * QFramework v1.0 * * https://qframework.cn * https://github.com/liangxiegame/QFramework * https://gitee.com/liangxiegame/QFramework * * Author: * liangxie https://github.com/liangxie * soso https://github.com/so-sos-so * * Contributor * TastSong https://github.com/TastSong * 京产肠饭 https://gitee.com/JingChanChangFan/hk_-unity-tools * 猫叔(一只皮皮虾) https://space.bilibili.com/656352/ * misakiMeiii https://github.com/misakiMeiii * New一天 * 幽飞冷凝雪~冷 * * Community * QQ Group: 623597263 * * Latest Update: 2024.5.12 20:17 add UnRegisterWhenCurrentSceneUnloaded(Suggested by misakiMeiii) ****************************************************************************/ using System; using System.Collections.Generic; using System.Linq; using UnityEngine; using UnityEngine.SceneManagement; namespace QFramework { #region Architecture public interface IArchitecture { void RegisterSystem(T system) where T : ISystem; void RegisterModel(T model) where T : IModel; void RegisterUtility(T utility) where T : IUtility; T GetSystem() where T : class, ISystem; T GetModel() where T : class, IModel; T GetUtility() where T : class, IUtility; void SendCommand(T command) where T : ICommand; TResult SendCommand(ICommand command); TResult SendQuery(IQuery query); void SendEvent() where T : new(); void SendEvent(T e); IUnRegister RegisterEvent(Action onEvent); void UnRegisterEvent(Action onEvent); void Deinit(); } public abstract class Architecture : IArchitecture where T : Architecture, new() { private bool mInited = false; public static Action OnRegisterPatch = architecture => { }; protected static T mArchitecture; public static IArchitecture Interface { get { if (mArchitecture == null) MakeSureArchitecture(); return mArchitecture; } } static void MakeSureArchitecture() { if (mArchitecture == null) { mArchitecture = new T(); mArchitecture.Init(); OnRegisterPatch?.Invoke(mArchitecture); foreach (var model in mArchitecture.mContainer.GetInstancesByType().Where(m => !m.Initialized)) { model.Init(); model.Initialized = true; } foreach (var system in mArchitecture.mContainer.GetInstancesByType() .Where(m => !m.Initialized)) { system.Init(); system.Initialized = true; } mArchitecture.mInited = true; } } protected abstract void Init(); public void Deinit() { OnDeinit(); foreach (var system in mContainer.GetInstancesByType().Where(s => s.Initialized)) system.Deinit(); foreach (var model in mContainer.GetInstancesByType().Where(m => m.Initialized)) model.Deinit(); mContainer.Clear(); mArchitecture = null; } protected virtual void OnDeinit() { } private IOCContainer mContainer = new IOCContainer(); public void RegisterSystem(TSystem system) where TSystem : ISystem { system.SetArchitecture(this); mContainer.Register(system); if (mInited) { system.Init(); system.Initialized = true; } } public void RegisterModel(TModel model) where TModel : IModel { model.SetArchitecture(this); mContainer.Register(model); if (mInited) { model.Init(); model.Initialized = true; } } public void RegisterUtility(TUtility utility) where TUtility : IUtility => mContainer.Register(utility); public TSystem GetSystem() where TSystem : class, ISystem => mContainer.Get(); public TModel GetModel() where TModel : class, IModel => mContainer.Get(); public TUtility GetUtility() where TUtility : class, IUtility => mContainer.Get(); public TResult SendCommand(ICommand command) => ExecuteCommand(command); public void SendCommand(TCommand command) where TCommand : ICommand => ExecuteCommand(command); protected virtual TResult ExecuteCommand(ICommand command) { command.SetArchitecture(this); return command.Execute(); } protected virtual void ExecuteCommand(ICommand command) { command.SetArchitecture(this); command.Execute(); } public TResult SendQuery(IQuery query) => DoQuery(query); protected virtual TResult DoQuery(IQuery query) { query.SetArchitecture(this); return query.Do(); } private TypeEventSystem mTypeEventSystem = new TypeEventSystem(); public void SendEvent() where TEvent : new() => mTypeEventSystem.Send(); public void SendEvent(TEvent e) => mTypeEventSystem.Send(e); public IUnRegister RegisterEvent(Action onEvent) => mTypeEventSystem.Register(onEvent); public void UnRegisterEvent(Action onEvent) => mTypeEventSystem.UnRegister(onEvent); } public interface IOnEvent { void OnEvent(T e); } public static class OnGlobalEventExtension { public static IUnRegister RegisterEvent(this IOnEvent self) where T : struct => TypeEventSystem.Global.Register(self.OnEvent); public static void UnRegisterEvent(this IOnEvent self) where T : struct => TypeEventSystem.Global.UnRegister(self.OnEvent); } #endregion #region Controller public interface IController : IBelongToArchitecture, ICanSendCommand, ICanGetSystem, ICanGetModel, ICanRegisterEvent, ICanSendQuery, ICanGetUtility { } #endregion #region System public interface ISystem : IBelongToArchitecture, ICanSetArchitecture, ICanGetModel, ICanGetUtility, ICanRegisterEvent, ICanSendEvent, ICanGetSystem, ICanInit { } public abstract class AbstractSystem : ISystem { private IArchitecture mArchitecture; IArchitecture IBelongToArchitecture.GetArchitecture() => mArchitecture; void ICanSetArchitecture.SetArchitecture(IArchitecture architecture) => mArchitecture = architecture; public bool Initialized { get; set; } void ICanInit.Init() => OnInit(); public void Deinit() => OnDeinit(); protected virtual void OnDeinit() { } protected abstract void OnInit(); } #endregion #region Model public interface IModel : IBelongToArchitecture, ICanSetArchitecture, ICanGetUtility, ICanSendEvent, ICanInit { } public abstract class AbstractModel : IModel { private IArchitecture mArchitecturel; IArchitecture IBelongToArchitecture.GetArchitecture() => mArchitecturel; void ICanSetArchitecture.SetArchitecture(IArchitecture architecture) => mArchitecturel = architecture; public bool Initialized { get; set; } void ICanInit.Init() => OnInit(); public void Deinit() => OnDeinit(); protected virtual void OnDeinit() { } protected abstract void OnInit(); } #endregion #region Utility public interface IUtility { } #endregion #region Command public interface ICommand : IBelongToArchitecture, ICanSetArchitecture, ICanGetSystem, ICanGetModel, ICanGetUtility, ICanSendEvent, ICanSendCommand, ICanSendQuery { void Execute(); } public interface ICommand : IBelongToArchitecture, ICanSetArchitecture, ICanGetSystem, ICanGetModel, ICanGetUtility, ICanSendEvent, ICanSendCommand, ICanSendQuery { TResult Execute(); } public abstract class AbstractCommand : ICommand { private IArchitecture mArchitecture; IArchitecture IBelongToArchitecture.GetArchitecture() => mArchitecture; void ICanSetArchitecture.SetArchitecture(IArchitecture architecture) => mArchitecture = architecture; void ICommand.Execute() => OnExecute(); protected abstract void OnExecute(); } public abstract class AbstractCommand : ICommand { private IArchitecture mArchitecture; IArchitecture IBelongToArchitecture.GetArchitecture() => mArchitecture; void ICanSetArchitecture.SetArchitecture(IArchitecture architecture) => mArchitecture = architecture; TResult ICommand.Execute() => OnExecute(); protected abstract TResult OnExecute(); } #endregion #region Query public interface IQuery : IBelongToArchitecture, ICanSetArchitecture, ICanGetModel, ICanGetSystem, ICanSendQuery { TResult Do(); } public abstract class AbstractQuery : IQuery { public T Do() => OnDo(); protected abstract T OnDo(); private IArchitecture mArchitecture; public IArchitecture GetArchitecture() => mArchitecture; public void SetArchitecture(IArchitecture architecture) => mArchitecture = architecture; } #endregion #region Rule public interface IBelongToArchitecture { IArchitecture GetArchitecture(); } public interface ICanSetArchitecture { void SetArchitecture(IArchitecture architecture); } public interface ICanGetModel : IBelongToArchitecture { } public static class CanGetModelExtension { public static T GetModel(this ICanGetModel self) where T : class, IModel => self.GetArchitecture().GetModel(); } public interface ICanGetSystem : IBelongToArchitecture { } public static class CanGetSystemExtension { public static T GetSystem(this ICanGetSystem self) where T : class, ISystem => self.GetArchitecture().GetSystem(); } public interface ICanGetUtility : IBelongToArchitecture { } public static class CanGetUtilityExtension { public static T GetUtility(this ICanGetUtility self) where T : class, IUtility => self.GetArchitecture().GetUtility(); } public interface ICanRegisterEvent : IBelongToArchitecture { } public static class CanRegisterEventExtension { public static IUnRegister RegisterEvent(this ICanRegisterEvent self, Action onEvent) => self.GetArchitecture().RegisterEvent(onEvent); public static void UnRegisterEvent(this ICanRegisterEvent self, Action onEvent) => self.GetArchitecture().UnRegisterEvent(onEvent); } public interface ICanSendCommand : IBelongToArchitecture { } public static class CanSendCommandExtension { public static void SendCommand(this ICanSendCommand self) where T : ICommand, new() => self.GetArchitecture().SendCommand(new T()); public static void SendCommand(this ICanSendCommand self, T command) where T : ICommand => self.GetArchitecture().SendCommand(command); public static TResult SendCommand(this ICanSendCommand self, ICommand command) => self.GetArchitecture().SendCommand(command); } public interface ICanSendEvent : IBelongToArchitecture { } public static class CanSendEventExtension { public static void SendEvent(this ICanSendEvent self) where T : new() => self.GetArchitecture().SendEvent(); public static void SendEvent(this ICanSendEvent self, T e) => self.GetArchitecture().SendEvent(e); } public interface ICanSendQuery : IBelongToArchitecture { } public static class CanSendQueryExtension { public static TResult SendQuery(this ICanSendQuery self, IQuery query) => self.GetArchitecture().SendQuery(query); } public interface ICanInit { bool Initialized { get; set; } void Init(); void Deinit(); } #endregion #region TypeEventSystem public interface IUnRegister { void UnRegister(); } public interface IUnRegisterList { List UnregisterList { get; } } public static class IUnRegisterListExtension { public static void AddToUnregisterList(this IUnRegister self, IUnRegisterList unRegisterList) => unRegisterList.UnregisterList.Add(self); public static void UnRegisterAll(this IUnRegisterList self) { foreach (var unRegister in self.UnregisterList) { unRegister.UnRegister(); } self.UnregisterList.Clear(); } } public struct CustomUnRegister : IUnRegister { private Action mOnUnRegister { get; set; } public CustomUnRegister(Action onUnRegister) => mOnUnRegister = onUnRegister; public void UnRegister() { mOnUnRegister.Invoke(); mOnUnRegister = null; } } #if UNITY_5_6_OR_NEWER public abstract class UnRegisterTrigger : UnityEngine.MonoBehaviour { private readonly HashSet mUnRegisters = new HashSet(); public IUnRegister AddUnRegister(IUnRegister unRegister) { mUnRegisters.Add(unRegister); return unRegister; } public void RemoveUnRegister(IUnRegister unRegister) => mUnRegisters.Remove(unRegister); public void UnRegister() { foreach (var unRegister in mUnRegisters) { unRegister.UnRegister(); } mUnRegisters.Clear(); } } public class UnRegisterOnDestroyTrigger : UnRegisterTrigger { private void OnDestroy() { UnRegister(); } } public class UnRegisterOnDisableTrigger : UnRegisterTrigger { private void OnDisable() { UnRegister(); } } public class UnRegisterCurrentSceneUnloadedTrigger : UnRegisterTrigger { private static UnRegisterCurrentSceneUnloadedTrigger mDefault; public static UnRegisterCurrentSceneUnloadedTrigger Get { get { if (!mDefault) { mDefault = new GameObject("UnRegisterCurrentSceneUnloadedTrigger") .AddComponent(); } return mDefault; } } private void Awake() { DontDestroyOnLoad(this); hideFlags = HideFlags.HideInHierarchy; SceneManager.sceneUnloaded += OnSceneUnloaded; } private void OnDestroy() => SceneManager.sceneUnloaded -= OnSceneUnloaded; void OnSceneUnloaded(Scene scene) => UnRegister(); } #endif public static class UnRegisterExtension { #if UNITY_5_6_OR_NEWER static T GetOrAddComponent(GameObject gameObject) where T : Component { var trigger = gameObject.GetComponent(); if (!trigger) { trigger = gameObject.AddComponent(); } return trigger; } public static IUnRegister UnRegisterWhenGameObjectDestroyed(this IUnRegister unRegister, UnityEngine.GameObject gameObject) => GetOrAddComponent(gameObject) .AddUnRegister(unRegister); public static IUnRegister UnRegisterWhenGameObjectDestroyed(this IUnRegister self, T component) where T : UnityEngine.Component => self.UnRegisterWhenGameObjectDestroyed(component.gameObject); public static IUnRegister UnRegisterWhenDisabled(this IUnRegister self, T component) where T : UnityEngine.Component => self.UnRegisterWhenDisabled(component.gameObject); public static IUnRegister UnRegisterWhenDisabled(this IUnRegister unRegister, UnityEngine.GameObject gameObject) => GetOrAddComponent(gameObject) .AddUnRegister(unRegister); public static IUnRegister UnRegisterWhenCurrentSceneUnloaded(this IUnRegister self) => UnRegisterCurrentSceneUnloadedTrigger.Get.AddUnRegister(self); #endif #if GODOT public static IUnRegister UnRegisterWhenNodeExitTree(this IUnRegister unRegister, Godot.Node node) { node.TreeExiting += unRegister.UnRegister; return unRegister; } #endif } public class TypeEventSystem { private readonly EasyEvents mEvents = new EasyEvents(); public static readonly TypeEventSystem Global = new TypeEventSystem(); public void Send() where T : new() => mEvents.GetEvent>()?.Trigger(new T()); public void Send(T e) => mEvents.GetEvent>()?.Trigger(e); public IUnRegister Register(Action onEvent) => mEvents.GetOrAddEvent>().Register(onEvent); public void UnRegister(Action onEvent) { var e = mEvents.GetEvent>(); e?.UnRegister(onEvent); } } #endregion #region IOC public class IOCContainer { private Dictionary mInstances = new Dictionary(); public void Register(T instance) { var key = typeof(T); if (mInstances.ContainsKey(key)) { mInstances[key] = instance; } else { mInstances.Add(key, instance); } } public T Get() where T : class { var key = typeof(T); if (mInstances.TryGetValue(key, out var retInstance)) { return retInstance as T; } return null; } public IEnumerable GetInstancesByType() { var type = typeof(T); return mInstances.Values.Where(instance => type.IsInstanceOfType(instance)).Cast(); } public void Clear() => mInstances.Clear(); } #endregion #region BindableProperty public interface IBindableProperty : IReadonlyBindableProperty { new T Value { get; set; } void SetValueWithoutEvent(T newValue); } public interface IReadonlyBindableProperty : IEasyEvent { T Value { get; } IUnRegister RegisterWithInitValue(Action action); void UnRegister(Action onValueChanged); IUnRegister Register(Action onValueChanged); } public class BindableProperty : IBindableProperty { public BindableProperty(T defaultValue = default) => mValue = defaultValue; protected T mValue; public static Func Comparer { get; set; } = (a, b) => a.Equals(b); public BindableProperty WithComparer(Func comparer) { Comparer = comparer; return this; } public T Value { get => GetValue(); set { if (value == null && mValue == null) return; if (value != null && Comparer(value, mValue)) return; SetValue(value); mOnValueChanged.Trigger(value); } } protected virtual void SetValue(T newValue) => mValue = newValue; protected virtual T GetValue() => mValue; public void SetValueWithoutEvent(T newValue) => mValue = newValue; private EasyEvent mOnValueChanged = new EasyEvent(); public IUnRegister Register(Action onValueChanged) { return mOnValueChanged.Register(onValueChanged); } public IUnRegister RegisterWithInitValue(Action onValueChanged) { onValueChanged(mValue); return Register(onValueChanged); } public void UnRegister(Action onValueChanged) => mOnValueChanged.UnRegister(onValueChanged); IUnRegister IEasyEvent.Register(Action onEvent) { return Register(Action); void Action(T _) => onEvent(); } public override string ToString() => Value.ToString(); } internal class ComparerAutoRegister { #if UNITY_5_6_OR_NEWER [UnityEngine.RuntimeInitializeOnLoadMethod(UnityEngine.RuntimeInitializeLoadType.BeforeSceneLoad)] public static void AutoRegister() { BindableProperty.Comparer = (a, b) => a == b; BindableProperty.Comparer = (a, b) => a == b; BindableProperty.Comparer = (a, b) => a == b; BindableProperty.Comparer = (a, b) => a == b; BindableProperty.Comparer = (a, b) => a == b; BindableProperty.Comparer = (a, b) => a == b; BindableProperty.Comparer = (a, b) => a == b; BindableProperty.Comparer = (a, b) => a == b; BindableProperty.Comparer = (a, b) => a == b; BindableProperty.Comparer = (a, b) => a.r == b.r && a.g == b.g && a.b == b.b && a.a == b.a; BindableProperty.Comparer = (a, b) => a == b; BindableProperty.Comparer = (a, b) => a == b; BindableProperty.Comparer = (a, b) => a == b; BindableProperty.Comparer = (a, b) => a == b; BindableProperty.Comparer = (a, b) => a == b; BindableProperty.Comparer = (a, b) => a == b; BindableProperty.Comparer = (a, b) => a.start == b.start && a.length == b.length; BindableProperty.Comparer = (a, b) => a.Equals(b); } #endif } #endregion #region EasyEvent public interface IEasyEvent { IUnRegister Register(Action onEvent); } public class EasyEvent : IEasyEvent { private Action mOnEvent = () => { }; public IUnRegister Register(Action onEvent) { mOnEvent += onEvent; return new CustomUnRegister(() => { UnRegister(onEvent); }); } public IUnRegister RegisterWithACall(Action onEvent) { onEvent.Invoke(); return Register(onEvent); } public void UnRegister(Action onEvent) => mOnEvent -= onEvent; public void Trigger() => mOnEvent?.Invoke(); } public class EasyEvent : IEasyEvent { private Action mOnEvent = e => { }; public IUnRegister Register(Action onEvent) { mOnEvent += onEvent; return new CustomUnRegister(() => { UnRegister(onEvent); }); } public void UnRegister(Action onEvent) => mOnEvent -= onEvent; public void Trigger(T t) => mOnEvent?.Invoke(t); IUnRegister IEasyEvent.Register(Action onEvent) { return Register(Action); void Action(T _) => onEvent(); } } public class EasyEvent : IEasyEvent { private Action mOnEvent = (t, k) => { }; public IUnRegister Register(Action onEvent) { mOnEvent += onEvent; return new CustomUnRegister(() => { UnRegister(onEvent); }); } public void UnRegister(Action onEvent) => mOnEvent -= onEvent; public void Trigger(T t, K k) => mOnEvent?.Invoke(t, k); IUnRegister IEasyEvent.Register(Action onEvent) { return Register(Action); void Action(T _, K __) => onEvent(); } } public class EasyEvent : IEasyEvent { private Action mOnEvent = (t, k, s) => { }; public IUnRegister Register(Action onEvent) { mOnEvent += onEvent; return new CustomUnRegister(() => { UnRegister(onEvent); }); } public void UnRegister(Action onEvent) => mOnEvent -= onEvent; public void Trigger(T t, K k, S s) => mOnEvent?.Invoke(t, k, s); IUnRegister IEasyEvent.Register(Action onEvent) { return Register(Action); void Action(T _, K __, S ___) => onEvent(); } } public class EasyEvents { private static readonly EasyEvents mGlobalEvents = new EasyEvents(); public static T Get() where T : IEasyEvent => mGlobalEvents.GetEvent(); public static void Register() where T : IEasyEvent, new() => mGlobalEvents.AddEvent(); private readonly Dictionary mTypeEvents = new Dictionary(); public void AddEvent() where T : IEasyEvent, new() => mTypeEvents.Add(typeof(T), new T()); public T GetEvent() where T : IEasyEvent { return mTypeEvents.TryGetValue(typeof(T), out var e) ? (T)e : default; } public T GetOrAddEvent() where T : IEasyEvent, new() { var eType = typeof(T); if (mTypeEvents.TryGetValue(eType, out var e)) { return (T)e; } var t = new T(); mTypeEvents.Add(eType, t); return t; } } #endregion #region Event Extension public class OrEvent : IUnRegisterList { public OrEvent Or(IEasyEvent easyEvent) { easyEvent.Register(Trigger).AddToUnregisterList(this); return this; } private Action mOnEvent = () => { }; public IUnRegister Register(Action onEvent) { mOnEvent += onEvent; return new CustomUnRegister(() => { UnRegister(onEvent); }); } public IUnRegister RegisterWithACall(Action onEvent) { onEvent.Invoke(); return Register(onEvent); } public void UnRegister(Action onEvent) { mOnEvent -= onEvent; this.UnRegisterAll(); } private void Trigger() => mOnEvent?.Invoke(); public List UnregisterList { get; } = new List(); } public static class OrEventExtensions { public static OrEvent Or(this IEasyEvent self, IEasyEvent e) => new OrEvent().Or(self).Or(e); } #endregion #if UNITY_EDITOR internal class EditorMenus { [UnityEditor.MenuItem("QFramework/Install QFrameworkWithToolKits")] public static void InstallPackageKit() => UnityEngine.Application.OpenURL("https://qframework.cn/qf"); } #endif }