hch
8 天以前 754995562ce45ca4acd7897eec9b2b4667a4fd2a
Main/System/UIBase/UIBase.cs
@@ -24,7 +24,9 @@
    SlideFromTop, // 从顶部滑入
    SlideFromBottom, // 从底部滑入
    SlideFromLeft, // 从左侧滑入
    SlideFromRight // 从右侧滑入
    SlideFromRight, // 从右侧滑入
    ScaleOverInOut,// 缩放(超过)之后再返回
}
[RequireComponent(typeof(Canvas))]
@@ -45,9 +47,12 @@
    [SerializeField][HideInInspector] public int maxIdleRounds = 20;
    // 动画相关
    [SerializeField] public UIAnimationType openAnimationType = UIAnimationType.ScaleInOut;
    [SerializeField] public UIAnimationType openAnimationType = UIAnimationType.None;
    [SerializeField] public UIAnimationType closeAnimationType = UIAnimationType.None;
    [SerializeField] protected RectTransform _rectTransform; //界面默认添加根节点用于表现界面开启关闭动画,或者设置适配用
    [SerializeField]/*[HideInInspector]*/ public float animeDuration = 0.2f;
    [SerializeField]public TweenCurve scaleOverInOutCurve;
    [SerializeField][HideInInspector] public Ease animationEase = Ease.OutQuad; // 确保使用 DG.Tweening.Ease
    // 运行时状态
@@ -67,7 +72,13 @@
    private Button btnClickEmptyClose;
    protected int functionOrder = 0;
    //  跟OneLevelWin联动 实际上是需要继承自OneLevelWin才能生效的值 使用需要注意
    int m_FunctionOrder = 0;
    public int functionOrder
    {
        get { return m_FunctionOrder; }
        set { m_FunctionOrder = value; }
    }
    // 内部状态
    protected bool isActive = false;
@@ -77,10 +88,8 @@
    // 组件引用
    protected Canvas canvas;
    protected CanvasGroup canvasGroup;
    protected RectTransform rectTransform;
    // 动画相关
    protected Vector3 originalScale;
    protected Vector3 originalPosition;
    protected Sequence currentAnimation;
