yyl
2026-02-11 3f2cd27c5dfb3b450245bf1a37fc1b3414031c7c
Main/Manager/UIManager.cs
@@ -4,6 +4,7 @@
using UnityEngine;
using System.Linq;
using DG.Tweening;
using Cysharp.Threading.Tasks;
/// <summary>
/// UI管理器 - 负责管理所有UI界面的显示、隐藏和层级
@@ -562,7 +563,9 @@
        }
        else
        {
            #pragma warning disable CS0618 // Obsolete — sync legacy fallback, use LoadUIResourceAsync
            prefab = ResManager.Instance.LoadAsset<GameObject>("UI", uiName);
            #pragma warning restore CS0618
        }
        // 检查预制体是否加载成功
@@ -617,6 +620,246 @@
    {
        return LoadUIResource(uiName) as T;
    }
    // ====================================================================
    // US2: Async variants — InitUIRootAsync, LoadUIResourceAsync, OpenWindowAsync
    // ====================================================================
    /// <summary>
    /// US2: 异步初始化 UI 根节点。
    /// </summary>
    public async UniTask InitUIRootAsync()
    {
        GameObject root = GameObject.Find("UIRoot");
        if (root == null)
        {
            var prefab = await BuiltInLoader.LoadPrefabAsync("UIRoot");
            root = GameObject.Instantiate(prefab);
            root.name = "UIRoot";
            if (root == null)
            {
                Debug.LogError("无法加载UI根节点");
                return;
            }
            GameObject.DontDestroyOnLoad(root);
        }
    }
    /// <summary>
    /// US2: 异步加载 UI 资源。
    /// </summary>
    private async UniTask<UIBase> LoadUIResourceAsync(string uiName)
    {
        GameObject prefab;
        if (uiName == "LaunchWin" || uiName == "DownLoadWin" || uiName == "RequestSecretWin" || uiName == "GameAgeWarnWin")
        {
            prefab = await BuiltInLoader.LoadPrefabAsync(uiName);
        }
        else
        {
            prefab = await ResManager.Instance.LoadAssetAsync<GameObject>("UI", uiName);
        }
        if (prefab == null)
        {
            Debug.LogError($"加载UI预制体失败: {uiName}");
            return null;
        }
        GameObject uiObject = GameObject.Instantiate(prefab);
        uiObject.name = uiName;
        Type uiType = Type.GetType(uiName);
        if (uiType == null)
        {
            Debug.LogError($"找不到UI类型: {uiName}");
            return null;
        }
        UIBase uiBase = uiObject.GetComponent(uiType) as UIBase;
        if (uiBase == null)
        {
            Debug.LogError($"UI预制体 {uiName} 没有 UIBase 组件或类型不匹配");
            return null;
        }
        uiBase.uiName = uiName;
        Transform parentTrans = GetTransForLayer(uiBase.uiLayer);
        uiObject.transform.SetParent(parentTrans, false);
        int baseSortingOrder = GetBaseSortingOrderForLayer(uiBase.uiLayer);
        uiBase.SetSortingOrder(baseSortingOrder);
        return uiBase;
    }
    /// <summary>
    /// US2: 异步打开窗口。
    /// </summary>
    public async UniTask<UIBase> OpenWindowAsync(string uiName, int functionOrder = 0)
    {
        UIBase returnValue = null;
        UIBase parentUI = null;
        // Check closed cache
        if (closedUIDict.TryGetValue(uiName, out var closedUIList) && closedUIList.Count > 0)
        {
            returnValue = closedUIList[0] as UIBase;
            closedUIList.RemoveAt(0);
            if (closedUIList.Count == 0)
            {
                closedUIDict.Remove(uiName);
            }
        }
        else
        {
            // US3: Show loading indicator while loading UI prefab (auto-hide after load)
            ShowLoadingIndicator();
            try
            {
                returnValue = await LoadUIResourceAsync(uiName);
            }
            finally
            {
                HideLoadingIndicator();
            }
            if (returnValue == null)
            {
                Debug.LogError($"打开UI失败: {uiName}");
                return null;
            }
        }
        returnValue.gameObject.SetActive(true);
        if (returnValue.supportParentChildRelation && uiStack.Count > 0 && !returnValue.isMainUI)
        {
            parentUI = GetLastSupportParentChildRelationUI();
        }
        if (parentUI != null)
        {
            returnValue.parentUI = parentUI;
            if (parentUI.childrenUI == null)
            {
                parentUI.childrenUI = new List<UIBase>();
            }
            parentUI.childrenUI.Add(returnValue);
        }
        currentRound++;
        returnValue.lastUsedRound = currentRound;
        UpdateParentUIRounds(returnValue);
        if (!uiDict.ContainsKey(uiName))
        {
            uiDict[uiName] = new List<UIBase>();
        }
        uiDict[uiName].Add(returnValue);
        uiStack.Push(returnValue);
        UpdateUISortingOrder();
        returnValue.functionOrder = functionOrder;
        returnValue.HandleOpen();
        OnOpenWindow?.Invoke(returnValue);
        CheckAndCloseIdleUI();
        return returnValue;
    }
    /// <summary>
    /// US2: 泛型 异步打开窗口。
    /// </summary>
    public async UniTask<T> OpenWindowAsync<T>(int functionOrder = 0) where T : UIBase
    {
        string uiName = typeof(T).Name;
        var result = await OpenWindowAsync(uiName, functionOrder);
        return result as T;
    }
    // ====================================================================
    // US3: Loading indicator for async UI loading
    // ====================================================================
    private GameObject _loadingIndicatorGO;
    private int _loadingRefCount;
    /// <summary>
    /// US3: 显示加载指示器(引用计数,支持重入)。
    /// </summary>
    public void ShowLoadingIndicator()
    {
        _loadingRefCount++;
        if (_loadingRefCount == 1)
        {
            EnsureLoadingIndicator();
            if (_loadingIndicatorGO != null)
            {
                _loadingIndicatorGO.SetActive(true);
            }
        }
    }
    /// <summary>
    /// US3: 隐藏加载指示器。
    /// </summary>
    public void HideLoadingIndicator()
    {
        _loadingRefCount = Mathf.Max(0, _loadingRefCount - 1);
        if (_loadingRefCount == 0 && _loadingIndicatorGO != null)
        {
            _loadingIndicatorGO.SetActive(false);
        }
    }
    private void EnsureLoadingIndicator()
    {
        if (_loadingIndicatorGO != null) return;
        // 创建简易加载指示器: 半透明遮罩 + 旋转图标
        var canvas = uiRoot != null ? uiRoot.GetComponentInChildren<Canvas>() : null;
        if (canvas == null) return;
        _loadingIndicatorGO = new GameObject("UILoadingIndicator");
        _loadingIndicatorGO.transform.SetParent(canvas.transform, false);
        // 全屏半透明遮罩
        var maskRT = _loadingIndicatorGO.AddComponent<RectTransform>();
        maskRT.anchorMin = Vector2.zero;
        maskRT.anchorMax = Vector2.one;
        maskRT.offsetMin = Vector2.zero;
        maskRT.offsetMax = Vector2.zero;
        var maskImage = _loadingIndicatorGO.AddComponent<UnityEngine.UI.Image>();
        maskImage.color = new Color(0, 0, 0, 0.3f);
        maskImage.raycastTarget = true; // 拦截点击
        // 加载提示文字
        var textGO = new GameObject("LoadingText");
        textGO.transform.SetParent(_loadingIndicatorGO.transform, false);
        var textRT = textGO.AddComponent<RectTransform>();
        textRT.anchorMin = new Vector2(0.5f, 0.5f);
        textRT.anchorMax = new Vector2(0.5f, 0.5f);
        textRT.sizeDelta = new Vector2(300, 60);
        var text = textGO.AddComponent<UnityEngine.UI.Text>();
        text.text = "Loading...";
        text.alignment = TextAnchor.MiddleCenter;
        text.fontSize = 28;
        text.color = Color.white;
        text.font = UnityEngine.Font.CreateDynamicFontFromOSFont("Arial", 28);
        // 确保在最上层
        var sortCanvas = _loadingIndicatorGO.AddComponent<Canvas>();
        sortCanvas.overrideSorting = true;
        sortCanvas.sortingOrder = 30000;
        _loadingIndicatorGO.AddComponent<UnityEngine.UI.GraphicRaycaster>();
        _loadingIndicatorGO.SetActive(false);
    }
    
    #endregion