diff --git a/Convention/[Architecture]/Architecture.cs b/Convention/[Architecture]/Architecture.cs index e4405cd..26dcf87 100644 --- a/Convention/[Architecture]/Architecture.cs +++ b/Convention/[Architecture]/Architecture.cs @@ -6,70 +6,90 @@ namespace Convention.Experimental { public static class Architecture { - private static readonly LinkedCacheList s_GameFrameworkModules = new(); + private static readonly LinkedCacheList s_GameFrameworkModules = new(); /// - /// 创建 + /// 创建实例类型 /// - /// 要创建的类型 - /// 要创建的 - private static GameModule CreateModule(Type moduleType, int stackLayer = 0) + /// 要创建的类型 + /// 目标实例 + private static object Create(Type instanceType, int stackLayer = 0) { if (stackLayer >= 100) { - throw new GameException($"Create module '{moduleType.FullName}' failed, recursion too deep, there may be a circular dependency."); + throw new GameException($"Create instance '{instanceType.FullName}' failed, recursion too deep, there may be a circular dependency."); } - GameModule module = (GameModule)Activator.CreateInstance(moduleType); - if (module == null) + object instance = Activator.CreateInstance(instanceType); + if (instance == null) { - throw new GameException($"Can not create module '{moduleType.FullName}'"); + throw new GameException($"Can not create instance '{instanceType.FullName}'"); } - // 递归创建依赖模块 - foreach (var dependenceType in module.Dependences()) - _ = GetModule(dependenceType, stackLayer + 1); - - LinkedListNode current = s_GameFrameworkModules.First; - while (current != null) + if (instance is GameModule module) { - if (module.Priority > current.Value.Priority) + // 递归创建依赖模块 + foreach (var dependenceType in module.Dependences()) + _ = Get(dependenceType, stackLayer + 1); + + var current = s_GameFrameworkModules.First; + while (current != null) { - break; + if (current.Value is GameModule nextModule && module.Priority > nextModule.Priority) + { + break; + } + + current = current.Next; } - current = current.Next; + if (current != null) + { + s_GameFrameworkModules.AddBefore(current, module); + } + else + { + s_GameFrameworkModules.AddLast(module); + } } - if (current != null) - { - s_GameFrameworkModules.AddBefore(current, module); - } - else - { - s_GameFrameworkModules.AddLast(module); - } - - return module; + return instance; } /// - /// 获取 + /// 获取实例 /// - /// 要获取的 - /// 要获取的 - /// 如果要获取的不存在,则自动创建该实例。 - private static GameModule GetModule(Type moduleType, int stackLayer = 0) + /// 要获取的实例类型 + /// 实例 + /// 如果要获取的实例类型不存在,则自动创建该类型的实例。 + public static object Get(Type instanceType, int stackLayer = 0) { - foreach (GameModule module in s_GameFrameworkModules) + foreach (object obj in s_GameFrameworkModules) { - if (module.GetType() == moduleType) + if (obj.GetType() == instanceType) { - return module; + return obj; } } - return CreateModule(moduleType, stackLayer + 1); + return Create(instanceType, stackLayer + 1); + } + + /// + /// 是否存在实例 + /// + /// 要检查的实例类型 + /// 实例是否存在 + public static bool Contains(Type type) + { + foreach (object obj in s_GameFrameworkModules) + { + if (obj.GetType() == type) + { + return true; + } + } + return false; } /// @@ -80,7 +100,7 @@ namespace Convention.Experimental /// 如果要获取的游戏框架模块不存在,则自动创建该游戏框架模块。 public static T GetModule() where T : GameModule { - return (T)GetModule(typeof(T)); + return (T)Get(typeof(T)); } /// @@ -90,7 +110,8 @@ namespace Convention.Experimental { for (var current = s_GameFrameworkModules.Last; current != null; current = current.Previous) { - current.Value.Shutdown(); + if (current.Value is GameModule module) + module.Shutdown(); } s_GameFrameworkModules.Clear(); @@ -105,9 +126,10 @@ namespace Convention.Experimental /// 真实流逝时间,以秒为单位。 public static void Update(float elapseSeconds, float realElapseSeconds) { - foreach (var module in s_GameFrameworkModules) + foreach (object obj in s_GameFrameworkModules) { - module.Update(elapseSeconds, realElapseSeconds); + if (obj is GameModule module) + module.Update(elapseSeconds, realElapseSeconds); } } } diff --git a/Convention/[Architecture]/Modules/ActionManager.cs b/Convention/[Architecture]/Modules/ActionManager.cs new file mode 100644 index 0000000..672e645 --- /dev/null +++ b/Convention/[Architecture]/Modules/ActionManager.cs @@ -0,0 +1,111 @@ +using System; +using System.Collections.Generic; + +namespace Convention.Experimental.Modules +{ + public partial class ActionManager : PublicType.GameModule + { + public interface IActor + { + void Execute(); + void Undo(); + string Description { get; } + } + + private readonly object m_lock = new(); + private readonly LinkedList m_history = new(); + private readonly Stack m_redoStack = new(); + private int m_historyLimit = 100; + public int HistoryLimit + { + get => m_historyLimit; + set + { + m_historyLimit = value; + while (m_history.Count > m_historyLimit) + { + m_history.RemoveFirst(); + } + } + } + + public bool CanUndo => m_history.Count > 0; + + public int UndoCount => m_history.Count; + + public List GetHistoryDescription() + { + lock (m_lock) + { + var descriptions = new List(); + foreach (var actor in m_history) + { + descriptions.Add(actor.Description); + } + return descriptions; + } + } + + public bool CanRedo => m_redoStack.Count > 0; + + public int RedoCount => m_redoStack.Count; + + public List GetRedoDescription() + { + lock (m_lock) + { + var descriptions = new List(); + foreach (var actor in m_redoStack) + { + descriptions.Add(actor.Description); + } + return descriptions; + } + } + + public void Execute(IActor actor) + { + lock (m_lock) + { + actor.Execute(); + m_history.AddLast(actor); + m_redoStack.Clear(); + if (m_history.Count > m_historyLimit) + { + m_history.RemoveFirst(); + } + } + } + + public bool Undo() + { + lock (m_lock) + { + if (CanUndo) + { + var actor = m_history.Last.Value; + m_history.RemoveLast(); + actor.Undo(); + m_redoStack.Push(actor); + return true; + } + return false; + } + } + + public bool Redo() + { + lock (m_lock) + { + if (CanRedo) + { + var actor = m_redoStack.Pop(); + actor.Execute(); + m_history.AddLast(actor); + return true; + } + return false; + } + } + } +} diff --git a/Convention/[Architecture]/Modules/ConfigManager.cs b/Convention/[Architecture]/Modules/ConfigManager.cs index bb65e9b..f54764e 100644 --- a/Convention/[Architecture]/Modules/ConfigManager.cs +++ b/Convention/[Architecture]/Modules/ConfigManager.cs @@ -5,13 +5,13 @@ namespace Convention.Experimental.Modules { public class ConfigManager : PublicType.GameModule { - public readonly ProjectConfig m_ProjectConfig = new(); + public readonly ProjectConfig Config = new(); public bool IsSavePropertiesWhenShutdown = false; internal override void Shutdown() { if (IsSavePropertiesWhenShutdown) - m_ProjectConfig.SaveProperties(); + Config.SaveProperties(); } } } diff --git a/Convention/[Architecture]/Modules/ObjectPoolManager.cs b/Convention/[Architecture]/Modules/ObjectPoolManager.cs index 01d2fe3..06916fb 100644 --- a/Convention/[Architecture]/Modules/ObjectPoolManager.cs +++ b/Convention/[Architecture]/Modules/ObjectPoolManager.cs @@ -1,69 +1,333 @@ +using Convention.Experimental.PublicType; +using System; +using System.Linq; using System.Collections.Generic; using UnityEngine; namespace Convention.Experimental.Modules { - public class ObjectPoolManager : PublicType.GameModule + public class GameObjectPoolManager : GameModule { - public interface IPawn + /// + /// 继承该接口的Component将在回收到对象池时调用 + /// 中禁止调用 + /// + public interface IPoolPawn { void Release(); } - private readonly Dictionary> ObjectPools = new(); - private readonly Dictionary LeavePoolObjects = new(); + private readonly Dictionary> GameObjectPools = new(); + private readonly Dictionary LeaveGameObjectPoolObjects = new(); + private bool GameObjectPoolStatus = true; + /// + /// 检查是否存在指定的游戏对象池 + /// + /// 用做键的预制体 + /// + public bool Contains(GameObject prefab) + { + return GameObjectPools.ContainsKey(prefab); + } + + /// + /// 确保不会嵌套调用 + /// + /// + private void EnsureSafeStatus() + { + if (GameObjectPoolStatus == false) + { + throw new GameException("GameObjectPool is busy now."); + } + } + + /// + /// 生成游戏对象 + /// + /// + /// public GameObject Spawn(GameObject prefab) { - if (!ObjectPools.ContainsKey(prefab)) + EnsureSafeStatus(); + lock (GameObjectPools) { - ObjectPools[prefab] = new(); + if (!GameObjectPools.ContainsKey(prefab)) + { + GameObjectPools[prefab] = new(); + } + + var pool = GameObjectPools[prefab]; + GameObject instance; + if (pool.Count > 0) + { + instance = pool.Pop(); + instance.SetActive(true); + instance.transform.SetParent(null); + } + else + { + instance = GameObject.Instantiate(prefab); + } + + lock (LeaveGameObjectPoolObjects) + { + LeaveGameObjectPoolObjects[instance] = prefab; + } + + return instance; } - var pool = ObjectPools[prefab]; - GameObject instance; - if (pool.Count > 0) + } + + /// + /// 回收游戏对象 + /// + /// + /// + public void Unspawn(GameObject instance) + { + EnsureSafeStatus(); + lock (LeaveGameObjectPoolObjects) { - instance = pool.Pop(); - instance.SetActive(true); - instance.transform.SetParent(null); + if (LeaveGameObjectPoolObjects.TryGetValue(instance, out var prefab)) + { + var releaser = instance.GetComponents(); + GameObjectPoolStatus = false; + foreach (var r in releaser) + { + r.Release(); + } + GameObjectPoolStatus = true; + instance.SetActive(false); + instance.transform.SetParent(ConventionUtility.Singleton.transform); + lock (GameObjectPools) + { + GameObjectPools[prefab].Push(instance); + LeaveGameObjectPoolObjects.Remove(instance); + } + } + else + { + throw new PublicType.GameException("This object does not belong to any pool."); + } + } + } + + public void Clear(GameObject prefab) + { + EnsureSafeStatus(); + lock (GameObjectPools) + { + lock (LeaveGameObjectPoolObjects) + { + // 销毁池中对象 + if (GameObjectPools.TryGetValue(prefab, out var pool)) + { + while (pool.Count > 0) + { + var instance = pool.Pop(); + GameObject.Destroy(instance); + } + } + else + { + throw new PublicType.GameException("This pool does not exist."); + } + // 销毁未回收对象 + var unbackInstances = from item in LeaveGameObjectPoolObjects + where item.Value == prefab + select item.Key; + foreach (var instance in unbackInstances) + { + LeaveGameObjectPoolObjects.Remove(instance); + } + foreach (var instance in unbackInstances) + { + GameObject.Destroy(instance); + } + } + } + } + } + + public class ObjectPoolManager + { + /// + /// 继承该接口的Component将在回收到对象池时调用 + /// 中禁止调用 + /// + public interface IPoolPawn + { + void Release(); + } + + private readonly Dictionary ObjectPools = new(); + private readonly Dictionary LeaveObjectPoolObjects = new(); + + private bool GameObjectPoolStatus = true; + private void EnsureSafeStatus() + { + if (GameObjectPoolStatus == false) + { + throw new GameException("ObjectPool is busy now."); + } + } + + public bool Contains(Type type) + { + return ObjectPools.ContainsKey(type); + } + + public bool Contains() where T : class, new() + { + return ObjectPools.ContainsKey(typeof(T)); + } + + /// + /// 插入自行创建的对象到对象池 + /// + /// 期望类型 + /// 实例 + /// + /// + public object Insert(Type type, object value) + { + if(LeaveObjectPoolObjects.ContainsKey(value)) + { + throw new PublicType.GameException("This object is already registered in pool"); + } + if (!ObjectPools.ContainsKey(type)) + { + ObjectPools[type] = Utility.CreateStack(type); + } + var pool = ObjectPools[type]; + var methodInfo = pool.GetType().GetMethod("Push"); + methodInfo.Invoke(pool, new object[] { value }); + return value; + } + + /// + /// 自行创建对象并插入对象池 + /// + /// 期望类型 + /// 实例 + /// + /// + public T Insert(T value) where T : class + { + if(LeaveObjectPoolObjects.ContainsKey(value)) + { + throw new PublicType.GameException("This object is already registered in pool"); + } + var type = typeof(T); + if (!ObjectPools.ContainsKey(type)) + { + ObjectPools[type] = new Stack(); + } + var pool = (Stack)ObjectPools[type]; + pool.Push(value); + return value; + } + + /// + /// 捕获一般对象并返回实例 + /// + /// 期望类型 + /// + public object Summon(Type type) + { + if (!ObjectPools.ContainsKey(type)) + { + ObjectPools[type] = Utility.CreateStack(type); + } + var pool = ObjectPools[type]; + var methodInfo = pool.GetType().GetMethod("Pop"); + object instance; + var countProperty = pool.GetType().GetProperty("Count"); + var count = (int)countProperty.GetValue(pool); + if (count > 0) + { + instance = methodInfo.Invoke(pool, null); } else { - instance = GameObject.Instantiate(prefab); + instance = Activator.CreateInstance(type); } - LeavePoolObjects[instance] = prefab; + LeaveObjectPoolObjects[instance] = type; return instance; } - public void BackPool(GameObject instance) + /// + /// 捕获一般对象并返回实例 + /// + /// 期望类型 + /// + public T Summon() where T : class, new() { - if(LeavePoolObjects.TryGetValue(instance,out var prefab)) + var type = typeof(T); + if (!ObjectPools.ContainsKey(type)) { - var releaser = instance.GetComponents(); - foreach (var r in releaser) - { - r.Release(); - } - instance.SetActive(false); - instance.transform.SetParent(ConventionUtility.Singleton.transform); - ObjectPools[prefab].Push(instance); - LeavePoolObjects.Remove(instance); + ObjectPools[type] = new Stack(); + } + var pool = (Stack)ObjectPools[type]; + T instance; + if (pool.Count > 0) + { + instance = pool.Pop(); } else { - throw new PublicType.GameException("This object does not belong to any pool."); + instance = new T(); + } + LeaveObjectPoolObjects[instance] = type; + return instance; + } + + /// + /// 回收对象 + /// + /// 实例 + /// + public void Reclaim(object instance) + { + if (LeaveObjectPoolObjects.TryGetValue(instance, out var objType)) + { + var pool = ObjectPools[objType]; + var methodInfo = pool.GetType().GetMethod("Push"); + if(instance is IPoolPawn pawn) + { + pawn.Release(); + } + methodInfo.Invoke(pool, new object[] { instance }); + LeaveObjectPoolObjects.Remove(instance); + } + else + { + throw new GameException("This object does not belong to any pool."); } } - public void ClearPool(GameObject prefab) + public void Clear(Type type) { - if (ObjectPools.TryGetValue(prefab, out var pool)) + if (ObjectPools.TryGetValue(type, out var pool)) { - while (pool.Count > 0) - { - var instance = pool.Pop(); - GameObject.Destroy(instance); - } + var clearMethod = pool.GetType().GetMethod("Clear"); + clearMethod.Invoke(pool, null); + } + else + { + throw new PublicType.GameException("This pool does not exist."); + } + } + + public void Clear() where T : class, new() + { + var type = typeof(T); + if (ObjectPools.TryGetValue(type, out var pool)) + { + var clearMethod = pool.GetType().GetMethod("Clear"); + clearMethod.Invoke(pool, null); } else { diff --git a/Convention/[Runtime]/Config.cs b/Convention/[Runtime]/Config.cs index 361ce2c..43631ab 100644 --- a/Convention/[Runtime]/Config.cs +++ b/Convention/[Runtime]/Config.cs @@ -2475,7 +2475,62 @@ namespace Convention namespace Convention { - public static partial class ConventionUtility + public static partial class Utility + { + /// + /// 鐢熸垚鏈熸湜绫诲瀷鐨勬爤瀹炰緥 + /// + /// 鏈熸湜绫诲瀷 + /// + public static object CreateStack([In] Type type) + { + var stackType = typeof(Stack<>).MakeGenericType(type); + var stackInstance = Activator.CreateInstance(stackType); + return stackInstance; + } + + /// + /// 鐢熸垚鏈熸湜绫诲瀷鐨勯槦鍒楀疄渚 + /// + /// 鏈熸湜绫诲瀷 + /// + public static object CreateQueue([In] Type type) + { + var queueType = typeof(Queue<>).MakeGenericType(type); + var queueInstance = Activator.CreateInstance(queueType); + return queueInstance; + } + + /// + /// 鐢熸垚鏈熸湜绫诲瀷鐨勫垪琛ㄥ疄渚 + /// + /// 鏈熸湜绫诲瀷 + /// + public static object CreateList([In] Type type) + { + var listType = typeof(List<>).MakeGenericType(type); + var listInstance = Activator.CreateInstance(listType); + return listInstance; + } + + /// + /// 鐢熸垚鏈熸湜閿煎绫诲瀷鐨勫瓧鍏稿疄渚 + /// + /// 鏈熸湜閿被鍨 + /// 鏈熸湜鍊肩被鍨 + /// + public static object CreateDictionary([In] Type keyType, [In] Type valueType) + { + var dictType = typeof(Dictionary<,>).MakeGenericType(keyType, valueType); + var dictInstance = Activator.CreateInstance(dictType); + return dictInstance; + } + } +} + +namespace Convention.Collections +{ + namespace Generic { } -} \ No newline at end of file +}