@@ -92,17 +101,15 @@
    protected virtual void Awake()
    {
        // 确保 DOTween 已初始化
        DOTween.SetTweensCapacity(500, 50);
        //  防止有人不写base.InitComponent引发错误 所以拆分
        InitComponentInternal();
        // 在Awake中进行基本初始化
        InitComponent();
        // 保存原始值用于动画
        if (rectTransform != null)
        if (_rectTransform != null)
        {
            originalScale = rectTransform.localScale;
            originalPosition = rectTransform.anchoredPosition;
            originalPosition = _rectTransform.anchoredPosition;
        }
        ApplySettings();
@@ -123,20 +130,20 @@
    protected async UniTask ApplySettings()
    {
        await UniTask.DelayFrame(5);
        if (null != transform)
        if (clickEmptySpaceClose)
        {
            if (clickEmptySpaceClose)
            {
                GameObject goBtnESC = GameObject.Instantiate(Resources.Load<GameObject>("Prefabs/ClickEmptyCloseMask"), transform);
                //  Load
                btnClickEmptyClose = goBtnESC.GetComponent<Button>();
                btnClickEmptyClose.AddListener(CloseWindow);
                btnClickEmptyClose.transform.SetAsFirstSibling();
            }
            //延迟创建会导致层级在ScreenMask之上
            GameObject goBtnESC = GameObject.Instantiate(Resources.Load<GameObject>("Prefabs/ClickEmptyCloseMask"), transform);
            btnClickEmptyClose = goBtnESC.GetComponent<Button>();
            btnClickEmptyClose.AddListener(CloseWindow);
            btnClickEmptyClose.transform.SetAsFirstSibling();
            await UniTask.DelayFrame(5);
            btnClickEmptyClose = goBtnESC.GetComponent<Button>();
            btnClickEmptyClose.AddListener(CloseWindow);
        }
    }
    protected async void ExecuteNextFrame(Action _action)
    {
@@ -158,8 +165,7 @@
    #region 初始化方法
    // 获取必要的组件
    protected virtual void InitComponent()
    private void InitComponentInternal()
    {
        // 获取或添加Canvas组件
        canvas = GetComponent<Canvas>();
@@ -170,6 +176,8 @@
        // 设置Canvas属性
        canvas.overrideSorting = true;
        canvas.worldCamera = CameraManager.uiCamera;
        // 获取或添加CanvasGroup组件
        canvasGroup = GetComponent<CanvasGroup>();
@@ -185,9 +193,14 @@
        }
        canvasScaler = GetComponent<CanvasScaler>();
        canvasScaler.referenceResolution = Constants.DESIGN_RESOLUTION;
        canvasScaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
        canvasScaler.matchWidthOrHeight = 0;
    }
        // 获取RectTransform组件
        rectTransform = GetComponent<RectTransform>();
    // 获取必要的组件
    protected virtual void InitComponent()
    {
    }
    #endregion
@@ -197,10 +210,7 @@
    // 设置UI层级
    public void SetSortingOrder(int order)
    {
        if (canvas != null)
        {
            canvas.sortingOrder = order;
        }
        canvas.sortingOrder = order;
    }
    protected virtual void OnPreOpen()
@@ -216,6 +226,12 @@
    // 打开UI
    public void HandleOpen()
    {
        if (_rectTransform == null)
        {
            Debug.LogError($"界面: {uiName} 需要设置根节点_rectTransform ");
            return;
        }
        OnPreOpen();
        // 如果正在播放动画,先停止
        StopCurrentAnimation();
@@ -230,6 +246,13 @@
        PlayOpenAnimation();
        OnOpen();
        ExecuteNextFrame(NextFrameAfterOpen);
    }
    protected virtual void NextFrameAfterOpen()
    {
    }
    // 关闭UI - 修改后的方法
@@ -250,7 +273,6 @@
        // 禁用交互但保持可见
        if (canvasGroup != null)
        {
            canvasGroup.interactable = false;
            canvasGroup.blocksRaycasts = false;
        }
@@ -314,139 +336,21 @@
    /// <param name="autoDestroy">是否自动销毁,默认为true</param>
    /// <param name="destroyDelay">自动销毁延迟时间,默认为5秒</param>
    /// <returns>特效游戏对象</returns>
    public async UniTask<GameObject> PlayUIEffect(int id, Transform parent = null, bool autoDestroy = true, float destroyDelay = 5f)
    public EffectPlayer PlayUIEffect(int id, Transform parent = null, bool autoDestroy = true, float destroyDelay = 5f)
    {
        // 使用默认值
        if (parent == null) parent = transform;
        
        EffectConfig effectCfg = EffectConfig.Get(id);
        EffectPlayer player = parent.gameObject.AddComponent<EffectPlayer>();
        if (null == effectCfg)
        {
            return null;
        }
        player.effectId = id;
        player.autoDestroy = autoDestroy;
        player.destroyDelay = destroyDelay;
        player.canvas = canvas;
        // 加载特效资源
        var effectPrefab = ResManager.Instance.LoadAsset<GameObject>("UIEffect/" + effectCfg.packageName, effectCfg.fxName);
        if (effectPrefab == null)
        {
            Debug.LogError($"加载UI特效失败: {effectCfg.packageName}");
            return null;
        }
        // 实例化特效
        GameObject effectObj = Instantiate(effectPrefab, parent);
        effectObj.name = $"Effect_{effectCfg.packageName}";
        // 添加特效穿透阻挡器
        EffectPenetrationBlocker blocker = effectObj.AddComponent<EffectPenetrationBlocker>();
        blocker.parentCanvas = canvas;
        blocker.UpdateSortingOrder();
        // 自动销毁
        if (autoDestroy)
        {
            Destroy(effectObj, destroyDelay);
        }
        return effectObj;
        return player;
    }
    
    /// <summary>
    /// 在两个UI元素之间播放特效(按照sortingOrder的中间值)
    /// </summary>
    /// <param name="effectName">特效资源名称</param>
    /// <param name="frontElement">前景UI元素(Image或RawImage)</param>
    /// <param name="backElement">背景UI元素(Image或RawImage)</param>
    /// <param name="autoDestroy">是否自动销毁,默认为true</param>
    /// <param name="destroyDelay">自动销毁延迟时间,默认为5秒</param>
    /// <returns>特效游戏对象</returns>
    public async UniTask<GameObject> PlayEffectBetweenUIElements(string effectName, Graphic frontElement, Graphic backElement, bool autoDestroy = true, float destroyDelay = 5f)
    {
        if (frontElement == null || backElement == null)
        {
            Debug.LogError("前景或背景UI元素为空");
            return null;
        }
        // 确保UI元素在当前UIBase的Canvas下
        if (frontElement.canvas != canvas || backElement.canvas != canvas)
        {
            Debug.LogError("UI元素不在当前UIBase的Canvas下");
            return null;
        }
        // 加载特效资源
        GameObject effectPrefab = ResManager.Instance.LoadAsset<GameObject>("UIEffect", effectName);
        if (effectPrefab == null)
        {
            Debug.LogError($"加载UI特效失败: {effectName}");
            return null;
        }
        // 创建一个新的GameObject作为特效容器
        GameObject container = new GameObject($"EffectContainer_{effectName}");
        container.transform.SetParent(transform, false);
        // 设置容器位置
        RectTransform containerRect = container.AddComponent<RectTransform>();
        containerRect.anchorMin = new Vector2(0.5f, 0.5f);
        containerRect.anchorMax = new Vector2(0.5f, 0.5f);
        containerRect.pivot = new Vector2(0.5f, 0.5f);
        containerRect.anchoredPosition = Vector2.zero;
        containerRect.sizeDelta = new Vector2(100, 100); // 默认大小,可以根据需要调整
        // 获取前景和背景元素的siblingIndex
        int frontIndex = frontElement.transform.GetSiblingIndex();
        int backIndex = backElement.transform.GetSiblingIndex();
        // 设置特效容器的siblingIndex在两者之间
        if (frontIndex > backIndex)
        {
            // 前景在背景之后,特效应该在中间
            container.transform.SetSiblingIndex((frontIndex + backIndex) / 2 + 1);
        }
        else
        {
            // 背景在前景之后,特效应该在中间
            container.transform.SetSiblingIndex((frontIndex + backIndex) / 2);
        }
        // 实例化特效
        GameObject effectObj = Instantiate(effectPrefab, container.transform);
        effectObj.name = $"Effect_{effectName}";
        // 添加特效穿透阻挡器
        EffectPenetrationBlocker blocker = effectObj.AddComponent<EffectPenetrationBlocker>();
        // 直接设置特效渲染器的排序顺序
        Renderer[] renderers = effectObj.GetComponentsInChildren<Renderer>(true);
        foreach (Renderer renderer in renderers)
        {
            renderer.sortingOrder = canvas.sortingOrder;
            renderer.sortingLayerName = canvas.sortingLayerName;
        }
        // 设置粒子系统渲染器的排序顺序
        ParticleSystem[] particleSystems = effectObj.GetComponentsInChildren<ParticleSystem>(true);
        foreach (ParticleSystem ps in particleSystems)
        {
            ParticleSystemRenderer psRenderer = ps.GetComponent<ParticleSystemRenderer>();
            if (psRenderer != null)
            {
                psRenderer.sortingOrder = canvas.sortingOrder;
                psRenderer.sortingLayerName = canvas.sortingLayerName;
            }
        }
        // 自动销毁
        if (autoDestroy)
        {
            Destroy(container, destroyDelay);
        }
        return effectObj;
    }
    #endregion
    public bool raycastTarget
@@ -476,15 +380,13 @@
    // 播放打开动画
    protected virtual void PlayOpenAnimation()
    {
        if (openAnimationType == UIAnimationType.None)
        {
            // 无动画,直接启用交互
            if (canvasGroup != null)
            {
                canvasGroup.alpha = 1f;
                canvasGroup.interactable = true;
                canvasGroup.blocksRaycasts = true;
            }
            //  初始值设定好
            _ResetToBegin();
            return;
        }
@@ -497,77 +399,74 @@
                if (canvasGroup != null)
                {
                    canvasGroup.alpha = 0f;
                    canvasGroup.interactable = false;
                    canvasGroup.blocksRaycasts = false;
                }
                if (canvasScaler != null)
                {
                    canvasScaler.scaleFactor = 1f;
                }
                break;
            case UIAnimationType.ScaleInOut:
                if (rectTransform != null)
                if (canvasGroup != null)
                {
                    canvasGroup.alpha = 1f;
                    canvasGroup.blocksRaycasts = false;
                }
                if (canvasScaler != null)
                {
                    canvasScaler.scaleFactor = 0.3f;
                    canvasGroup.interactable = false;
                    canvasGroup.blocksRaycasts = false;
                }
                break;
            case UIAnimationType.SlideFromTop:
                if (rectTransform != null)
                _ResetToBegin();
                if (_rectTransform != null)
                {
                    Vector2 startPos = originalPosition;
                    startPos.y = Screen.height;
                    rectTransform.anchoredPosition = startPos;
                }
                if (canvasGroup != null)
                {
                    canvasGroup.alpha = 0f;
                    canvasGroup.interactable = false;
                    canvasGroup.blocksRaycasts = false;
                    startPos.y = originalPosition.y + Screen.height;
                    _rectTransform.anchoredPosition = startPos;
                }
                break;
            case UIAnimationType.SlideFromBottom:
                if (rectTransform != null)
                _ResetToBegin();
                if (_rectTransform != null)
                {
                    Vector2 startPos = originalPosition;
                    startPos.y = -Screen.height;
                    rectTransform.anchoredPosition = startPos;
                }
                if (canvasGroup != null)
                {
                    canvasGroup.alpha = 0f;
                    canvasGroup.interactable = false;
                    canvasGroup.blocksRaycasts = false;
                    startPos.y = originalPosition.y-Screen.height;
                    _rectTransform.anchoredPosition = startPos;
                }
                break;
            case UIAnimationType.SlideFromLeft:
                if (rectTransform != null)
                _ResetToBegin();
                if (_rectTransform != null)
                {
                    Vector2 startPos = originalPosition;
                    startPos.x = -Screen.width;
                    rectTransform.anchoredPosition = startPos;
                }
                if (canvasGroup != null)
                {
                    canvasGroup.alpha = 0f;
                    canvasGroup.interactable = false;
                    canvasGroup.blocksRaycasts = false;
                    startPos.x = originalPosition.x-Screen.width;
                    _rectTransform.anchoredPosition = startPos;
                }
                break;
            case UIAnimationType.SlideFromRight:
                if (rectTransform != null)
                _ResetToBegin();
                if (_rectTransform != null)
                {
                    Vector2 startPos = originalPosition;
                    startPos.x = Screen.width;
                    rectTransform.anchoredPosition = startPos;
                    startPos.x = originalPosition.x+Screen.width;
                    _rectTransform.anchoredPosition = startPos;
                }
                break;
            case UIAnimationType.ScaleOverInOut:
                if (canvasGroup != null)
                {
                    canvasGroup.alpha = 0f;
                    canvasGroup.interactable = false;
                    canvasGroup.alpha = 1f;
                    canvasGroup.blocksRaycasts = false;
                }
                if (canvasScaler != null)
                {
                    canvasScaler.scaleFactor = 0.3f;
                }
                break;
        }
@@ -588,9 +487,9 @@
                    break;
                case UIAnimationType.ScaleInOut:
                    if (rectTransform != null)
                    if (_rectTransform != null)
                    {
                        currentAnimation.Append(DOVirtual.Float(0.3f, originalScale.x, animeDuration, (value) => {canvasScaler.scaleFactor = value;}).SetEase(animationEase));
                        currentAnimation.Append(DOVirtual.Float(0.3f, 1f, animeDuration, (value) => {canvasScaler.scaleFactor = value;}).SetEase(animationEase));
                    }
                    break;
@@ -598,13 +497,27 @@
                case UIAnimationType.SlideFromBottom:
                case UIAnimationType.SlideFromLeft:
                case UIAnimationType.SlideFromRight:
                    if (rectTransform != null)
                    if (_rectTransform != null)
                    {
                        currentAnimation.Append(rectTransform.DOAnchorPos(originalPosition, animeDuration).SetEase(animationEase));
                        currentAnimation.Append(_rectTransform.DOAnchorPos(originalPosition, animeDuration).SetEase(animationEase));
                    }
                    if (canvasGroup != null)
                    break;
                case UIAnimationType.ScaleOverInOut:
                    if (_rectTransform != null)
                    {
                        currentAnimation.Join(canvasGroup.DOFade(1f, animeDuration).SetEase(animationEase));
                        if (null == scaleOverInOutCurve)
                        {
                            currentAnimation.Append(DOVirtual.Float(0.3f, 1.2f, animeDuration,
                            (value) => {canvasScaler.scaleFactor = value;}).SetEase(animationEase));
                        }
                        else
                        {
                            currentAnimation.Append(DOVirtual.Float(0.3f, 1.2f, animeDuration,
                                (value) => {canvasScaler.scaleFactor = value;}).SetEase(scaleOverInOutCurve.curve));
                        }
                        // currentAnimation.Append(DOVirtual.Float(1.2f, 1f, 0.1f, (value) => {canvasScaler.scaleFactor = value;}).SetEase(scaleOverInOutCurve));
                    }
                    break;
            }
