| using System; | 
| using System.Collections; | 
| using System.Collections.Generic; | 
| using UnityEngine; | 
| using System.Linq; | 
| using DG.Tweening; | 
|   | 
| /// <summary> | 
| /// UI管理器 - 负责管理所有UI界面的显示、隐藏和层级 | 
| /// </summary> | 
| public class UIManager : ManagerBase<UIManager> | 
| { | 
|     #region 常量和枚举 | 
|      | 
|     // 基础排序顺序 | 
|     private const int BASE_SORTING_ORDER = 10; | 
|      | 
|     #endregion | 
|   | 
|     #region 字段和属性 | 
|      | 
|     // UI根节点 | 
|     private Transform uiRoot; | 
|      | 
|     // 各层级的Transform | 
|     private Transform staticTrans; | 
|     private Transform bottomTrans; | 
|     private Transform midTrans; | 
|     private Transform systemTrans; | 
|     private Transform loadingTrans; | 
|   | 
|     // UI字典,存储所有已加载的UI,键为UI名称,值为UI实例 | 
| #if UNITY_EDITOR | 
|     public | 
| #else | 
|     private | 
| #endif  | 
|     Dictionary<string, List<UIBase>> uiDict = new Dictionary<string, List<UIBase>>(); | 
|   | 
|     // 存储关闭但未销毁的UI,键为UI名称,值为UI实例 | 
|     private Dictionary<string, List<UIBase>> closedUIDict = new Dictionary<string, List<UIBase>>(); | 
|   | 
|     // UI栈,用于管理UI的显示顺序 | 
|     private Stack<UIBase> uiStack = new Stack<UIBase>(); | 
|      | 
|     // 当前最高的排序顺序 | 
|     private int currentHighestSortingOrder = 0; | 
|      | 
|     // 当前回合数,用于记录UI的使用情况 | 
|     private int currentRound = 0; | 
|      | 
|     // 缓存层级对应的排序顺序 | 
|     private static Dictionary<UILayer, int> layerSortingOrderCache = new Dictionary<UILayer, int>(); | 
|      | 
|     // 缓存层级对应的Transform | 
|     private Dictionary<UILayer, Transform> layerTransformCache = new Dictionary<UILayer, Transform>(); | 
|      | 
|     // UI实例计数器,用于为同类型UI生成唯一标识 | 
|     private Dictionary<string, int> uiInstanceCounter = new Dictionary<string, int>(); | 
|      | 
|     // 上次检查时间 | 
|     private float lastCheckTime = 0f; | 
|     // 检查间隔(秒) | 
|     private const float CHECK_INTERVAL = 5f; | 
|      | 
|     public Action<UIBase> OnOpenWindow; | 
|   | 
|     public Action<UIBase> OnCloseWindow; | 
|   | 
|     #endregion | 
|   | 
|     #region 初始化 | 
|   | 
|     /// <summary> | 
|     /// 初始化UI管理器 | 
|     /// </summary> | 
|     public override void Init() | 
|     { | 
|         base.Init(); | 
|   | 
|         // 初始化UI根节点 | 
|         InitUIRoot(); | 
|   | 
|         // 初始化缓存 | 
|         layerSortingOrderCache.Clear(); | 
|         layerTransformCache.Clear(); | 
|         uiInstanceCounter.Clear(); | 
|   | 
|         DOTween.SetTweensCapacity(500, 50); | 
|         Debug.Log("UI管理器初始化完成"); | 
|          | 
|     } | 
|      | 
|     /// <summary> | 
|     /// 初始化UI根节点 | 
|     /// </summary> | 
|     private void InitUIRoot() | 
|     { | 
|         // 查找UI根节点 | 
|         GameObject root = GameObject.Find("UIRoot"); | 
|          | 
|         // 如果场景中没有UI根节点,则创建一个 | 
|         if (root == null) | 
|         { | 
|             root = GameObject.Instantiate(BuiltInLoader.LoadPrefab("UIRoot")); | 
|             root.name = "UIRoot"; | 
|             if (root == null) | 
|             { | 
|                 Debug.LogError("无法找到UI根节点"); | 
|                 return; | 
|             } | 
|   | 
|             // 添加DontDestroyOnLoad组件,确保UI根节点在场景切换时不被销毁 | 
|             GameObject.DontDestroyOnLoad(root); | 
|         } | 
|   | 
|         uiRoot = root.transform; | 
|         uiRoot.position = Vector3.zero; | 
|   | 
|         staticTrans = uiRoot.Find("Static"); | 
|         bottomTrans = uiRoot.Find("Bottom"); | 
|         midTrans = uiRoot.Find("Middle"); | 
|         loadingTrans = uiRoot.Find("Loading"); | 
|         systemTrans = uiRoot.Find("System"); | 
|   | 
|         layerTransformCache.Clear(); | 
|         layerTransformCache.Add(UILayer.Static, staticTrans); | 
|         layerTransformCache.Add(UILayer.Bottom, bottomTrans); | 
|         layerTransformCache.Add(UILayer.Mid, midTrans); | 
|         layerTransformCache.Add(UILayer.System, systemTrans); | 
|         layerTransformCache.Add(UILayer.Loading, loadingTrans); | 
|     } | 
|      | 
|     public Transform GetUIRoot() | 
|     { | 
|         return uiRoot; | 
|     } | 
|      | 
|     #endregion | 
|   | 
|     #region 辅助方法 | 
|   | 
|     // 获取UI层级对应的基础排序顺序 | 
|     public static int GetBaseSortingOrderForLayer(UILayer layer) | 
|     { | 
|         // 尝试从缓存中获取排序顺序 | 
|         if (layerSortingOrderCache.TryGetValue(layer, out int order)) | 
|             return order; | 
|   | 
|         // 如果缓存中没有,使用原来的方法计算并缓存结果 | 
|         int result; | 
|         switch (layer) | 
|         { | 
|             case UILayer.Static: | 
|                 result = BASE_SORTING_ORDER; | 
|                 break; | 
|             case UILayer.Bottom: | 
|                 result = BASE_SORTING_ORDER * 100; | 
|                 break; | 
|             case UILayer.Mid: | 
|                 result = BASE_SORTING_ORDER * 1000; | 
|                 break; | 
|             case UILayer.System: | 
|                 result = BASE_SORTING_ORDER * 2000; | 
|                 break; | 
|             case UILayer.Loading: | 
|                 result = BASE_SORTING_ORDER * 3000; | 
|                 break; | 
|             default: | 
|                 result = BASE_SORTING_ORDER * 10; | 
|                 break; | 
|         } | 
|   | 
|         // 将结果存入缓存 | 
|         layerSortingOrderCache[layer] = result; | 
|         return result; | 
|     } | 
|   | 
|     // 获取层级对应的Transform | 
|     public Transform GetTransForLayer(UILayer layer) | 
|     { | 
|         // 尝试从缓存中获取Transform | 
|         if (layerTransformCache.TryGetValue(layer, out Transform trans)) | 
|             return trans; | 
|              | 
|         // 如果缓存中没有,使用原来的方法获取并缓存结果 | 
|         Transform result; | 
|         switch (layer) | 
|         { | 
|             case UILayer.Static: | 
|                 result = staticTrans; | 
|                 break; | 
|             case UILayer.Bottom: | 
|                 result = bottomTrans; | 
|                 break; | 
|             case UILayer.Mid: | 
|                 result = midTrans; | 
|                 break; | 
|             case UILayer.System: | 
|                 result = systemTrans; | 
|                 break; | 
|             case UILayer.Loading: | 
|                 result = loadingTrans; | 
|                 break; | 
|             default: | 
|                 result = bottomTrans; | 
|                 break; | 
|         } | 
|          | 
|         // 将结果存入缓存 | 
|         layerTransformCache[layer] = result; | 
|         return result; | 
|     } | 
|      | 
|     // 获取UI实例,如果不存在则返回null | 
|     public T GetUI<T>() where T : UIBase | 
|     { | 
|         // 获取UI类型名称 | 
|         string uiName = typeof(T).Name; | 
|         if (string.IsNullOrEmpty(uiName)) | 
|         { | 
|             // 记录错误日志 | 
|             Debug.LogError("UI名称为空"); | 
|             return null; | 
|         } | 
|   | 
|         // 尝试从字典中获取UI实例列表 | 
|         if (uiDict.TryGetValue(uiName, out List<UIBase> uiList) && uiList.Count > 0) | 
|         { | 
|             // 返回第一个实例 | 
|             return uiList[0] as T; | 
|         } | 
|   | 
|         // 如果不存在,返回null | 
|         return null; | 
|     } | 
|   | 
|     public UIBase GetUI(string uiName) | 
|     { | 
|         if (string.IsNullOrEmpty(uiName)) | 
|         { | 
|             // 记录错误日志 | 
|             Debug.LogError("UI名称为空"); | 
|             return null; | 
|         } | 
|   | 
|         // 尝试从字典中获取UI实例列表 | 
|         if (uiDict.TryGetValue(uiName, out List<UIBase> uiList) && uiList.Count > 0) | 
|         { | 
|             // 返回第一个实例 | 
|             return uiList[0]; | 
|         } | 
|   | 
|         // 如果不存在,返回null | 
|         return null; | 
|     } | 
|   | 
|     public List<T> GetUIList<T>() where T : UIBase | 
|     { | 
|         List<T> uiList = new List<T>(); | 
|   | 
|         // 获取UI类型名称 | 
|         string uiName = typeof(T).Name; | 
|         if (string.IsNullOrEmpty(uiName)) | 
|         { | 
|             // 记录错误日志 | 
|             Debug.LogError("UI名称为空"); | 
|             return uiList; | 
|         } | 
|   | 
|         // 尝试从字典中获取UI实例列表 | 
|         List<UIBase> tempList = null; | 
|         uiDict.TryGetValue(uiName, out tempList); | 
|   | 
|         if (tempList != null) | 
|         { | 
|             for (int i = 0; i < tempList.Count; i++) | 
|             { | 
|                 UIBase ui = tempList[i]; | 
|                 if (null != ui) | 
|                 { | 
|                     uiList.Add(ui as T); | 
|                 } | 
|             } | 
|         } | 
|   | 
|         return uiList; | 
|     } | 
|   | 
|     public bool IsOpenedInList<T>() where T : UIBase | 
|     { | 
|         List<T> uiList = GetUIList<T>(); | 
|   | 
|         foreach (T ui in uiList) | 
|         { | 
|             if (ui.IsActive()) | 
|             { | 
|                 return true; | 
|             } | 
|         } | 
|   | 
|         return false; | 
|     } | 
|   | 
|     public bool IsOpened<T>() where T : UIBase | 
|     { | 
|         T ui = GetUI<T>(); | 
|   | 
|         if (null != ui) | 
|         { | 
|             return ui.IsActive(); | 
|         } | 
|   | 
|         return false; | 
|     } | 
|   | 
|     public bool IsOpened(string uiName) | 
|     { | 
|         UIBase ui = GetUI(uiName); | 
|   | 
|         if (null != ui) | 
|         { | 
|             return ui.IsActive(); | 
|         } | 
|   | 
|         return false; | 
|     } | 
|      | 
|     // 检查是否存在任何全屏或遮罩窗口, 暂未定义全屏可以统一勾选openMask | 
|     public bool ExistAnyFullScreenOrMaskWin(string excludeUIName) | 
|     { | 
|         var exist = false; | 
|         foreach (var uiList in uiDict.Values) | 
|         { | 
|             // 遍历该类型的所有UI实例 | 
|             foreach (var ui in uiList) | 
|             { | 
|                 // 刷新UI | 
|                 if (ui.IsActive() && ui.name != excludeUIName) | 
|                 { | 
|                     if (ui.openMask) | 
|                     { | 
|                         exist = true; | 
|                         break; | 
|                     } | 
|                 } | 
|             } | 
|         } | 
|   | 
|         return exist; | 
|     } | 
|      | 
|     // 获取指定类型的所有UI实例 | 
|     public List<T> GetAllUI<T>() where T : UIBase | 
|     { | 
|         // 获取UI类型名称 | 
|         string uiName = typeof(T).Name; | 
|         if (string.IsNullOrEmpty(uiName)) | 
|         { | 
|             // 记录错误日志 | 
|             Debug.LogError("UI名称为空"); | 
|             return new List<T>(); | 
|         } | 
|   | 
|         // 尝试从字典中获取UI实例列表 | 
|         if (uiDict.TryGetValue(uiName, out List<UIBase> uiList) && uiList.Count > 0) | 
|         { | 
|             // 将列表中的所有实例转换为指定类型并返回 | 
|             return uiList.Cast<T>().ToList(); | 
|         } | 
|   | 
|         // 如果不存在,返回空列表 | 
|         return new List<T>(); | 
|     } | 
|      | 
|     // 更新父级UI的回合数 | 
|     private void UpdateParentUIRounds(UIBase ui) | 
|     { | 
|         // 如果UI为空或不支持父子关系,直接返回 | 
|         if (ui == null || !ui.supportParentChildRelation) | 
|             return; | 
|              | 
|         // 获取父级UI | 
|         UIBase parentUI = ui.parentUI; | 
|         // 遍历所有父级UI,更新回合数 | 
|         while (parentUI != null) | 
|         { | 
|             // 更新父级UI的最后使用回合数 | 
|             parentUI.lastUsedRound = currentRound; | 
|             // 继续向上查找父级UI | 
|             parentUI = parentUI.parentUI; | 
|         } | 
|     } | 
|      | 
|     // 递归收集所有子UI | 
|     private void CollectChildrenUI(UIBase ui, List<UIBase> result) | 
|     { | 
|         // 如果UI为空或没有子UI或不支持父子关系,直接返回 | 
|         if (ui == null || !ui.supportParentChildRelation || ui.childrenUI == null || ui.childrenUI.Count == 0) | 
|             return; | 
|          | 
|         // 遍历所有子UI | 
|         foreach (var childUI in ui.childrenUI) | 
|         { | 
|             // 如果结果列表中不包含当前子UI,则添加 | 
|             if (!result.Contains(childUI)) | 
|             { | 
|                 // 添加到结果列表 | 
|                 result.Add(childUI); | 
|                 // 递归收集子UI的子UI | 
|                 CollectChildrenUI(childUI, result); | 
|             } | 
|         } | 
|     } | 
|      | 
|     /// <summary> | 
|     /// 检查并关闭长时间未使用的UI | 
|     /// </summary> | 
|     public void CheckAndCloseIdleUI() | 
|     { | 
|         // 如果没有UI,直接返回 | 
|         if (uiDict.Count == 0 && closedUIDict.Count == 0) | 
|             return; | 
|              | 
|         // 创建需要关闭的UI列表 | 
|         List<UIBase> uiToClose = new List<UIBase>(); | 
|          | 
|         // 遍历所有活跃UI | 
|         foreach (var uiList in uiDict.Values) | 
|         { | 
|             foreach (var ui in uiList) | 
|             { | 
|                 // 如果UI是持久化的,跳过 | 
|                 if (ui.isPersistent) | 
|                     continue; | 
|                      | 
|                 if (ui.IsActive()) | 
|                     continue; | 
|   | 
|                 // 计算UI空闲的回合数 | 
|                 int idleRounds = currentRound - ui.lastUsedRound; | 
|                  | 
|                 // 如果空闲回合数超过最大空闲回合数,添加到关闭列表 | 
|                 if (idleRounds > ui.maxIdleRounds) | 
|                 { | 
|                     uiToClose.Add(ui); | 
|                 } | 
|             } | 
|         } | 
|          | 
|         // 遍历所有关闭的UI | 
|         List<string> emptyKeys = new List<string>(); | 
|         foreach (var kvp in closedUIDict) | 
|         { | 
|             string uiName = kvp.Key; | 
|             List<UIBase> uiList = kvp.Value; | 
|             List<UIBase> uiToRemove = new List<UIBase>(); | 
|              | 
|             foreach (var ui in uiList) | 
|             { | 
|                 // 计算UI空闲的回合数 | 
|                 int idleRounds = currentRound - ui.lastUsedRound; | 
|                  | 
|                 // 如果空闲回合数超过最大空闲回合数,添加到关闭列表 | 
|                 if (idleRounds > ui.maxIdleRounds) | 
|                 { | 
|                     uiToClose.Add(ui); | 
|                     uiToRemove.Add(ui); | 
|                 } | 
|             } | 
|              | 
|             // 从关闭列表中移除需要销毁的UI | 
|             foreach (var ui in uiToRemove) | 
|             { | 
|                 uiList.Remove(ui); | 
|             } | 
|              | 
|             // 如果列表为空,记录需要从字典中移除的键 | 
|             if (uiList.Count == 0) | 
|             { | 
|                 emptyKeys.Add(uiName); | 
|             } | 
|         } | 
|          | 
|         // 从字典中移除空列表 | 
|         foreach (var key in emptyKeys) | 
|         { | 
|             closedUIDict.Remove(key); | 
|         } | 
|          | 
|         // 销毁所有需要关闭的UI | 
|         foreach (var ui in uiToClose) | 
|         { | 
|             // 记录日志 | 
| #if UNITY_EDITOR | 
|             Debug.Log($"销毁长时间未使用的UI: {ui.uiName}, 空闲回合数: {currentRound - ui.lastUsedRound}"); | 
| #endif | 
|             // 销毁UI对象 | 
|             GameObject.Destroy(ui.gameObject); | 
|         } | 
|     } | 
|      | 
|     // // 生成UI实例的唯一标识 | 
|     // private string GenerateUIInstanceID(string uiName) | 
|     // { | 
|     //     // 如果计数器中不存在该UI类型,初始化为0 | 
|     //     if (!uiInstanceCounter.ContainsKey(uiName)) | 
|     //     { | 
|     //         uiInstanceCounter[uiName] = 0; | 
|     //     } | 
|          | 
|     //     // 递增计数器 | 
|     //     uiInstanceCounter[uiName]++; | 
|          | 
|     //     // 返回带有计数的唯一标识 | 
|     //     return $"{uiName}_{uiInstanceCounter[uiName]}"; | 
|     // } | 
|      | 
|     #endregion | 
|   | 
|     #region UI资源管理 | 
|   | 
|     private UIBase LoadUIResource(string uiName) | 
|     { | 
|          | 
|         // 从资源管理器加载UI预制体 | 
|         GameObject prefab; | 
|         if (uiName == "LaunchWin" || uiName == "DownLoadWin" || uiName == "RequestSecretWin" || uiName == "GameAgeWarnWin") | 
|         { | 
|             prefab = BuiltInLoader.LoadPrefab(uiName); | 
|         } | 
|         else | 
|         { | 
|             prefab = ResManager.Instance.LoadAsset<GameObject>("UI", uiName); | 
|         } | 
|   | 
|         // 检查预制体是否加载成功 | 
|         if (prefab == null) | 
|         { | 
|             // 记录错误日志 | 
|             Debug.LogError($"加载UI预制体失败: {uiName}"); | 
|             return null; | 
|         } | 
|   | 
|         // 实例化UI对象 | 
|         GameObject uiObject = GameObject.Instantiate(prefab); | 
|         // 设置对象名称 | 
|         uiObject.name = uiName; | 
|   | 
|         // 通过uiName映射Type | 
|         Type uiType = Type.GetType(uiName); | 
|         if (uiType == null) | 
|         { | 
|             Debug.LogError($"找不到UI类型: {uiName}"); | 
|             return null; | 
|         } | 
|   | 
|         // 获取UI基类组件 | 
|         UIBase uiBase = uiObject.GetComponent(uiType) as UIBase; | 
|   | 
|         // 检查UI基类组件是否存在 | 
|         if (uiBase == null) | 
|         { | 
|             // 记录错误日志 | 
|             Debug.LogError($"UI预制体 {uiName} 没有 UIBase 组件或类型不匹配"); | 
|             return null; | 
|         } | 
|   | 
|         // 设置UI名称 | 
|         uiBase.uiName = uiName; | 
|   | 
|         // 设置父节点为UI根节点 | 
|         Transform parentTrans = GetTransForLayer(uiBase.uiLayer); | 
|   | 
|         uiObject.transform.SetParent(parentTrans, false); | 
|   | 
|         // 设置排序顺序 | 
|         int baseSortingOrder = GetBaseSortingOrderForLayer(uiBase.uiLayer); | 
|         uiBase.SetSortingOrder(baseSortingOrder); | 
|   | 
|         return uiBase; | 
|     } | 
|   | 
|     // 加载UI预制体 | 
|     private T LoadUIResource<T>(string uiName) where T : UIBase | 
|     { | 
|         return LoadUIResource(uiName) as T; | 
|     } | 
|      | 
|     #endregion | 
|   | 
|     #region UI排序管理 | 
|      | 
|     // 更新UI排序顺序 | 
|     private void UpdateUISortingOrder() | 
|     { | 
|         // 重置当前最高排序顺序 | 
|         currentHighestSortingOrder = 0; | 
|          | 
|         // 遍历UI栈,设置排序顺序 | 
|         UIBase[] uiArray = new UIBase[uiStack.Count]; | 
|         uiStack.CopyTo(uiArray, 0); | 
|          | 
|         // 先按照UILayer进行排序,然后再按照栈顺序排序 | 
|         Dictionary<UIBase, int> uiOrderDict = new Dictionary<UIBase, int>(); | 
|         for (int i = 0; i < uiArray.Length; i++) | 
|         { | 
|             uiOrderDict[uiArray[i]] = i; | 
|         } | 
|   | 
|         Array.Sort(uiArray, (a, b) => | 
|         { | 
|             int layerCompare = a.uiLayer.CompareTo(b.uiLayer); | 
|             if (layerCompare != 0) | 
|                 return layerCompare; | 
|   | 
|             return uiOrderDict[b].CompareTo(uiOrderDict[a]); | 
|         }); | 
|          | 
|         // 遍历排序后的UI数组,设置排序顺序 | 
|         foreach (var ui in uiArray) | 
|         { | 
|             // 获取基础排序顺序 | 
|             int baseSortingOrder = GetBaseSortingOrderForLayer(ui.uiLayer); | 
|             // 计算当前UI的排序顺序 | 
|             int sortingOrder = baseSortingOrder + currentHighestSortingOrder; | 
|             // 设置UI的排序顺序 | 
|             ui.SetSortingOrder(sortingOrder); | 
|             // 更新当前最高排序顺序 | 
|             currentHighestSortingOrder += ui.uiLayer == UILayer.Static ? 155/*这里是角色+特效之上的层级*/ : 10; | 
|   | 
|             // Debug.Log(ui.uiName + " order is " + sortingOrder + " " + currentHighestSortingOrder); | 
|         } | 
|     } | 
|      | 
|     #endregion | 
|   | 
|     #region UI操作 | 
|   | 
|   | 
|   | 
|     private UIBase GetLastSupportParentChildRelationUI() | 
|     { | 
|   | 
|         foreach (var uiBase in uiStack) | 
|         { | 
|             if (uiBase != null && uiBase.supportParentChildRelation) | 
|             { | 
|                 return uiBase; | 
|             } | 
|         } | 
|   | 
|         return null; | 
|     } | 
|   | 
|     public UIBase OpenWindow(string uiName, int functionOrder = 0) | 
|     { | 
|         // 优先从closedUIDict中获取 | 
|         UIBase parentUI = null; | 
|   | 
|         UIBase returnValue = null; | 
|   | 
|         List<UIBase> closedUIList = new List<UIBase>(); | 
|   | 
|         if (closedUIDict.TryGetValue(uiName, out closedUIList) && closedUIList.Count > 0) | 
|         { | 
|             #if UNITY_EDITOR | 
|             Debug.Log("OpenWindow getFromClosedDict " + uiName); | 
|             #endif | 
|   | 
|             returnValue = closedUIList[0] as UIBase; | 
|             closedUIList.RemoveAt(0); | 
|              | 
|             if (closedUIList.Count == 0) | 
|             { | 
|                 closedUIDict.Remove(uiName); | 
|             } | 
|         } | 
|         else | 
|         { | 
|             #if UNITY_EDITOR | 
|             Debug.Log("OpenWindow getNewLoad " + uiName); | 
|             #endif | 
|             returnValue = LoadUIResource(uiName); | 
|             if (returnValue == null) | 
|             { | 
|                 // 记录错误日志 | 
|                 Debug.LogError($"打开UI失败: {uiName}"); | 
|                 return null; | 
|             } | 
|         } | 
|          | 
|         returnValue.gameObject.SetActive(true); | 
|          | 
|         // 自动设置父级UI, 如果勾选了ismainui 则不需要找父级UI | 
|         if (returnValue.supportParentChildRelation && uiStack.Count > 0 && !returnValue.isMainUI) | 
|         { | 
|             // 获取栈顶UI | 
|             parentUI = GetLastSupportParentChildRelationUI(); | 
|         } | 
|          | 
|         // 设置父级UI | 
|         if (parentUI != null) | 
|         { | 
|             // 设置父子关系 | 
|             returnValue.parentUI = parentUI; | 
|             if (parentUI.childrenUI == null) | 
|             { | 
|                 // 初始化父级UI的子UI列表 | 
|                 parentUI.childrenUI = new List<UIBase>(); | 
|             } | 
|             // 添加到父级UI的子UI列表 | 
|             parentUI.childrenUI.Add(returnValue); | 
|         } | 
|          | 
|         // 更新回合数 | 
|         currentRound++; | 
|         // 设置UI的最后使用回合数 | 
|         returnValue.lastUsedRound = currentRound; | 
|         // 更新父级UI的回合数 | 
|         UpdateParentUIRounds(returnValue); | 
|          | 
|         // 将UI添加到字典中 | 
|         if (!uiDict.ContainsKey(uiName)) | 
|         { | 
|             // 如果字典中不存在该类型的UI,创建新列表 | 
|             uiDict[uiName] = new List<UIBase>(); | 
|         } | 
|         // 添加到UI列表 | 
|         uiDict[uiName].Add(returnValue); | 
|   | 
| #if UNITY_EDITOR | 
|         if (uiDict[uiName].Count > 5) | 
|         {  | 
|             Debug.LogError("已打开" + uiDict[uiName].Count + "个界面:" + uiName); | 
|         } | 
| #endif | 
|   | 
|         // 将UI添加到栈中 | 
|         uiStack.Push(returnValue); | 
|          | 
|         // 更新UI排序顺序 | 
|         UpdateUISortingOrder(); | 
|          | 
|         // 打开UI | 
|         returnValue.functionOrder = functionOrder; | 
|         returnValue.HandleOpen(); | 
|   | 
|         OnOpenWindow?.Invoke(returnValue); | 
|          | 
|         // 检查并关闭长时间未使用的UI | 
|         CheckAndCloseIdleUI(); | 
|          | 
|         return returnValue; | 
|     } | 
|   | 
|     /// <summary> | 
|     /// 打开UI | 
|     /// </summary> | 
|     public T OpenWindow<T>(int functionOrder = 0) where T : UIBase | 
|     { | 
|         // 获取UI类型名称 | 
|         string uiName = typeof(T).Name; | 
|         return OpenWindow(uiName, functionOrder) as T; | 
|     } | 
|      | 
|     /// <summary> | 
|     /// 关闭UI | 
|     /// </summary> | 
|     public void CloseWindow<T>(bool destroy = false) where T : UIBase | 
|     { | 
|         // 获取UI类型名称 | 
|         string uiName = typeof(T).Name; | 
|          | 
|         CloseWindow(uiName, destroy); | 
|          | 
|     } | 
|   | 
|     public void CloseWindow(string uiName, bool destroy = false) | 
|     { | 
|         // 检查UI是否存在 | 
|         if (!uiDict.ContainsKey(uiName) || uiDict[uiName].Count == 0) | 
|         { | 
|             // 记录警告日志 | 
|             Debug.LogWarning($"尝试关闭不存在的UI: {uiName}"); | 
|             return; | 
|         } | 
|          | 
|         // 获取第一个UI实例 | 
|         UIBase ui = uiDict[uiName][0]; | 
|          | 
|         // 关闭UI | 
|         CloseWindow(ui, destroy); | 
|     } | 
|      | 
|     /// <summary> | 
|     /// 关闭指定的UI实例 | 
|     /// </summary> | 
|     public void CloseWindow(UIBase ui, bool destroy = false) | 
|     { | 
|         // 检查UI是否为空 | 
|         if (ui == null) | 
|         { | 
|             // 记录警告日志 | 
|             Debug.LogWarning("尝试关闭空UI"); | 
|             return; | 
|         } | 
|   | 
|         if (!ui.IsActive()) | 
|         { | 
|             return; | 
|         } | 
|          | 
|         // 获取UI类型名称 | 
|         string uiName = ui.uiName; | 
| #if UNITY_EDITOR | 
|         Debug.Log("CloseWindow " + uiName + " destroy : " + destroy.ToString()); | 
| #endif | 
|   | 
|         // 收集所有子UI | 
|         List<UIBase> childrenUI = new List<UIBase>(); | 
|         if (ui.supportParentChildRelation) | 
|         { | 
|             CollectChildrenUI(ui, childrenUI); | 
|         } | 
|          | 
|         // 先关闭所有子UI | 
|         foreach (var childUI in childrenUI) | 
|         { | 
|             // 关闭子UI | 
|             CloseWindow(childUI, destroy); | 
|         } | 
|          | 
|         // 从栈中移除UI | 
|         Stack<UIBase> tempStack = new Stack<UIBase>(); | 
|         while (uiStack.Count > 0) | 
|         { | 
|             // 弹出栈顶UI | 
|             UIBase tempUI = uiStack.Pop(); | 
|             // 如果不是要关闭的UI,则保存到临时栈中 | 
|             if (tempUI != ui) | 
|             { | 
|                 tempStack.Push(tempUI); | 
|             } | 
|         } | 
|          | 
|         // 将临时栈中的UI重新压入栈中 | 
|         while (tempStack.Count > 0) | 
|         { | 
|             uiStack.Push(tempStack.Pop()); | 
|         } | 
|          | 
|         // 从字典中移除UI | 
|         if (uiDict.ContainsKey(uiName)) | 
|         { | 
|             // 从UI列表中移除 | 
|             uiDict[uiName].Remove(ui); | 
|             // 如果列表为空,从字典中移除该类型 | 
|             if (uiDict[uiName].Count == 0) | 
|             { | 
|                 uiDict.Remove(uiName); | 
|             } | 
|         } | 
|          | 
|         // 从父级UI的子UI列表中移除 | 
|         if (ui.supportParentChildRelation && ui.parentUI != null && ui.parentUI.childrenUI != null) | 
|         { | 
|             // 从父级UI的子UI列表中移除 | 
|             ui.parentUI.childrenUI.Remove(ui); | 
|         } | 
|          | 
|         // 关闭UI | 
|         ui.HandleClose(); | 
|         OnCloseWindow?.Invoke(ui); | 
|   | 
|         if (destroy) | 
|         { | 
|             // 销毁UI对象 | 
|             GameObject.Destroy(ui.gameObject); | 
|         } | 
|         else | 
|         { | 
|             // 添加到closedUIDict | 
|             if (!closedUIDict.ContainsKey(uiName)) | 
|             { | 
|                 closedUIDict[uiName] = new List<UIBase>(); | 
|             } | 
|             closedUIDict[uiName].Add(ui); | 
|   | 
| #if UNITY_EDITOR | 
|             Debug.Log("CloseWindow " + uiName + " destroy : " + destroy.ToString() + " push to closedUIDict"); | 
| #endif | 
|   | 
|             // 隐藏UI (交给handle close内部自己去做) | 
|             // ui.gameObject.SetActive(false); | 
|         } | 
|          | 
|         // 更新UI排序顺序 | 
|         UpdateUISortingOrder(); | 
|     } | 
|      | 
|     /// <summary> | 
|     /// 关闭指定类型的所有UI实例 | 
|     /// </summary> | 
|     public void CloseAllWindows<T>() where T : UIBase | 
|     { | 
|         // 获取UI类型名称 | 
|         string uiName = typeof(T).Name; | 
|          | 
|         // 检查UI是否存在 | 
|         if (!uiDict.ContainsKey(uiName) || uiDict[uiName].Count == 0) | 
|         { | 
|             // 记录警告日志 | 
|             Debug.LogWarning($"尝试关闭不存在的UI: {uiName}"); | 
|             return; | 
|         } | 
|          | 
|         // 创建UI实例列表的副本,因为在关闭过程中会修改原列表 | 
|         List<UIBase> uiListCopy = new List<UIBase>(uiDict[uiName]); | 
|          | 
|         // 关闭所有UI实例 | 
|         foreach (var ui in uiListCopy) | 
|         { | 
|             // 关闭UI实例 | 
|             CloseWindow(ui, false); | 
|         } | 
|     } | 
|   | 
|     public void ReturnMainUI() | 
|     { | 
|         List<UIBase> allUI = new List<UIBase>(uiStack); | 
|   | 
|         for (int i = 0; i < allUI.Count; i++) | 
|         { | 
|             UIBase uiBase = allUI[i]; | 
|             if (!uiBase.isMainUI && uiBase.uiLayer < UILayer.System) | 
|             { | 
|                 CloseWindow(uiBase); | 
|             } | 
|         } | 
|     } | 
|      | 
|     /// <summary> | 
|     /// 关闭所有UI | 
|     /// </summary> | 
|     public void DestroyAllUI() | 
|     { | 
|         // 创建UI栈的副本,因为在关闭过程中会修改原栈 | 
|         UIBase[] uiArray = new UIBase[uiStack.Count]; | 
|         uiStack.CopyTo(uiArray, 0); | 
|          | 
|         // 关闭所有UI | 
|         foreach (var ui in uiArray) | 
|         { | 
|             // 关闭UI | 
|             CloseWindow(ui, true); | 
|         } | 
|          | 
|         foreach (var uiList in closedUIDict.Values) | 
|         { | 
|             foreach (var ui in uiList) | 
|             { | 
|                 GameObject.Destroy(ui.gameObject); | 
|             } | 
|         } | 
|   | 
|         // 清空UI字典和栈 | 
|         uiDict.Clear(); | 
|         uiStack.Clear(); | 
|         closedUIDict.Clear(); | 
|     } | 
|      | 
|     /// <summary> | 
|     /// 刷新UI | 
|     /// </summary> | 
|     public void RefreshUI<T>() where T : UIBase | 
|     { | 
|         // 获取UI类型名称 | 
|         string uiName = typeof(T).Name; | 
|          | 
|         // 检查UI是否存在 | 
|         if (!uiDict.ContainsKey(uiName) || uiDict[uiName].Count == 0) | 
|         { | 
|             // 记录警告日志 | 
|             Debug.LogWarning($"尝试刷新不存在的UI: {uiName}"); | 
|             return; | 
|         } | 
|          | 
|         // 刷新所有该类型的UI实例 | 
|         foreach (var ui in uiDict[uiName]) | 
|         { | 
|             // 刷新UI | 
|             ui.Refresh(); | 
|         } | 
|     } | 
|      | 
|     /// <summary> | 
|     /// 刷新所有UI | 
|     /// </summary> | 
|     public void RefreshAllUI() | 
|     { | 
|         // 遍历所有UI类型 | 
|         foreach (var uiList in uiDict.Values) | 
|         { | 
|             // 遍历该类型的所有UI实例 | 
|             foreach (var ui in uiList) | 
|             { | 
|                 // 刷新UI | 
|                 ui.Refresh(); | 
|             } | 
|         } | 
|     } | 
|      | 
|     private void Update() | 
|     { | 
|         // 如果距离上次检查的时间超过了检查间隔 | 
|         if (Time.time - lastCheckTime > CHECK_INTERVAL) | 
|         { | 
|             // 更新上次检查时间 | 
|             lastCheckTime = Time.time; | 
|             // 检查并关闭长时间未使用的UI | 
|             CheckAndCloseIdleUI(); | 
|         } | 
|     } | 
|      | 
|     #endregion | 
|   | 
|     #region 释放资源 | 
|      | 
|     /// <summary> | 
|     /// 释放资源 | 
|     /// </summary> | 
|     public override void Release() | 
|     { | 
|         // 关闭所有UI | 
|         DestroyAllUI(); | 
|          | 
|         // 清空缓存 | 
|         layerSortingOrderCache.Clear(); | 
|         layerTransformCache.Clear(); | 
|         uiInstanceCounter.Clear(); | 
|          | 
|         Debug.Log("UI管理器资源释放完成"); | 
|     } | 
|      | 
|     #endregion | 
| } |