using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using System.Linq; using DG.Tweening; /// /// UI管理器 - 负责管理所有UI界面的显示、隐藏和层级 /// public class UIManager : ManagerBase { #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实例 private Dictionary> uiDict = new Dictionary>(); // 存储关闭但未销毁的UI,键为UI名称,值为UI实例 private Dictionary> closedUIDict = new Dictionary>(); // UI栈,用于管理UI的显示顺序 private Stack uiStack = new Stack(); // 当前最高的排序顺序 private int currentHighestSortingOrder = 0; // 当前回合数,用于记录UI的使用情况 private int currentRound = 0; // 缓存层级对应的排序顺序 private Dictionary layerSortingOrderCache = new Dictionary(); // 缓存层级对应的Transform private Dictionary layerTransformCache = new Dictionary(); // UI实例计数器,用于为同类型UI生成唯一标识 private Dictionary uiInstanceCounter = new Dictionary(); // 上次检查时间 private float lastCheckTime = 0f; // 检查间隔(秒) private const float CHECK_INTERVAL = 5f; public Action OnOpenWindow; public Action OnCloseWindow; #endregion #region 初始化 /// /// 初始化UI管理器 /// public override void Init() { base.Init(); // 初始化UI根节点 InitUIRoot(); // 初始化缓存 layerSortingOrderCache.Clear(); layerTransformCache.Clear(); uiInstanceCounter.Clear(); DOTween.SetTweensCapacity(500, 50); Debug.Log("UI管理器初始化完成"); } /// /// 初始化UI根节点 /// 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); } #endregion #region 辅助方法 // 获取UI层级对应的基础排序顺序 private 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 * 10000; break; case UILayer.Loading: result = BASE_SORTING_ORDER * 100000; break; default: result = BASE_SORTING_ORDER * 10; break; } // 将结果存入缓存 layerSortingOrderCache[layer] = result; return result; } // 获取层级对应的Transform private 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() 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 uiList) && uiList.Count > 0) { // 返回第一个实例 return uiList[0] as T; } // 如果不存在,返回null return null; } public List GetUIList() where T : UIBase { List uiList = new List(); // 获取UI类型名称 string uiName = typeof(T).Name; if (string.IsNullOrEmpty(uiName)) { // 记录错误日志 Debug.LogError("UI名称为空"); return uiList; } // 尝试从字典中获取UI实例列表 List 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() where T : UIBase { List uiList = GetUIList(); foreach (T ui in uiList) { if (ui.IsActive()) { return true; } } return false; } public bool IsOpened() where T : UIBase { T ui = GetUI(); if (null != ui) { return ui.IsActive(); } return false; } // 获取指定类型的所有UI实例 public List GetAllUI() where T : UIBase { // 获取UI类型名称 string uiName = typeof(T).Name; if (string.IsNullOrEmpty(uiName)) { // 记录错误日志 Debug.LogError("UI名称为空"); return new List(); } // 尝试从字典中获取UI实例列表 if (uiDict.TryGetValue(uiName, out List uiList) && uiList.Count > 0) { // 将列表中的所有实例转换为指定类型并返回 return uiList.Cast().ToList(); } // 如果不存在,返回空列表 return new List(); } // 更新父级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 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); } } } /// /// 检查并关闭长时间未使用的UI /// public void CheckAndCloseIdleUI() { // 如果没有UI,直接返回 if (uiDict.Count == 0 && closedUIDict.Count == 0) return; // 创建需要关闭的UI列表 List uiToClose = new List(); // 遍历所有活跃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 emptyKeys = new List(); foreach (var kvp in closedUIDict) { string uiName = kvp.Key; List uiList = kvp.Value; List uiToRemove = new List(); 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) { // 记录日志 Debug.Log($"销毁长时间未使用的UI: {ui.uiName}, 空闲回合数: {currentRound - ui.lastUsedRound}"); // 销毁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 = ResManager.Instance.LoadAsset("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(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进行排序,然后再按照栈顺序排序 Array.Sort(uiArray, (a, b) => { // 比较UI层级 int layerCompare = a.uiLayer.CompareTo(b.uiLayer); if (layerCompare != 0) return layerCompare; // 同层级内,按照栈中的顺序排序 return Array.IndexOf(uiArray, b).CompareTo(Array.IndexOf(uiArray, a)); }); // 遍历排序后的UI数组,设置排序顺序 foreach (var ui in uiArray) { // 获取基础排序顺序 int baseSortingOrder = GetBaseSortingOrderForLayer(ui.uiLayer); // 计算当前UI的排序顺序 int sortingOrder = baseSortingOrder + currentHighestSortingOrder; // 设置UI的排序顺序 ui.SetSortingOrder(sortingOrder); // 更新当前最高排序顺序 currentHighestSortingOrder += 10; // Debug.Log(ui.uiName + " order is " + sortingOrder + " " + currentHighestSortingOrder); } } #endregion #region UI操作 private UIBase GetLastSupportParentChildRelationUI() { List tempList = new List(); UIBase target = null; while (target == null && uiStack.Count > 0) { UIBase uiBase = uiStack.Pop(); if (uiBase != null && uiBase.supportParentChildRelation && !uiBase.isMainUI) { target = uiBase; } tempList.Add(uiBase); } for (int i = tempList.Count - 1; i >= 0; i--) { uiStack.Push(tempList[i]); } return target; } public UIBase OpenWindow(string uiName) { // 优先从closedUIDict中获取 UIBase parentUI = null; UIBase returnValue = null; Debug.Log("OpenWindow " + uiName); List closedUIList = new List(); if (closedUIDict.TryGetValue(uiName, out closedUIList) && closedUIList.Count > 0) { Debug.Log("OpenWindow getFromClosedDict " + uiName); returnValue = closedUIList[0] as UIBase; closedUIList.RemoveAt(0); if (closedUIList.Count == 0) { closedUIDict.Remove(uiName); } } else { Debug.Log("OpenWindow getNewLoad " + uiName); returnValue = LoadUIResource(uiName); if (returnValue == null) { // 记录错误日志 Debug.LogError($"打开UI失败: {uiName}"); return null; } } returnValue.gameObject.SetActive(true); // 自动设置父级UI(如果未指定且支持父子关系) if (returnValue.supportParentChildRelation && uiStack.Count > 0) { // 获取栈顶UI parentUI = GetLastSupportParentChildRelationUI(); } // 设置父级UI if (parentUI != null) { // 设置父子关系 returnValue.parentUI = parentUI; if (parentUI.childrenUI == null) { // 初始化父级UI的子UI列表 parentUI.childrenUI = new List(); } // 添加到父级UI的子UI列表 parentUI.childrenUI.Add(returnValue); } // 更新回合数 currentRound++; // 设置UI的最后使用回合数 returnValue.lastUsedRound = currentRound; // 更新父级UI的回合数 UpdateParentUIRounds(returnValue); // 将UI添加到字典中 if (!uiDict.ContainsKey(uiName)) { // 如果字典中不存在该类型的UI,创建新列表 uiDict[uiName] = new List(); } // 添加到UI列表 uiDict[uiName].Add(returnValue); // 将UI添加到栈中 uiStack.Push(returnValue); // 更新UI排序顺序 UpdateUISortingOrder(); // 打开UI returnValue.HandleOpen(); OnOpenWindow?.Invoke(returnValue); // 检查并关闭长时间未使用的UI CheckAndCloseIdleUI(); return returnValue; } /// /// 打开UI /// public T OpenWindow() where T : UIBase { // 获取UI类型名称 string uiName = typeof(T).Name; return OpenWindow(uiName) as T; } /// /// 关闭UI /// public void CloseWindow(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); } /// /// 关闭指定的UI实例 /// 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; Debug.Log("CloseWindow " + uiName + " destroy : " + destroy.ToString()); // 收集所有子UI List childrenUI = new List(); if (ui.supportParentChildRelation) { CollectChildrenUI(ui, childrenUI); } // 先关闭所有子UI foreach (var childUI in childrenUI) { // 关闭子UI CloseWindow(childUI, destroy); } // 从栈中移除UI Stack tempStack = new Stack(); 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(); } closedUIDict[uiName].Add(ui); Debug.Log("CloseWindow " + uiName + " destroy : " + destroy.ToString() + " push to closedUIDict"); // 隐藏UI (交给handle close内部自己去做) // ui.gameObject.SetActive(false); } // 更新UI排序顺序 UpdateUISortingOrder(); } /// /// 关闭指定类型的所有UI实例 /// public void CloseAllWindows() 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 uiListCopy = new List(uiDict[uiName]); // 关闭所有UI实例 foreach (var ui in uiListCopy) { // 关闭UI实例 CloseWindow(ui, false); } } public void ReturnMainUI() { List allUI = new List(uiStack); for (int i = 0; i < allUI.Count; i++) { UIBase uiBase = allUI[i]; if (!uiBase.isMainUI && uiBase.uiLayer < UILayer.System) { CloseWindow(uiBase); } } } /// /// 关闭所有UI /// 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(); } /// /// 刷新UI /// public void RefreshUI() 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(); } } /// /// 刷新所有UI /// 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 释放资源 /// /// 释放资源 /// public override void Release() { // 关闭所有UI DestroyAllUI(); // 清空缓存 layerSortingOrderCache.Clear(); layerTransformCache.Clear(); uiInstanceCounter.Clear(); Debug.Log("UI管理器资源释放完成"); } #endregion }