Main/UI/UIManager.cs
@@ -31,6 +31,9 @@
    // 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>();
    
@@ -91,56 +94,33 @@
        // 如果场景中没有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
@@ -370,13 +350,13 @@
    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)
@@ -385,6 +365,9 @@
                if (ui.isPersistent)
                    continue;
                    
                if (ui.IsActive())
                    continue;
                // 计算UI空闲的回合数
                int idleRounds = currentRound - ui.lastUsedRound;
                
@@ -396,13 +379,53 @@
            }
        }
        
        // 关闭所有需要关闭的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);
        }
    }
    
@@ -525,8 +548,80 @@
        // 获取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);
@@ -601,16 +696,16 @@
    /// <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)
@@ -624,13 +719,13 @@
        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)
@@ -654,7 +749,7 @@
        foreach (var childUI in childrenUI)
        {
            // 关闭子UI
            CloseWindow(childUI);
            CloseWindow(childUI, destroy);
        }
        
        // 从栈中移除UI
@@ -699,8 +794,23 @@
        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();
@@ -729,7 +839,7 @@
        foreach (var ui in uiListCopy)
        {
            // 关闭UI实例
            CloseWindow(ui);
            CloseWindow(ui, false);
        }
    }
    
@@ -746,12 +856,13 @@
        foreach (var ui in uiArray)
        {
            // 关闭UI
            CloseWindow(ui);
            CloseWindow(ui, true);
        }
        
        // 清空UI字典和栈
        uiDict.Clear();
        uiStack.Clear();
        closedUIDict.Clear();
    }
    
    /// <summary>