| | |
| | | using System.Collections.Generic; |
| | | using UnityEngine; |
| | | using System.Linq; |
| | | using DG.Tweening; |
| | | |
| | | /// <summary> |
| | | /// UI管理器 - 负责管理所有UI界面的显示、隐藏和层级 |
| | |
| | | private Transform staticTrans; |
| | | private Transform bottomTrans; |
| | | private Transform midTrans; |
| | | private Transform topTrans; |
| | | private Transform systemTrans; |
| | | |
| | | private Transform loadingTrans; |
| | | |
| | | // UI字典,存储所有已加载的UI,键为UI名称,值为UI实例 |
| | | private Dictionary<string, List<UIBase>> uiDict = new Dictionary<string, List<UIBase>>(); |
| | | |
| | | #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>>(); |
| | | |
| | |
| | | private int currentRound = 0; |
| | | |
| | | // 缓存层级对应的排序顺序 |
| | | private Dictionary<UILayer, int> layerSortingOrderCache = new Dictionary<UILayer, int>(); |
| | | private static Dictionary<UILayer, int> layerSortingOrderCache = new Dictionary<UILayer, int>(); |
| | | |
| | | // 缓存层级对应的Transform |
| | | private Dictionary<UILayer, Transform> layerTransformCache = new Dictionary<UILayer, Transform>(); |
| | |
| | | #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> |
| | |
| | | staticTrans = uiRoot.Find("Static"); |
| | | bottomTrans = uiRoot.Find("Bottom"); |
| | | midTrans = uiRoot.Find("Middle"); |
| | | topTrans = uiRoot.Find("Top"); |
| | | 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.Top, topTrans); |
| | | layerTransformCache.Add(UILayer.System, systemTrans); |
| | | layerTransformCache.Add(UILayer.Loading, loadingTrans); |
| | | } |
| | | |
| | | public Transform GetUIRoot() |
| | | { |
| | | return uiRoot; |
| | | } |
| | | |
| | | #endregion |
| | | |
| | | #region 辅助方法 |
| | | |
| | | |
| | | // 获取UI层级对应的基础排序顺序 |
| | | private int GetBaseSortingOrderForLayer(UILayer layer) |
| | | public static int GetBaseSortingOrderForLayer(UILayer layer) |
| | | { |
| | | // 尝试从缓存中获取排序顺序 |
| | | if (layerSortingOrderCache.TryGetValue(layer, out int order)) |
| | | return order; |
| | | |
| | | |
| | | // 如果缓存中没有,使用原来的方法计算并缓存结果 |
| | | int result; |
| | | switch (layer) |
| | |
| | | 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: |
| | | case UILayer.Mid: |
| | | result = BASE_SORTING_ORDER * 1000; |
| | | break; |
| | | case UILayer.System: |
| | | result = BASE_SORTING_ORDER * 10000; |
| | | 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 |
| | | private Transform GetTransForLayer(UILayer layer) |
| | | public Transform GetTransForLayer(UILayer layer) |
| | | { |
| | | // 尝试从缓存中获取Transform |
| | | if (layerTransformCache.TryGetValue(layer, out Transform trans)) |
| | |
| | | case UILayer.Mid: |
| | | result = midTrans; |
| | | break; |
| | | case UILayer.Top: |
| | | result = topTrans; |
| | | break; |
| | | case UILayer.System: |
| | | result = systemTrans; |
| | | break; |
| | | case UILayer.Loading: |
| | | result = loadingTrans; |
| | | break; |
| | | default: |
| | | result = bottomTrans; |
| | |
| | | { |
| | | // 返回第一个实例 |
| | | 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 false; |
| | | } |
| | | |
| | | public bool IsOpened(string uiName) |
| | | { |
| | | UIBase ui = GetUI(uiName); |
| | | |
| | | if (null != ui) |
| | | { |
| | | return ui.IsActive(); |
| | | } |
| | | |
| | | return false; |
| | | } |
| | | |
| | | // 检查是否存在任何全屏或遮罩窗口, 有screenMask 即可代表全屏 |
| | | 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.screenMask != null) |
| | | { |
| | | exist = true; |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | return exist; |
| | | } |
| | | |
| | | //在此界面上有没任何全屏或遮罩窗口 |
| | | public bool ExistAnyFullScreenOrMaskWinAbove(string uiName) |
| | | { |
| | | var ui = GetUI(uiName); |
| | | if (ui == null || !ui.IsActive()) |
| | | { |
| | | // 如果UI不存在或未打开,默认为有被挡住 |
| | | return true; |
| | | } |
| | | |
| | | foreach (var uiBase in uiStack) |
| | | { |
| | | if (uiBase == null) |
| | | { |
| | | continue; |
| | | } |
| | | if (uiBase.name == uiName) |
| | | { |
| | | break; |
| | | } |
| | | if (uiBase.IsActive() && uiBase.screenMask != null) |
| | | { |
| | | return true; |
| | | } |
| | | } |
| | | |
| | | return false; |
| | | } |
| | | |
| | | |
| | | // 获取指定类型的所有UI实例 |
| | | public List<T> GetAllUI<T>() where T : UIBase |
| | |
| | | foreach (var ui in uiToClose) |
| | | { |
| | | // 记录日志 |
| | | #if UNITY_EDITOR |
| | | Debug.Log($"销毁长时间未使用的UI: {ui.uiName}, 空闲回合数: {currentRound - ui.lastUsedRound}"); |
| | | #endif |
| | | // 销毁UI对象 |
| | | GameObject.Destroy(ui.gameObject); |
| | | } |
| | |
| | | |
| | | private UIBase LoadUIResource(string uiName) |
| | | { |
| | | |
| | | // 从资源管理器加载UI预制体 |
| | | GameObject prefab = ResManager.Instance.LoadAsset<GameObject>("UI", uiName); |
| | | 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) |
| | |
| | | uiStack.CopyTo(uiArray, 0); |
| | | |
| | | // 先按照UILayer进行排序,然后再按照栈顺序排序 |
| | | Array.Sort(uiArray, (a, b) => { |
| | | // 比较UI层级 |
| | | 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 Array.IndexOf(uiArray, a).CompareTo(Array.IndexOf(uiArray, b)); |
| | | |
| | | return uiOrderDict[b].CompareTo(uiOrderDict[a]); |
| | | }); |
| | | |
| | | // 遍历排序后的UI数组,设置排序顺序 |
| | |
| | | // 设置UI的排序顺序 |
| | | ui.SetSortingOrder(sortingOrder); |
| | | // 更新当前最高排序顺序 |
| | | currentHighestSortingOrder += 10; |
| | | currentHighestSortingOrder += ui.uiLayer == UILayer.Static ? 155/*这里是角色+特效之上的层级*/ : 10; |
| | | |
| | | // Debug.Log(ui.uiName + " order is " + sortingOrder + " " + currentHighestSortingOrder); |
| | | } |
| | | } |
| | | |
| | |
| | | |
| | | private UIBase GetLastSupportParentChildRelationUI() |
| | | { |
| | | List<UIBase> tempList = new List<UIBase>(); |
| | | |
| | | UIBase target = null; |
| | | |
| | | while (target == null && uiStack.Count > 0) |
| | | foreach (var uiBase in uiStack) |
| | | { |
| | | UIBase uiBase = uiStack.Pop(); |
| | | |
| | | if (uiBase != null && uiBase.supportParentChildRelation && !uiBase.isMainUI) |
| | | if (uiBase != null && uiBase.supportParentChildRelation) |
| | | { |
| | | target = uiBase; |
| | | return uiBase; |
| | | } |
| | | |
| | | tempList.Add(uiBase); |
| | | } |
| | | |
| | | for (int i = tempList.Count - 1; i >= 0; i--) |
| | | { |
| | | uiStack.Push(tempList[i]); |
| | | } |
| | | |
| | | return target; |
| | | return null; |
| | | } |
| | | |
| | | public UIBase OpenWindow(string uiName) |
| | | public UIBase OpenWindow(string uiName, int functionOrder = 0) |
| | | { |
| | | // 优先从closedUIDict中获取 |
| | | UIBase parentUI = null; |
| | | |
| | | UIBase returnValue = null; |
| | | |
| | | if (closedUIDict.TryGetValue(uiName, out List<UIBase> closedUIList) && closedUIList.Count > 0) |
| | | 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); |
| | | |
| | |
| | | } |
| | | else |
| | | { |
| | | #if UNITY_EDITOR |
| | | Debug.Log("OpenWindow getNewLoad " + uiName); |
| | | #endif |
| | | returnValue = LoadUIResource(uiName); |
| | | if (returnValue == null) |
| | | { |
| | |
| | | |
| | | returnValue.gameObject.SetActive(true); |
| | | |
| | | // 自动设置父级UI(如果未指定且支持父子关系) |
| | | if (returnValue.supportParentChildRelation && uiStack.Count > 0) |
| | | // 自动设置父级UI, 如果勾选了ismainui 则不需要找父级UI |
| | | if (returnValue.supportParentChildRelation && uiStack.Count > 0 && !returnValue.isMainUI) |
| | | { |
| | | // 获取栈顶UI |
| | | parentUI = GetLastSupportParentChildRelationUI(); |
| | |
| | | } |
| | | // 添加到UI列表 |
| | | uiDict[uiName].Add(returnValue); |
| | | |
| | | |
| | | #if UNITY_EDITOR |
| | | if (uiDict[uiName].Count > 5) |
| | | { |
| | | Debug.LogError("已打开" + uiDict[uiName].Count + "个界面:" + uiName); |
| | | } |
| | | #endif |
| | | |
| | | // 将UI添加到栈中 |
| | | uiStack.Push(returnValue); |
| | | |
| | |
| | | UpdateUISortingOrder(); |
| | | |
| | | // 打开UI |
| | | returnValue.functionOrder = functionOrder; |
| | | returnValue.HandleOpen(); |
| | | |
| | | OnOpenWindow?.Invoke(returnValue); |
| | |
| | | /// <summary> |
| | | /// 打开UI |
| | | /// </summary> |
| | | public T OpenWindow<T>() where T : UIBase |
| | | public T OpenWindow<T>(int functionOrder = 0) where T : UIBase |
| | | { |
| | | // 获取UI类型名称 |
| | | string uiName = typeof(T).Name; |
| | | return OpenWindow(uiName) as T; |
| | | return OpenWindow(uiName, functionOrder) as T; |
| | | } |
| | | |
| | | /// <summary> |
| | |
| | | |
| | | // 获取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) |
| | |
| | | if (tempUI != ui) |
| | | { |
| | | tempStack.Push(tempUI); |
| | | } |
| | | else |
| | | { |
| | | break; |
| | | } |
| | | } |
| | | |
| | |
| | | // 关闭UI |
| | | ui.HandleClose(); |
| | | OnCloseWindow?.Invoke(ui); |
| | | |
| | | |
| | | if (destroy) |
| | | { |
| | | // 销毁UI对象 |
| | |
| | | closedUIDict[uiName] = new List<UIBase>(); |
| | | } |
| | | closedUIDict[uiName].Add(ui); |
| | | |
| | | // 隐藏UI |
| | | ui.gameObject.SetActive(false); |
| | | |
| | | #if UNITY_EDITOR |
| | | Debug.Log("CloseWindow " + uiName + " destroy : " + destroy.ToString() + " push to closedUIDict"); |
| | | #endif |
| | | |
| | | // 隐藏UI (交给handle close内部自己去做) |
| | | // ui.gameObject.SetActive(false); |
| | | } |
| | | |
| | | // 更新UI排序顺序 |
| | |
| | | } |
| | | } |
| | | |
| | | 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 |
| | |
| | | CloseWindow(ui, true); |
| | | } |
| | | |
| | | foreach (var uiList in closedUIDict.Values) |
| | | { |
| | | foreach (var ui in uiList) |
| | | { |
| | | GameObject.Destroy(ui.gameObject); |
| | | } |
| | | } |
| | | |
| | | // 清空UI字典和栈 |
| | | uiDict.Clear(); |
| | | uiStack.Clear(); |