| using System.Collections; | 
| using System.Collections.Generic; | 
| using UnityEngine; | 
| using DG.Tweening; | 
| using UnityEngine.UI; // DOTween 插件引用 | 
| using Cysharp.Threading.Tasks; | 
| using System; | 
|   | 
| public enum UILayer | 
| { | 
|     Static, // 静态UI 适合做 战斗 主界面 | 
|     Bottom, // 部分界面特殊处理层级用 | 
|     Mid,    // 大部分功能窗口都放这层,便于跳转上下层管理(一个界面可以同时存在多个) | 
|     System,  // 网络弹窗,信息提示等,其他重要弹窗 | 
|     Loading,    // 加载界面 | 
|   | 
| } | 
|   | 
| public enum UIAnimationType | 
| { | 
|     None,       // 无动画 | 
|     FadeInOut,  // 淡入淡出 | 
|     ScaleInOut, // 缩放 | 
|     SlideFromTop, // 从顶部滑入 | 
|     SlideFromBottom, // 从底部滑入 | 
|     SlideFromLeft, // 从左侧滑入 | 
|     SlideFromRight, // 从右侧滑入 | 
|     ScaleOverInOut,// 缩放根据曲线 | 
|   | 
| } | 
|   | 
| [RequireComponent(typeof(Canvas))] | 
| [RequireComponent(typeof(CanvasGroup))] | 
| [RequireComponent(typeof(CanvasScaler))] | 
| public class UIBase : MonoBehaviour | 
| { | 
|     #region 属性和变量 | 
|   | 
|     // UI基本属性 | 
|     [SerializeField] public UILayer uiLayer = UILayer.Mid; | 
|     [SerializeField][HideInInspector] public string uiName; | 
|     [SerializeField] public bool isMainUI = false; | 
|   | 
|     // 新增:是否支持父子关系 | 
|     // 父子关系:UI拥有的上下级链式关系 | 
|     // 在非特定情况下 都要拥有父子关系 (一般来说功能都要有父子关系 例外的是例如系统弹窗  | 
|     // 主界面这种不需要 在制作UI伤  目前的需求是 功能全部做在Middle层) | 
|     // 拥有父子关系 在关闭父界面的时候 子界面会连同一起关闭 子界面打开时 父界面的“回合数”会刷新 回合数详见持久化相关的注释 | 
|     //  | 
|     [SerializeField] public bool supportParentChildRelation = true;  | 
|   | 
|     // 持久化相关 | 
|     [SerializeField] public bool isPersistent = false; | 
|     [SerializeField][HideInInspector] public int maxIdleRounds = 20; | 
|   | 
|   | 
|     // 动画相关 | 
|     [SerializeField] public UIAnimationType openAnimationType = UIAnimationType.None; | 
|     [SerializeField] public UIAnimationType closeAnimationType = UIAnimationType.None; | 
|      | 
|     [SerializeField]/*[HideInInspector]*/ public float animeDuration = 0.2f; | 
|     [SerializeField]public TweenCurve scaleOverInOutCurve; | 
|     [SerializeField][HideInInspector] public Ease animationEase = Ease.OutQuad; // 确保使用 DG.Tweening.Ease | 
|   | 
|     // 运行时状态 | 
|     [HideInInspector] public int lastUsedRound = 0; | 
|     [HideInInspector] public UIBase parentUI; | 
|   | 
|     // 子UI管理 | 
|     [HideInInspector] public List<UIBase> childrenUI = new List<UIBase>(); | 
|   | 
|     [Header("所有UI排版应该在此节点内层")] | 
|     [SerializeField] protected RectTransform _rectTransform; //界面默认添加根节点用于表现界面开启关闭动画,或者设置适配用 | 
|   | 
|     //遮罩组件在界面开发中生成,遮罩开关和点击空白为其中的组件特性 | 
|     //  打开遮罩 | 
|     [Header("遮罩(透明)开关")] | 
|     [SerializeField] public bool openMask = false; | 
|   | 
|     //  默认点击空白区域关闭界面 | 
|     [Header("点击空白关闭")] | 
|     [SerializeField] public bool clickEmptySpaceClose = false; | 
|   | 
|     public GameObject screenMask = null; | 
|   | 
|     private Button btnClickEmptyClose; | 
|     public Action btnClickEmptyCloseEvent = null;   //提供点击空白区域关闭界面的回调 | 
|   | 
|     //  跟OneLevelWin联动 实际上是需要继承自OneLevelWin才能生效的值 使用需要注意 | 
|     int m_FunctionOrder = 0; | 
|     public int functionOrder | 
|     { | 
|         get { return m_FunctionOrder; } | 
|         set { m_FunctionOrder = value; } | 
|     } | 
|   | 
|     // 内部状态 | 
|     protected bool isActive = false; | 
|     protected bool isAnimating = false; | 
|     protected bool isClosing = false; // 新增:标记是否正在关闭 | 
|   | 
|     // 组件引用 | 
|     protected Canvas canvas; | 
|     protected CanvasGroup canvasGroup; | 
|   | 
|     // 动画相关 | 
|     protected Vector3 originalPosition; | 
|     protected Sequence currentAnimation; | 
|   | 
|     private CanvasScaler canvasScaler; | 
|   | 
|     #endregion | 
|   | 
|     #region Unity生命周期 | 
|   | 
|     protected virtual void Awake() | 
|     { | 
|         try | 
|         { | 
|             InitComponentInternal(); | 
|         } | 
|         catch (Exception e) | 
|         { | 
|             Debug.LogError($"{uiName}界面的InitComponentInternal报错: {e.StackTrace}"); | 
|         } | 
|   | 
|         try | 
|         { | 
|             InitComponent(); | 
|         } | 
|         catch (Exception e) | 
|         { | 
|             Debug.LogError($"{uiName}界面的InitComponent报错: {e.StackTrace}"); | 
|         } | 
|   | 
|         // 保存原始值用于动画 | 
|         if (_rectTransform != null) | 
|         { | 
|             originalPosition = _rectTransform.anchoredPosition; | 
|         } | 
|   | 
|         if (screenMask != null) | 
|             screenMask.transform.SetAsFirstSibling(); | 
|         InitClickEmptySpaceBtn(); | 
|   | 
|     } | 
|   | 
|     protected virtual void Start() | 
|     { | 
|         // 子类可以重写此方法进行额外初始化 | 
|     } | 
|   | 
|     protected async UniTask ApplyClickEmptySpaceClose() | 
|     { | 
|         if (clickEmptySpaceClose) | 
|         { | 
|             //延迟x帧后可点击,防止点击过快立即关闭了 | 
|             await UniTask.Delay(200); | 
|             btnClickEmptyClose.enabled = true; | 
|         } | 
|     } | 
|   | 
|     private void InitClickEmptySpaceBtn() | 
|     { | 
|         if (!clickEmptySpaceClose) | 
|         { | 
|             return; | 
|         } | 
|   | 
|         btnClickEmptyClose = screenMask.GetComponent<Button>(); | 
|   | 
|         btnClickEmptyClose.AddListener(() => | 
|         { | 
|             if (btnClickEmptyCloseEvent != null) | 
|             { | 
|                 btnClickEmptyCloseEvent(); | 
|             } | 
|             else | 
|             { | 
|                 CloseWindow(); | 
|             } | 
|         }); | 
|   | 
|         btnClickEmptyClose.enabled = false; | 
|     } | 
|   | 
|   | 
|     protected async void ExecuteNextFrame(Action _action) | 
|     { | 
|         await UniTask.DelayFrame(1); | 
|         _action?.Invoke(); | 
|     } | 
|   | 
|     protected virtual void OnDestroy() | 
|     { | 
|         // 确保动画被正确清理 | 
|         if (currentAnimation != null) | 
|         { | 
|             currentAnimation.Kill(); | 
|             currentAnimation = null; | 
|         } | 
|     } | 
|   | 
|     #endregion | 
|   | 
|     #region 初始化方法 | 
|   | 
|     private void InitComponentInternal() | 
|     { | 
|         // 获取或添加Canvas组件 | 
|         canvas = GetComponent<Canvas>(); | 
|         if (canvas == null) | 
|         { | 
|             canvas = gameObject.AddComponent<Canvas>(); | 
|         } | 
|   | 
|         // 设置Canvas属性 | 
|         canvas.overrideSorting = true; | 
|         canvas.worldCamera = CameraManager.uiCamera; | 
|   | 
|         canvas.sortingLayerID = SortingLayer.NameToID("UI"); // 确保使用正确的排序层 | 
|   | 
|         // 获取或添加CanvasGroup组件 | 
|         canvasGroup = GetComponent<CanvasGroup>(); | 
|         if (canvasGroup == null) | 
|         { | 
|             canvasGroup = gameObject.AddComponent<CanvasGroup>(); | 
|         } | 
|   | 
|         // 添加GraphicRaycaster组件(如果没有) | 
|         if (GetComponent<UnityEngine.UI.GraphicRaycaster>() == null) | 
|         { | 
|             gameObject.AddComponent<UnityEngine.UI.GraphicRaycaster>(); | 
|         } | 
|   | 
|         canvasScaler = GetComponent<CanvasScaler>(); | 
|         canvasScaler.referenceResolution = Constants.DESIGN_RESOLUTION; | 
|         canvasScaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize; | 
|         canvasScaler.matchWidthOrHeight = 0; | 
|     } | 
|   | 
|     // 获取必要的组件 | 
|     protected virtual void InitComponent() | 
|     { | 
|     } | 
|   | 
|     #endregion | 
|   | 
|     #region UI操作方法 | 
|   | 
|     // 设置UI层级 | 
|     public void SetSortingOrder(int order) | 
|     { | 
|         canvas.sortingOrder = order; | 
|     } | 
|   | 
|     protected virtual void OnPreOpen() | 
|     { | 
|         // 子类可以重写此方法进行额外的预打开操作 | 
|     } | 
|   | 
|     protected virtual void OnPreClose() | 
|     { | 
|         // 子类可以重写此方法进行额外的预关闭操作 | 
|     } | 
|   | 
|     // 打开UI | 
|     public void HandleOpen() | 
|     { | 
|         if (_rectTransform == null) | 
|         { | 
|             Debug.LogError($"界面: {uiName} 需要设置根节点_rectTransform "); | 
|             return; | 
|         } | 
|   | 
|         if ((clickEmptySpaceClose || openMask) && screenMask == null) | 
|         { | 
|             Debug.LogError($"界面: {uiName} 缺少遮罩 "); | 
|             return; | 
|         } | 
|   | 
|         try | 
|         { | 
|             OnPreOpen(); | 
|         } | 
|         catch (Exception e) | 
|         { | 
|             Debug.LogError($"{uiName}界面的OnPreOpen报错: {e.StackTrace}"); | 
|         } | 
|   | 
|         StopCurrentAnimation(); | 
|   | 
|         isClosing = false; | 
|         gameObject.SetActive(true); | 
|         isActive = true; | 
|   | 
|         PlayOpenAnimation(); | 
|   | 
|         // // 如果后续需要统一处理刘海或者小游戏的界面适配问题 | 
|         // _rectTransform.offsetMin = new Vector2(0, 10);  //下方 | 
|         // _rectTransform.offsetMax = new Vector2(0, -50); //上方 | 
|   | 
|         try | 
|         { | 
|             OnOpen(); | 
|         } | 
|         catch (Exception e) | 
|         { | 
|             Debug.LogError($"{uiName}界面的OnOpen报错: {e.StackTrace}"); | 
|         } | 
|   | 
|         ApplyClickEmptySpaceClose(); | 
|   | 
|         ExecuteNextFrame(() => | 
|         { | 
|             try | 
|             { | 
|                 NextFrameAfterOpen(); | 
|             } | 
|             catch (Exception e) | 
|             { | 
|                 Debug.LogError($"{uiName}界面的NextFrameAfterOpen报错: {e.StackTrace}"); | 
|             } | 
|         }); | 
|     } | 
|   | 
|     protected virtual void NextFrameAfterOpen() | 
|     { | 
|   | 
|     } | 
|   | 
|     // 关闭UI - 修改后的方法 | 
|     public void HandleClose() | 
|     { | 
|         // 如果已经在关闭过程中,直接返回 | 
|         if (isClosing) return; | 
|   | 
|         if (clickEmptySpaceClose) | 
|             btnClickEmptyClose.enabled = false; | 
|   | 
|         try | 
|         { | 
|             OnPreClose(); | 
|         } | 
|         catch (Exception e) | 
|         { | 
|             Debug.LogError($"{uiName}界面的OnPreClose报错: {e.StackTrace}"); | 
|         } | 
|   | 
|         StopCurrentAnimation(); | 
|   | 
|         isClosing = true; | 
|         isActive = false; | 
|   | 
|         if (canvasGroup != null) | 
|         { | 
|             canvasGroup.blocksRaycasts = false; | 
|         } | 
|   | 
|         PlayCloseAnimation(); | 
|   | 
|         try | 
|         { | 
|             OnClose(); | 
|         } | 
|         catch (Exception e) | 
|         { | 
|             Debug.LogError($"{uiName}界面的OnClose报错: {e.StackTrace}"); | 
|         } | 
|   | 
|         if (closeAnimationType == UIAnimationType.None) | 
|         { | 
|             try | 
|             { | 
|                 CompleteClose(); | 
|             } | 
|             catch (Exception e) | 
|             { | 
|                 Debug.LogError($"{uiName}界面的CompleteClose报错: {e.StackTrace}"); | 
|             } | 
|         } | 
|         // 否则在动画完成后禁用游戏对象(在PlayCloseAnimation中处理) | 
|     } | 
|   | 
|     // 完成关闭过程 - 新增方法 | 
|     protected virtual void CompleteClose() | 
|     { | 
|         gameObject.SetActive(false); | 
|         isClosing = false; | 
|     } | 
|   | 
|     public virtual void CloseWindow() | 
|     { | 
|         UIManager.Instance.CloseWindow(this, false); | 
|     } | 
|   | 
|     // 刷新UI | 
|     public virtual void Refresh() | 
|     { | 
|         // 子类可以重写此方法实现UI刷新逻辑 | 
|     } | 
|   | 
|     // 销毁UI | 
|     public virtual void Destroy() | 
|     { | 
|         // 停止所有动画 | 
|         StopCurrentAnimation(); | 
|   | 
|         ClearChildrenUI(); | 
|   | 
|         if (parentUI != null) | 
|         { | 
|             parentUI.RemoveChildUI(this); | 
|         } | 
|   | 
|         Destroy(gameObject); | 
|     } | 
|   | 
|     #endregion | 
|      | 
|     #region 特效相关 | 
|      | 
|     /// <summary> | 
|     /// 播放UI特效 | 
|     /// </summary> | 
|     /// <param name="id">特效资源名称</param> | 
|     /// <param name="parent">特效父节点,默认为当前UI</param> | 
|     /// <returns>特效游戏对象</returns> | 
|     public UIEffectPlayer PlayUIEffect(int id, Transform parent = null) | 
|     { | 
|         // 使用默认值 | 
|         if (parent == null) parent = transform; | 
|   | 
|         return UIEffectPlayer.CreateEffect(id, parent, false); | 
|     } | 
|      | 
|     #endregion | 
|   | 
|     public bool raycastTarget | 
|     { | 
|         get; | 
|         set; | 
|     } = true; | 
|   | 
|     public bool IsRaycastLocationValid(Vector2 sp, Camera eventCamera) | 
|     { | 
|         return raycastTarget; | 
|     } | 
|   | 
|     #region 动画方法 | 
|   | 
|     // 停止当前正在播放的动画 | 
|     protected void StopCurrentAnimation() | 
|     { | 
|         if (currentAnimation != null && currentAnimation.IsActive() && !currentAnimation.IsComplete()) | 
|         { | 
|             currentAnimation.Kill(false); // 不要完成动画,直接停止 | 
|             currentAnimation = null; | 
|         } | 
|         isAnimating = false; | 
|     } | 
|   | 
|     // 播放打开动画 | 
|     protected virtual void PlayOpenAnimation() | 
|     { | 
|         //禁用交互会引起点透问题, 后续解决可以考虑EventSystem | 
|         canvasGroup.blocksRaycasts = true; | 
|   | 
|         if (openAnimationType == UIAnimationType.None) | 
|         { | 
|             // 无动画,直接启用交互 | 
|             //  初始值设定好 | 
|             _ResetToBegin(); | 
|             return; | 
|         } | 
|   | 
|         isAnimating = true; | 
|   | 
|         // 初始化动画前的状态 | 
|         switch (openAnimationType) | 
|         { | 
|             case UIAnimationType.FadeInOut: | 
|                 if (canvasGroup != null) | 
|                 { | 
|                     canvasGroup.alpha = 0f; | 
|                     // canvasGroup.blocksRaycasts = false; | 
|                 } | 
|                 if (_rectTransform != null) | 
|                 { | 
|                     _rectTransform.localScale = Vector3.one; | 
|                 } | 
|                 break; | 
|   | 
|             case UIAnimationType.ScaleInOut: | 
|                 if (canvasGroup != null) | 
|                 { | 
|                     canvasGroup.alpha = 1f; | 
|                     // canvasGroup.blocksRaycasts = false; | 
|                 } | 
|                 if (_rectTransform != null) | 
|                 { | 
|                     _rectTransform.localScale = Vector3.one * 0.3f; | 
|                 } | 
|                 break; | 
|   | 
|             case UIAnimationType.SlideFromTop: | 
|                 _ResetToBegin(); | 
|                 if (_rectTransform != null) | 
|                 { | 
|                     Vector2 startPos = originalPosition; | 
|                     startPos.y = originalPosition.y + Screen.height; | 
|                     _rectTransform.anchoredPosition = startPos; | 
|                 } | 
|                 break; | 
|   | 
|             case UIAnimationType.SlideFromBottom: | 
|                 _ResetToBegin(); | 
|                 if (_rectTransform != null) | 
|                 { | 
|                     Vector2 startPos = originalPosition; | 
|                     startPos.y = originalPosition.y-Screen.height; | 
|                     _rectTransform.anchoredPosition = startPos; | 
|                 } | 
|                 break; | 
|   | 
|             case UIAnimationType.SlideFromLeft: | 
|                 _ResetToBegin(); | 
|                 if (_rectTransform != null) | 
|                 { | 
|                     Vector2 startPos = originalPosition; | 
|                     startPos.x = originalPosition.x-Screen.width; | 
|                     _rectTransform.anchoredPosition = startPos; | 
|                 } | 
|                 break; | 
|   | 
|             case UIAnimationType.SlideFromRight: | 
|                 _ResetToBegin(); | 
|                 if (_rectTransform != null) | 
|                 { | 
|                     Vector2 startPos = originalPosition; | 
|                     startPos.x = originalPosition.x+Screen.width; | 
|                     _rectTransform.anchoredPosition = startPos; | 
|                 } | 
|                 break; | 
|             case UIAnimationType.ScaleOverInOut: | 
|                 if (canvasGroup != null) | 
|                 { | 
|                     canvasGroup.alpha = 1f; | 
|                     // 禁用交互会引起点透问题 | 
|                     // canvasGroup.blocksRaycasts = false; | 
|                 } | 
|                 if (_rectTransform != null) | 
|                 { | 
|                     _rectTransform.localScale = Vector3.one * 0.3f; | 
|                 } | 
|                 break; | 
|         } | 
|   | 
|         try | 
|         { | 
|             // 创建动画序列 | 
|             currentAnimation = DOTween.Sequence(); | 
|   | 
|             // 添加动画 | 
|             switch (openAnimationType) | 
|             { | 
|                 case UIAnimationType.FadeInOut: | 
|                     if (canvasGroup != null) | 
|                     { | 
|                         currentAnimation.Append(canvasGroup.DOFade(1f, animeDuration).SetEase(animationEase)); | 
|                     } | 
|                     break; | 
|   | 
|                 case UIAnimationType.ScaleInOut: | 
|                     if (_rectTransform != null) | 
|                     { | 
|                         currentAnimation.Append(DOVirtual.Float(0.3f, 1f, animeDuration, (value) => {_rectTransform.localScale = Vector3.one * value;}).SetEase(animationEase)); | 
|                     } | 
|                     break; | 
|   | 
|                 case UIAnimationType.SlideFromTop: | 
|                 case UIAnimationType.SlideFromBottom: | 
|                 case UIAnimationType.SlideFromLeft: | 
|                 case UIAnimationType.SlideFromRight: | 
|                     if (_rectTransform != null) | 
|                     { | 
|                         currentAnimation.Append(_rectTransform.DOAnchorPos(originalPosition, animeDuration).SetEase(animationEase)); | 
|                     } | 
|                     break; | 
|                 case UIAnimationType.ScaleOverInOut: | 
|                     if (_rectTransform != null) | 
|                     { | 
|                         float startScale = scaleOverInOutCurve.curve.Evaluate(0f); | 
|                         _rectTransform.localScale = Vector3.one * startScale; | 
|                         currentAnimation.Append( | 
|                             DOTween.To( | 
|                                 () => _rectTransform.localScale.x, | 
|                                 (value) => _rectTransform.localScale = Vector3.one * value, | 
|                                 1f,  | 
|                                 animeDuration | 
|                             ) | 
|                             .SetEase(scaleOverInOutCurve.curve) | 
|                             .OnComplete(() => _rectTransform.localScale = Vector3.one) // 确保最终值1正确 | 
|                         ); | 
|                          | 
|                     } | 
|                     break; | 
|             } | 
|   | 
|             // 动画完成后的回调 | 
|             currentAnimation.OnComplete(() => | 
|             { | 
|                 isAnimating = false; | 
|                 _ResetToBegin(); | 
|                 OnOpenAnimationComplete(); | 
|   | 
|                 // 启用交互 | 
|                 if (canvasGroup != null) | 
|                 { | 
|                     canvasGroup.blocksRaycasts = true; | 
|                 } | 
|   | 
|             }); | 
|   | 
|             // currentAnimation.ingoreTimeScale = true; | 
|   | 
|             currentAnimation.Play(); | 
|         } | 
|         catch (System.Exception e) | 
|         { | 
|             Debug.LogError($"播放打开动画时出错: {e.StackTrace}"); | 
|   | 
|             // 出错时确保UI可见并可交互 | 
|             if (canvasGroup != null) | 
|             { | 
|                 canvasGroup.alpha = 1f; | 
|                 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 (_rectTransform != null) | 
|         { | 
|             _rectTransform.localScale = Vector3.one; | 
|         } | 
|   | 
|         if (_rectTransform != null) | 
|         { | 
|             _rectTransform.anchoredPosition = originalPosition; | 
|         } | 
|     } | 
|   | 
|     protected virtual void OnOpenAnimationComplete() | 
|     { | 
|   | 
|     } | 
|   | 
|     // 播放关闭动画 - 修改后的方法 | 
|     protected virtual void PlayCloseAnimation() | 
|     { | 
|         if (closeAnimationType == UIAnimationType.None) | 
|         { | 
|             // 无动画,直接返回,让HandleClose方法处理 | 
|             return; | 
|         } | 
|   | 
|         isAnimating = true; | 
|   | 
|         try | 
|         { | 
|             // 创建动画序列 | 
|             currentAnimation = DOTween.Sequence(); | 
|   | 
|             // 添加动画 | 
|             switch (closeAnimationType) | 
|             { | 
|                 case UIAnimationType.FadeInOut: | 
|                     if (canvasGroup != null) | 
|                     { | 
|                         currentAnimation.Append(canvasGroup.DOFade(0.1f, animeDuration).SetEase(animationEase)); | 
|                     } | 
|                     break; | 
|   | 
|                 case UIAnimationType.ScaleInOut: | 
|                 case UIAnimationType.ScaleOverInOut: | 
|                     if (_rectTransform != null) | 
|                     { | 
|                         currentAnimation.Append(DOVirtual.Float(1f, 0.3f, animeDuration, (value) => {_rectTransform.localScale = Vector3.one * value;}).SetEase(animationEase)); | 
|                     } | 
|                     break; | 
|   | 
|                 case UIAnimationType.SlideFromTop: | 
|                     if (_rectTransform != null) | 
|                     { | 
|                         Vector2 endPos = originalPosition; | 
|                         endPos.y = Screen.height; | 
|                         currentAnimation.Append(_rectTransform.DOAnchorPos(endPos, animeDuration).SetEase(animationEase)); | 
|                     } | 
|                     break; | 
|   | 
|                 case UIAnimationType.SlideFromBottom: | 
|                     if (_rectTransform != null) | 
|                     { | 
|                         Vector2 endPos = originalPosition; | 
|                         endPos.y = -Screen.height; | 
|                         currentAnimation.Append(_rectTransform.DOAnchorPos(endPos, animeDuration).SetEase(animationEase)); | 
|                     } | 
|                     break; | 
|   | 
|                 case UIAnimationType.SlideFromLeft: | 
|                     if (_rectTransform != null) | 
|                     { | 
|                         Vector2 endPos = originalPosition; | 
|                         endPos.x = -Screen.width; | 
|                         currentAnimation.Append(_rectTransform.DOAnchorPos(endPos, animeDuration).SetEase(animationEase)); | 
|                     } | 
|                     break; | 
|   | 
|                 case UIAnimationType.SlideFromRight: | 
|                     if (_rectTransform != null) | 
|                     { | 
|                         Vector2 endPos = originalPosition; | 
|                         endPos.x = Screen.width; | 
|                         currentAnimation.Append(_rectTransform.DOAnchorPos(endPos, animeDuration).SetEase(animationEase)); | 
|                     } | 
|                     break; | 
|             } | 
|   | 
|             // 动画完成后的回调 - 修改为调用CompleteClose | 
|             currentAnimation.OnComplete(() => | 
|             { | 
|                 isAnimating = false; | 
|                 if (isClosing) | 
|                 { | 
|                     try | 
|                     { | 
|                         CompleteClose(); | 
|                     } | 
|                     catch (Exception e) | 
|                     { | 
|                         Debug.LogError($"{uiName}界面的CompleteClose报错: {e.StackTrace}"); | 
|                     } | 
|                 } | 
|             }); | 
|   | 
|             currentAnimation.Play(); | 
|         } | 
|         catch (System.Exception e) | 
|         { | 
|             Debug.LogError($"播放关闭动画时出错: {e.StackTrace}"); | 
|              | 
|             // 出错时直接完成关闭 | 
|             isAnimating = false; | 
|             CompleteClose(); | 
|         } | 
|     } | 
|   | 
|     #endregion | 
|   | 
|     #region 子UI管理 | 
|   | 
|     // 添加子UI | 
|     public void AddChildUI(UIBase childUI) | 
|     { | 
|         if (childUI != null && !childrenUI.Contains(childUI)) | 
|         { | 
|             childrenUI.Add(childUI); | 
|             childUI.parentUI = this; | 
|         } | 
|     } | 
|   | 
|     // 移除子UI | 
|     public void RemoveChildUI(UIBase childUI) | 
|     { | 
|         if (childUI != null && childrenUI.Contains(childUI)) | 
|         { | 
|             childrenUI.Remove(childUI); | 
|             childUI.parentUI = null; | 
|         } | 
|     } | 
|   | 
|     // 清空所有子UI | 
|     public void ClearChildrenUI() | 
|     { | 
|         for (int i = childrenUI.Count - 1; i >= 0; i--) | 
|         { | 
|             if (childrenUI[i] != null) | 
|             { | 
|                 childrenUI[i].parentUI = null; | 
|             } | 
|         } | 
|   | 
|         childrenUI.Clear(); | 
|     } | 
|   | 
|     public bool IsActive() | 
|     { | 
|         return isActive; | 
|     } | 
|   | 
|     #endregion | 
|   | 
|     #region 回调方法 | 
|   | 
|     // UI打开时的回调 | 
|     protected virtual void OnOpen() | 
|     { | 
|         // 子类可以重写此方法实现打开UI时的逻辑 | 
|     } | 
|   | 
|     // UI关闭时的回调 | 
|     protected virtual void OnClose() | 
|     { | 
|         // 子类可以重写此方法实现关闭UI时的逻辑 | 
|     } | 
|   | 
|     #endregion | 
| } |