@@ -613,14 +526,18 @@
            currentAnimation.OnComplete(() =>
            {
                isAnimating = false;
                _ResetToBegin();
                OnOpenAnimationComplete();
                // 启用交互
                if (canvasGroup != null)
                {
                    canvasGroup.interactable = true;
                    canvasGroup.blocksRaycasts = true;
                }
            });
            // currentAnimation.ingoreTimeScale = true;
            currentAnimation.Play();
        }
@@ -632,11 +549,38 @@
            if (canvasGroup != null)
            {
                canvasGroup.alpha = 1f;
                canvasGroup.interactable = true;
                canvasGroup.blocksRaycasts = true;
            }
            isAnimating = false;
        }
    }
    // private void Update()
    // {
    //     Debug.LogError(_rectTransform.parent.name + ":" + _rectTransform.anchoredPosition);
    // }
    private void _ResetToBegin()
    {
        if (canvasGroup != null)
        {
            canvasGroup.alpha = 1f;
            canvasGroup.blocksRaycasts = true;
        }
        if (canvasScaler != null)
        {
            canvasScaler.scaleFactor = 1f;
        }
        if (_rectTransform != null)
        {
            _rectTransform.anchoredPosition = originalPosition;
        }
    }
    protected virtual void OnOpenAnimationComplete()
    {
    }
    // 播放关闭动画 - 修改后的方法
