| | |
| | | // UI字典,存储所有已加载的UI,键为UI名称,值为UI实例
|
| | | private 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>();
|
| | |
|
| | |
| | | // 如果场景中没有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);
|
| | | |
| | | root = GameObject.Instantiate(BuiltInLoader.LoadPrefab("UIRoot"));
|
| | |
|
| | | if (root == null)
|
| | | {
|
| | | Debug.LogError("无法找到UI根节点");
|
| | | return;
|
| | | }
|
| | |
|
| | | // 添加DontDestroyOnLoad组件,确保UI根节点在场景切换时不被销毁
|
| | | GameObject.DontDestroyOnLoad(root);
|
| | | }
|
| | |
|
| | | uiRoot = root.transform;
|
| | | uiRoot.position = Vector3.zero;
|
| | |
|
| | | // 初始化各层级的Transform
|
| | | staticTrans = uiRoot.Find("Static");
|
| | | bottomTrans = uiRoot.Find("Bottom");
|
| | | midTrans = uiRoot.Find("Middle");
|
| | | topTrans = uiRoot.Find("Top");
|
| | | systemTrans = uiRoot.Find("System");
|
| | |
|
| | | // // 添加基础Canvas
|
| | | // Canvas rootCanvas = root.AddComponent<Canvas>();
|
| | | // rootCanvas.renderMode = RenderMode.ScreenSpaceOverlay;
|
| | | // rootCanvas.sortingOrder = 0;
|
| | |
|
| | | // // 添加CanvasScaler
|
| | | // UnityEngine.UI.CanvasScaler scaler = root.AddComponent<UnityEngine.UI.CanvasScaler>();
|
| | | // 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<UnityEngine.UI.GraphicRaycaster>();
|
| | | 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);
|
| | | }
|
| | |
|
| | | #endregion
|
| | |
| | | public void CheckAndCloseIdleUI()
|
| | | {
|
| | | // 如果没有UI,直接返回
|
| | | if (uiDict.Count == 0)
|
| | | if (uiDict.Count == 0 && closedUIDict.Count == 0)
|
| | | return;
|
| | |
|
| | | // 创建需要关闭的UI列表
|
| | | List<UIBase> uiToClose = new List<UIBase>();
|
| | |
|
| | | // 遍历所有UI
|
| | | // 遍历所有活跃UI
|
| | | foreach (var uiList in uiDict.Values)
|
| | | {
|
| | | foreach (var ui in uiList)
|
| | |
| | | if (ui.isPersistent)
|
| | | continue;
|
| | |
|
| | | if (ui.IsActive())
|
| | | continue;
|
| | |
|
| | | // 计算UI空闲的回合数
|
| | | int idleRounds = currentRound - ui.lastUsedRound;
|
| | |
|
| | |
| | | }
|
| | | }
|
| | |
|
| | | // 关闭所有需要关闭的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)
|
| | | {
|
| | | // 记录日志
|
| | | Debug.Log($"关闭长时间未使用的UI: {ui.uiName}, 空闲回合数: {currentRound - ui.lastUsedRound}");
|
| | | // 关闭UI
|
| | | CloseWindow(ui);
|
| | | Debug.Log($"销毁长时间未使用的UI: {ui.uiName}, 空闲回合数: {currentRound - ui.lastUsedRound}");
|
| | | // 销毁UI对象
|
| | | GameObject.Destroy(ui.gameObject);
|
| | | }
|
| | | }
|
| | |
|
| | |
| | | // 获取UI类型名称
|
| | | string uiName = typeof(T).Name;
|
| | |
|
| | | // Debug.LogError("打开ui " + uiName);
|
| | | // 优先从closedUIDict中获取
|
| | | if (closedUIDict.TryGetValue(uiName, out List<UIBase> closedUIList) && closedUIList.Count > 0)
|
| | | {
|
| | | T recycledUI = closedUIList[0] as T;
|
| | | closedUIList.RemoveAt(0);
|
| | | |
| | | if (closedUIList.Count == 0)
|
| | | {
|
| | | closedUIDict.Remove(uiName);
|
| | | }
|
| | | |
| | | recycledUI.gameObject.SetActive(true);
|
| | | |
| | | // 自动设置父级UI(如果未指定且支持父子关系)
|
| | | if (parentUI == null && recycledUI.supportParentChildRelation && uiStack.Count > 0)
|
| | | {
|
| | | // 获取栈顶UI
|
| | | UIBase topUI = uiStack.Peek();
|
| | | // 如果栈顶UI也支持父子关系且不是主UI,则将其设为父级
|
| | | if (topUI != null && topUI.supportParentChildRelation && !topUI.isMainUI)
|
| | | {
|
| | | parentUI = topUI;
|
| | | }
|
| | | }
|
| | | |
| | | // 设置父级UI
|
| | | if (parentUI != null && recycledUI.supportParentChildRelation && !parentUI.isMainUI)
|
| | | {
|
| | | // 设置父子关系
|
| | | recycledUI.parentUI = parentUI;
|
| | | if (parentUI.childrenUI == null)
|
| | | {
|
| | | // 初始化父级UI的子UI列表
|
| | | parentUI.childrenUI = new List<UIBase>();
|
| | | }
|
| | | // 添加到父级UI的子UI列表
|
| | | parentUI.childrenUI.Add(recycledUI);
|
| | | }
|
| | | |
| | | // 更新回合数
|
| | | currentRound++;
|
| | | // 设置UI的最后使用回合数
|
| | | recycledUI.lastUsedRound = currentRound;
|
| | | // 更新父级UI的回合数
|
| | | UpdateParentUIRounds(recycledUI);
|
| | | |
| | | // 将UI添加到字典中
|
| | | if (!uiDict.ContainsKey(uiName))
|
| | | {
|
| | | // 如果字典中不存在该类型的UI,创建新列表
|
| | | uiDict[uiName] = new List<UIBase>();
|
| | | }
|
| | | // 添加到UI列表
|
| | | uiDict[uiName].Add(recycledUI);
|
| | | |
| | | // 将UI添加到栈中
|
| | | uiStack.Push(recycledUI);
|
| | | |
| | | // 更新UI排序顺序
|
| | | UpdateUISortingOrder();
|
| | | |
| | | // 打开UI
|
| | | recycledUI.HandleOpen();
|
| | |
|
| | | OnOpenWindow?.Invoke(recycledUI);
|
| | | |
| | | // 检查并关闭长时间未使用的UI
|
| | | CheckAndCloseIdleUI();
|
| | | |
| | | return recycledUI;
|
| | | }
|
| | |
|
| | | // 如果closedUIDict中没有可用的UI实例,则加载新的UI资源
|
| | | // Debug.LogError("打开ui " + uiName);
|
| | |
|
| | | // 加载UI资源
|
| | | T ui = LoadUIResource<T>(uiName);
|
| | |
| | | /// <summary>
|
| | | /// 关闭UI
|
| | | /// </summary>
|
| | | public void CloseWindow<T>() where T : UIBase
|
| | | public void CloseWindow<T>(bool destroy = false) where T : UIBase
|
| | | {
|
| | | // 获取UI类型名称
|
| | | string uiName = typeof(T).Name;
|
| | |
|
| | | CloseWindow(uiName);
|
| | | CloseWindow(uiName, destroy);
|
| | |
|
| | | }
|
| | |
|
| | | public void CloseWindow(string uiName)
|
| | | public void CloseWindow(string uiName, bool destroy = false)
|
| | | {
|
| | | // 检查UI是否存在
|
| | | if (!uiDict.ContainsKey(uiName) || uiDict[uiName].Count == 0)
|
| | |
| | | UIBase ui = uiDict[uiName][0];
|
| | |
|
| | | // 关闭UI
|
| | | CloseWindow(ui);
|
| | | CloseWindow(ui, destroy);
|
| | | }
|
| | |
|
| | | /// <summary>
|
| | | /// 关闭指定的UI实例
|
| | | /// </summary>
|
| | | public void CloseWindow(UIBase ui)
|
| | | public void CloseWindow(UIBase ui, bool destroy = false)
|
| | | {
|
| | | // 检查UI是否为空
|
| | | if (ui == null)
|
| | |
| | | foreach (var childUI in childrenUI)
|
| | | {
|
| | | // 关闭子UI
|
| | | CloseWindow(childUI);
|
| | | CloseWindow(childUI, destroy);
|
| | | }
|
| | |
|
| | | // 从栈中移除UI
|
| | |
| | | ui.HandleClose();
|
| | | OnCloseWindow?.Invoke(ui);
|
| | |
|
| | | // 销毁UI对象
|
| | | GameObject.Destroy(ui.gameObject);
|
| | | if (destroy)
|
| | | {
|
| | | // 销毁UI对象
|
| | | GameObject.Destroy(ui.gameObject);
|
| | | }
|
| | | else
|
| | | {
|
| | | // 添加到closedUIDict
|
| | | if (!closedUIDict.ContainsKey(uiName))
|
| | | {
|
| | | closedUIDict[uiName] = new List<UIBase>();
|
| | | }
|
| | | closedUIDict[uiName].Add(ui);
|
| | | |
| | | // 隐藏UI
|
| | | ui.gameObject.SetActive(false);
|
| | | }
|
| | |
|
| | | // 更新UI排序顺序
|
| | | UpdateUISortingOrder();
|
| | |
| | | foreach (var ui in uiListCopy)
|
| | | {
|
| | | // 关闭UI实例
|
| | | CloseWindow(ui);
|
| | | CloseWindow(ui, false);
|
| | | }
|
| | | }
|
| | |
|
| | |
| | | foreach (var ui in uiArray)
|
| | | {
|
| | | // 关闭UI
|
| | | CloseWindow(ui);
|
| | | CloseWindow(ui, true);
|
| | | }
|
| | |
|
| | | // 清空UI字典和栈
|
| | | uiDict.Clear();
|
| | | uiStack.Clear();
|
| | | closedUIDict.Clear();
|
| | | }
|
| | |
|
| | | /// <summary>
|