using System.Collections;
|
using System.Collections.Generic;
|
using UnityEngine;
|
using DG.Tweening; // DOTween 插件引用
|
|
public enum UILayer
|
{
|
Static, // 静态UI 适合做 常驻用的如 主界面
|
Bottom, // 最底层 适合做 主界面上面的 一级窗口 一级界面
|
Mid, // 适合做 二级窗口 二级界面
|
Top, // 适合做提示弹窗
|
System // 网络弹窗/其他重要弹窗/系统公告
|
}
|
|
public enum UIAnimationType
|
{
|
None, // 无动画
|
FadeInOut, // 淡入淡出
|
ScaleInOut, // 缩放
|
SlideFromTop, // 从顶部滑入
|
SlideFromBottom, // 从底部滑入
|
SlideFromLeft, // 从左侧滑入
|
SlideFromRight // 从右侧滑入
|
}
|
|
public class UIBase : MonoBehaviour
|
{
|
#region 属性和变量
|
|
// UI基本属性
|
[SerializeField] public UILayer uiLayer = UILayer.Mid;
|
[SerializeField] public string uiName;
|
[SerializeField] public bool isMainUI = false;
|
|
// 持久化相关
|
[SerializeField] public bool isPersistent = false;
|
[SerializeField] public int maxIdleRounds = 20;
|
|
// 动画相关
|
[SerializeField] public UIAnimationType openAnimationType = UIAnimationType.FadeInOut;
|
[SerializeField] public UIAnimationType closeAnimationType = UIAnimationType.FadeInOut;
|
[SerializeField] public float animationDuration = 0.3f;
|
[SerializeField] 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>();
|
|
// 内部状态
|
protected bool isActive = false;
|
protected bool isAnimating = false;
|
|
// 组件引用
|
protected Canvas canvas;
|
protected CanvasGroup canvasGroup;
|
protected RectTransform rectTransform;
|
|
// 动画相关
|
protected Vector3 originalScale;
|
protected Vector3 originalPosition;
|
protected Sequence currentAnimation;
|
|
#endregion
|
|
#region Unity生命周期
|
|
protected virtual void Awake()
|
{
|
// 确保 DOTween 已初始化
|
DOTween.SetTweensCapacity(500, 50);
|
|
// 在Awake中进行基本初始化
|
InitComponent();
|
|
// 保存原始值用于动画
|
if (rectTransform != null)
|
{
|
originalScale = rectTransform.localScale;
|
originalPosition = rectTransform.anchoredPosition;
|
}
|
}
|
|
protected virtual void Start()
|
{
|
// 子类可以重写此方法进行额外初始化
|
}
|
|
protected virtual void OnDestroy()
|
{
|
// 确保动画被正确清理
|
if (currentAnimation != null)
|
{
|
currentAnimation.Kill();
|
currentAnimation = null;
|
}
|
}
|
|
#endregion
|
|
#region 初始化方法
|
|
// 获取必要的组件
|
protected virtual void InitComponent()
|
{
|
// 获取或添加Canvas组件
|
canvas = GetComponent<Canvas>();
|
if (canvas == null)
|
{
|
canvas = gameObject.AddComponent<Canvas>();
|
}
|
|
// 设置Canvas属性
|
canvas.overrideSorting = true;
|
|
// 获取或添加CanvasGroup组件
|
canvasGroup = GetComponent<CanvasGroup>();
|
if (canvasGroup == null)
|
{
|
canvasGroup = gameObject.AddComponent<CanvasGroup>();
|
}
|
|
// 添加GraphicRaycaster组件(如果没有)
|
if (GetComponent<UnityEngine.UI.GraphicRaycaster>() == null)
|
{
|
gameObject.AddComponent<UnityEngine.UI.GraphicRaycaster>();
|
}
|
|
// 获取RectTransform组件
|
rectTransform = GetComponent<RectTransform>();
|
}
|
|
// 初始化UI
|
public virtual void Init()
|
{
|
// 确保组件已初始化
|
InitComponent();
|
}
|
|
#endregion
|
|
#region UI操作方法
|
|
// 设置UI层级
|
public void SetSortingOrder(int order)
|
{
|
if (canvas != null)
|
{
|
canvas.sortingOrder = order;
|
}
|
}
|
|
// 打开UI
|
public virtual void Open()
|
{
|
// 如果正在播放动画,先停止
|
StopCurrentAnimation();
|
|
gameObject.SetActive(true);
|
|
// 根据动画类型播放打开动画
|
PlayOpenAnimation();
|
|
isActive = true;
|
OnOpen();
|
}
|
|
// 关闭UI
|
public virtual void Close()
|
{
|
// 如果正在播放动画,先停止
|
StopCurrentAnimation();
|
|
// 根据动画类型播放关闭动画
|
PlayCloseAnimation();
|
|
isActive = false;
|
OnClose();
|
|
// 关闭所有子UI
|
for (int i = childrenUI.Count - 1; i >= 0; i--)
|
{
|
if (childrenUI[i] != null && childrenUI[i].isActive)
|
{
|
childrenUI[i].Close();
|
}
|
}
|
}
|
|
// 刷新UI
|
public virtual void Refresh()
|
{
|
// 子类可以重写此方法实现UI刷新逻辑
|
}
|
|
// 销毁UI
|
public virtual void Destroy()
|
{
|
// 停止所有动画
|
StopCurrentAnimation();
|
|
ClearChildrenUI();
|
|
if (parentUI != null)
|
{
|
parentUI.RemoveChildUI(this);
|
}
|
|
Destroy(gameObject);
|
}
|
|
#endregion
|
|
#region 动画方法
|
|
// 停止当前正在播放的动画
|
protected void StopCurrentAnimation()
|
{
|
if (currentAnimation != null && currentAnimation.IsActive() && !currentAnimation.IsComplete())
|
{
|
currentAnimation.Kill(false); // 不要完成动画,直接停止
|
currentAnimation = null;
|
}
|
isAnimating = false;
|
}
|
|
// 播放打开动画
|
protected virtual void PlayOpenAnimation()
|
{
|
if (openAnimationType == UIAnimationType.None)
|
{
|
// 无动画,直接启用交互
|
if (canvasGroup != null)
|
{
|
canvasGroup.alpha = 1f;
|
canvasGroup.interactable = true;
|
canvasGroup.blocksRaycasts = true;
|
}
|
return;
|
}
|
|
isAnimating = true;
|
|
// 初始化动画前的状态
|
switch (openAnimationType)
|
{
|
case UIAnimationType.FadeInOut:
|
if (canvasGroup != null)
|
{
|
canvasGroup.alpha = 0f;
|
canvasGroup.interactable = false;
|
canvasGroup.blocksRaycasts = false;
|
}
|
break;
|
|
case UIAnimationType.ScaleInOut:
|
if (rectTransform != null)
|
{
|
rectTransform.localScale = Vector3.zero;
|
}
|
if (canvasGroup != null)
|
{
|
canvasGroup.alpha = 0f;
|
canvasGroup.interactable = false;
|
canvasGroup.blocksRaycasts = false;
|
}
|
break;
|
|
case UIAnimationType.SlideFromTop:
|
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;
|
}
|
break;
|
|
case UIAnimationType.SlideFromBottom:
|
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;
|
}
|
break;
|
|
case UIAnimationType.SlideFromLeft:
|
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;
|
}
|
break;
|
|
case UIAnimationType.SlideFromRight:
|
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;
|
}
|
break;
|
}
|
|
try
|
{
|
// 创建动画序列
|
currentAnimation = DOTween.Sequence();
|
|
// 添加动画
|
switch (openAnimationType)
|
{
|
case UIAnimationType.FadeInOut:
|
if (canvasGroup != null)
|
{
|
currentAnimation.Append(canvasGroup.DOFade(1f, animationDuration).SetEase(animationEase));
|
}
|
break;
|
|
case UIAnimationType.ScaleInOut:
|
if (rectTransform != null)
|
{
|
currentAnimation.Append(rectTransform.DOScale(originalScale, animationDuration).SetEase(animationEase));
|
}
|
if (canvasGroup != null)
|
{
|
currentAnimation.Join(canvasGroup.DOFade(1f, animationDuration).SetEase(animationEase));
|
}
|
break;
|
|
case UIAnimationType.SlideFromTop:
|
case UIAnimationType.SlideFromBottom:
|
case UIAnimationType.SlideFromLeft:
|
case UIAnimationType.SlideFromRight:
|
if (rectTransform != null)
|
{
|
currentAnimation.Append(rectTransform.DOAnchorPos(originalPosition, animationDuration).SetEase(animationEase));
|
}
|
if (canvasGroup != null)
|
{
|
currentAnimation.Join(canvasGroup.DOFade(1f, animationDuration).SetEase(animationEase));
|
}
|
break;
|
}
|
|
// 动画完成后的回调
|
currentAnimation.OnComplete(() => {
|
isAnimating = false;
|
|
// 启用交互
|
if (canvasGroup != null)
|
{
|
canvasGroup.interactable = true;
|
canvasGroup.blocksRaycasts = true;
|
}
|
});
|
}
|
catch (System.Exception e)
|
{
|
Debug.LogError($"播放打开动画时出错: {e.Message}");
|
|
// 出错时确保UI可见并可交互
|
if (canvasGroup != null)
|
{
|
canvasGroup.alpha = 1f;
|
canvasGroup.interactable = true;
|
canvasGroup.blocksRaycasts = true;
|
}
|
isAnimating = false;
|
}
|
}
|
|
// 播放关闭动画
|
protected virtual void PlayCloseAnimation()
|
{
|
if (closeAnimationType == UIAnimationType.None)
|
{
|
// 无动画,直接禁用交互
|
if (canvasGroup != null)
|
{
|
canvasGroup.alpha = 0f;
|
canvasGroup.interactable = false;
|
canvasGroup.blocksRaycasts = false;
|
}
|
return;
|
}
|
|
isAnimating = true;
|
|
// 禁用交互,但保持可见
|
if (canvasGroup != null)
|
{
|
canvasGroup.interactable = false;
|
canvasGroup.blocksRaycasts = false;
|
}
|
|
try
|
{
|
// 创建动画序列
|
currentAnimation = DOTween.Sequence();
|
|
// 添加动画
|
switch (closeAnimationType)
|
{
|
case UIAnimationType.FadeInOut:
|
if (canvasGroup != null)
|
{
|
currentAnimation.Append(canvasGroup.DOFade(0f, animationDuration).SetEase(animationEase));
|
}
|
break;
|
|
case UIAnimationType.ScaleInOut:
|
if (rectTransform != null)
|
{
|
currentAnimation.Append(rectTransform.DOScale(Vector3.zero, animationDuration).SetEase(animationEase));
|
}
|
if (canvasGroup != null)
|
{
|
currentAnimation.Join(canvasGroup.DOFade(0f, animationDuration).SetEase(animationEase));
|
}
|
break;
|
|
case UIAnimationType.SlideFromTop:
|
if (rectTransform != null)
|
{
|
Vector2 endPos = originalPosition;
|
endPos.y = Screen.height;
|
currentAnimation.Append(rectTransform.DOAnchorPos(endPos, animationDuration).SetEase(animationEase));
|
}
|
if (canvasGroup != null)
|
{
|
currentAnimation.Join(canvasGroup.DOFade(0f, animationDuration).SetEase(animationEase));
|
}
|
break;
|
|
case UIAnimationType.SlideFromBottom:
|
if (rectTransform != null)
|
{
|
Vector2 endPos = originalPosition;
|
endPos.y = -Screen.height;
|
currentAnimation.Append(rectTransform.DOAnchorPos(endPos, animationDuration).SetEase(animationEase));
|
}
|
if (canvasGroup != null)
|
{
|
currentAnimation.Join(canvasGroup.DOFade(0f, animationDuration).SetEase(animationEase));
|
}
|
break;
|
|
case UIAnimationType.SlideFromLeft:
|
if (rectTransform != null)
|
{
|
Vector2 endPos = originalPosition;
|
endPos.x = -Screen.width;
|
currentAnimation.Append(rectTransform.DOAnchorPos(endPos, animationDuration).SetEase(animationEase));
|
}
|
if (canvasGroup != null)
|
{
|
currentAnimation.Join(canvasGroup.DOFade(0f, animationDuration).SetEase(animationEase));
|
}
|
break;
|
|
case UIAnimationType.SlideFromRight:
|
if (rectTransform != null)
|
{
|
Vector2 endPos = originalPosition;
|
endPos.x = Screen.width;
|
currentAnimation.Append(rectTransform.DOAnchorPos(endPos, animationDuration).SetEase(animationEase));
|
}
|
if (canvasGroup != null)
|
{
|
currentAnimation.Join(canvasGroup.DOFade(0f, animationDuration).SetEase(animationEase));
|
}
|
break;
|
}
|
|
// 动画完成后的回调
|
currentAnimation.OnComplete(() => {
|
isAnimating = false;
|
|
// 确保UI不可见且不可交互
|
if (canvasGroup != null)
|
{
|
canvasGroup.alpha = 0f;
|
canvasGroup.interactable = false;
|
canvasGroup.blocksRaycasts = false;
|
}
|
|
// 延迟禁用GameObject,避免频繁的Enable/Disable操作
|
// 如果需要立即释放资源,可以取消注释下面的代码
|
// gameObject.SetActive(false);
|
});
|
}
|
catch (System.Exception e)
|
{
|
Debug.LogError($"播放关闭动画时出错: {e.Message}");
|
|
// 出错时确保UI不可见且不可交互
|
if (canvasGroup != null)
|
{
|
canvasGroup.alpha = 0f;
|
canvasGroup.interactable = false;
|
canvasGroup.blocksRaycasts = false;
|
}
|
isAnimating = false;
|
}
|
}
|
|
#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();
|
}
|
|
#endregion
|
|
#region 回调方法
|
|
// UI打开时的回调
|
protected virtual void OnOpen()
|
{
|
// 子类可以重写此方法实现打开UI时的逻辑
|
}
|
|
// UI关闭时的回调
|
protected virtual void OnClose()
|
{
|
// 子类可以重写此方法实现关闭UI时的逻辑
|
}
|
|
#endregion
|
}
|