@@ -661,66 +605,51 @@
                case UIAnimationType.FadeInOut:
                    if (canvasGroup != null)
                    {
                        currentAnimation.Append(canvasGroup.DOFade(0f, animeDuration).SetEase(animationEase));
                        currentAnimation.Append(canvasGroup.DOFade(0.1f, animeDuration).SetEase(animationEase));
                    }
                    break;
                case UIAnimationType.ScaleInOut:
                    if (rectTransform != null)
                case UIAnimationType.ScaleOverInOut:
                    if (_rectTransform != null)
                    {
                        currentAnimation.Append(DOVirtual.Float(originalScale.x, 0.3f, animeDuration, (value) => {canvasScaler.scaleFactor = value;}).SetEase(animationEase));
                        currentAnimation.Append(DOVirtual.Float(1f, 0.3f, animeDuration, (value) => {canvasScaler.scaleFactor = value;}).SetEase(animationEase));
                    }
                    break;
                case UIAnimationType.SlideFromTop:
                    if (rectTransform != null)
                    if (_rectTransform != null)
                    {
                        Vector2 endPos = originalPosition;
                        endPos.y = Screen.height;
                        currentAnimation.Append(rectTransform.DOAnchorPos(endPos, animeDuration).SetEase(animationEase));
                    }
                    if (canvasGroup != null)
                    {
                        currentAnimation.Join(canvasGroup.DOFade(0f, animeDuration).SetEase(animationEase));
                        currentAnimation.Append(_rectTransform.DOAnchorPos(endPos, animeDuration).SetEase(animationEase));
                    }
                    break;
                case UIAnimationType.SlideFromBottom:
                    if (rectTransform != null)
                    if (_rectTransform != null)
                    {
                        Vector2 endPos = originalPosition;
                        endPos.y = -Screen.height;
                        currentAnimation.Append(rectTransform.DOAnchorPos(endPos, animeDuration).SetEase(animationEase));
                    }
                    if (canvasGroup != null)
                    {
                        currentAnimation.Join(canvasGroup.DOFade(0f, animeDuration).SetEase(animationEase));
                        currentAnimation.Append(_rectTransform.DOAnchorPos(endPos, animeDuration).SetEase(animationEase));
                    }
                    break;
                case UIAnimationType.SlideFromLeft:
                    if (rectTransform != null)
                    if (_rectTransform != null)
                    {
                        Vector2 endPos = originalPosition;
                        endPos.x = -Screen.width;
                        currentAnimation.Append(rectTransform.DOAnchorPos(endPos, animeDuration).SetEase(animationEase));
                    }
                    if (canvasGroup != null)
                    {
                        currentAnimation.Join(canvasGroup.DOFade(0f, animeDuration).SetEase(animationEase));
                        currentAnimation.Append(_rectTransform.DOAnchorPos(endPos, animeDuration).SetEase(animationEase));
                    }
                    break;
                case UIAnimationType.SlideFromRight:
                    if (rectTransform != null)
                    if (_rectTransform != null)
                    {
                        Vector2 endPos = originalPosition;
                        endPos.x = Screen.width;
                        currentAnimation.Append(rectTransform.DOAnchorPos(endPos, animeDuration).SetEase(animationEase));
                    }
                    if (canvasGroup != null)
                    {
                        currentAnimation.Join(canvasGroup.DOFade(0f, animeDuration).SetEase(animationEase));
                        currentAnimation.Append(_rectTransform.DOAnchorPos(endPos, animeDuration).SetEase(animationEase));
                    }
                    break;
            }