using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using System.Linq; /// /// 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 topTrans; private Transform systemTrans; // UI字典,存储所有已加载的UI,键为UI名称,值为UI实例 private Dictionary> uiDict = 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(); Debug.Log("UI管理器初始化完成"); } /// /// 初始化UI根节点 /// private void InitUIRoot() { // 查找UI根节点 GameObject root = GameObject.Find("UIRoot"); // 如果场景中没有UI根节点,则创建一个 if (root == null) { root = new GameObject("UIRoot"); // 添加DontDestroyOnLoad组件,确保UI根节点在场景切换时不被销毁 GameObject.DontDestroyOnLoad(root); // 创建各层级节点 GameObject staticNode = new GameObject("Static"); GameObject bottomNode = new GameObject("Bottom"); GameObject midNode = new GameObject("Mid"); GameObject topNode = new GameObject("Top"); GameObject systemNode = new GameObject("System"); // 设置父节点 staticNode.transform.SetParent(root.transform, false); bottomNode.transform.SetParent(root.transform, false); midNode.transform.SetParent(root.transform, false); topNode.transform.SetParent(root.transform, false); systemNode.transform.SetParent(root.transform, false); if (root == null) { Debug.LogError("无法找到UI根节点"); return; } } uiRoot = root.transform; uiRoot.position = Vector3.zero; // 初始化各层级的Transform staticTrans = uiRoot.Find("Static"); bottomTrans = uiRoot.Find("Bottom"); midTrans = uiRoot.Find("Mid"); topTrans = uiRoot.Find("Top"); systemTrans = uiRoot.Find("System"); // // 添加基础Canvas // Canvas rootCanvas = root.AddComponent(); // rootCanvas.renderMode = RenderMode.ScreenSpaceOverlay; // rootCanvas.sortingOrder = 0; // // 添加CanvasScaler // UnityEngine.UI.CanvasScaler scaler = root.AddComponent(); // scaler.uiScaleMode = UnityEngine.UI.CanvasScaler.ScaleMode.ScaleWithScreenSize; // scaler.referenceResolution = new Vector2(1920, 1080); // scaler.screenMatchMode = UnityEngine.UI.CanvasScaler.ScreenMatchMode.MatchWidthOrHeight; // scaler.matchWidthOrHeight = 1.0f; // // 添加GraphicRaycaster // root.AddComponent(); } #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 * 10; break; case UILayer.Mid: result = BASE_SORTING_ORDER * 100; break; case UILayer.Top: result = BASE_SORTING_ORDER * 1000; break; case UILayer.System: result = BASE_SORTING_ORDER * 10000; 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.Top: result = topTrans; break; case UILayer.System: result = systemTrans; 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) return; // 创建需要关闭的UI列表 List uiToClose = new List(); // 遍历所有UI foreach (var uiList in uiDict.Values) { foreach (var ui in uiList) { // 如果UI是持久化的,跳过 if (ui.isPersistent) continue; // 计算UI空闲的回合数 int idleRounds = currentRound - ui.lastUsedRound; // 如果空闲回合数超过最大空闲回合数,添加到关闭列表 if (idleRounds > ui.maxIdleRounds) { uiToClose.Add(ui); } } } // 关闭所有需要关闭的UI foreach (var ui in uiToClose) { // 记录日志 Debug.Log($"关闭长时间未使用的UI: {ui.uiName}, 空闲回合数: {currentRound - ui.lastUsedRound}"); // 关闭UI CloseWindow(ui); } } // // 生成UI实例的唯一标识 // private string GenerateUIInstanceID(string uiName) // { // // 如果计数器中不存在该UI类型,初始化为0 // if (!uiInstanceCounter.ContainsKey(uiName)) // { // uiInstanceCounter[uiName] = 0; // } // // 递增计数器 // uiInstanceCounter[uiName]++; // // 返回带有计数的唯一标识 // return $"{uiName}_{uiInstanceCounter[uiName]}"; // } #endregion #region UI资源管理 // 加载UI预制体 private T LoadUIResource(string uiName) where T : UIBase { // 从资源管理器加载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; // 获取UI基类组件 T uiBase = uiObject.GetComponent(); // 检查UI基类组件是否存在 if (uiBase == null) { // 记录错误日志 Debug.LogError($"UI预制体 {uiName} 没有 UIBase 组件"); return null; } // 设置UI名称 uiBase.uiName = uiName; // // 设置UI实例ID // uiBase.instanceID = GenerateUIInstanceID(uiName); // 设置父节点为UI根节点 Transform parentTrans = GetTransForLayer(uiBase.uiLayer); uiObject.transform.SetParent(parentTrans, false); // 设置排序顺序 int baseSortingOrder = GetBaseSortingOrderForLayer(uiBase.uiLayer); uiBase.SetSortingOrder(baseSortingOrder); return uiBase; } #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, a).CompareTo(Array.IndexOf(uiArray, b)); }); // 遍历排序后的UI数组,设置排序顺序 foreach (var ui in uiArray) { // 获取基础排序顺序 int baseSortingOrder = GetBaseSortingOrderForLayer(ui.uiLayer); // 计算当前UI的排序顺序 int sortingOrder = baseSortingOrder + currentHighestSortingOrder; // 设置UI的排序顺序 ui.SetSortingOrder(sortingOrder); // 更新当前最高排序顺序 currentHighestSortingOrder += 10; } } #endregion #region UI操作 /// /// 打开UI /// public T OpenWindow(UIBase parentUI = null) where T : UIBase { // 获取UI类型名称 string uiName = typeof(T).Name; // 加载UI资源 T ui = LoadUIResource(uiName); if (ui == null) { // 记录错误日志 Debug.LogError($"打开UI失败: {uiName}"); return null; } // 自动设置父级UI(如果未指定且支持父子关系) if (parentUI == null && ui.supportParentChildRelation && uiStack.Count > 0) { // 获取栈顶UI UIBase topUI = uiStack.Peek(); // 如果栈顶UI也支持父子关系且不是主UI,则将其设为父级 if (topUI != null && topUI.supportParentChildRelation && !topUI.isMainUI) { parentUI = topUI; } } // 设置父级UI if (parentUI != null && ui.supportParentChildRelation && !parentUI.isMainUI) { // 设置父子关系 ui.parentUI = parentUI; if (parentUI.childrenUI == null) { // 初始化父级UI的子UI列表 parentUI.childrenUI = new List(); } // 添加到父级UI的子UI列表 parentUI.childrenUI.Add(ui); } // 更新回合数 currentRound++; // 设置UI的最后使用回合数 ui.lastUsedRound = currentRound; // 更新父级UI的回合数 UpdateParentUIRounds(ui); // 将UI添加到字典中 if (!uiDict.ContainsKey(uiName)) { // 如果字典中不存在该类型的UI,创建新列表 uiDict[uiName] = new List(); } // 添加到UI列表 uiDict[uiName].Add(ui); // 将UI添加到栈中 uiStack.Push(ui); // 更新UI排序顺序 UpdateUISortingOrder(); // 打开UI ui.HandleOpen(); OnOpenWindow?.Invoke(ui); // 检查并关闭长时间未使用的UI CheckAndCloseIdleUI(); return ui; } /// /// 关闭UI /// public void CloseWindow() where T : UIBase { // 获取UI类型名称 string uiName = typeof(T).Name; CloseWindow(uiName); } public void CloseWindow(string uiName) { // 检查UI是否存在 if (!uiDict.ContainsKey(uiName) || uiDict[uiName].Count == 0) { // 记录警告日志 Debug.LogWarning($"尝试关闭不存在的UI: {uiName}"); return; } // 获取第一个UI实例 UIBase ui = uiDict[uiName][0]; // 关闭UI CloseWindow(ui); } /// /// 关闭指定的UI实例 /// public void CloseWindow(UIBase ui) { // 检查UI是否为空 if (ui == null) { // 记录警告日志 Debug.LogWarning("尝试关闭空UI"); return; } // 获取UI类型名称 string uiName = ui.uiName; // 收集所有子UI List childrenUI = new List(); if (ui.supportParentChildRelation) { CollectChildrenUI(ui, childrenUI); } // 先关闭所有子UI foreach (var childUI in childrenUI) { // 关闭子UI CloseWindow(childUI); } // 从栈中移除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); // 销毁UI对象 GameObject.Destroy(ui.gameObject); // 更新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); } } /// /// 关闭所有UI /// public void DestroyAllUI() { // 创建UI栈的副本,因为在关闭过程中会修改原栈 UIBase[] uiArray = new UIBase[uiStack.Count]; uiStack.CopyTo(uiArray, 0); // 关闭所有UI foreach (var ui in uiArray) { // 关闭UI CloseWindow(ui); } // 清空UI字典和栈 uiDict.Clear(); uiStack.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 }