using System; using System.Collections.Generic; using DG.Tweening; using UnityEngine; /// /// 可视化UI动画组件:以步骤列表驱动DOTween播放 /// 支持 位移/缩放/旋转/透明度 四类动画,正向/反向/停止 /// 配合 UIAnimatorInspector 可在 Inspector 中可视化编排并预览 /// public class UIAnimator : MonoBehaviour { public enum UIAnimationType { Move, // 位移(相对偏移) Scale, // 缩放(目标值) Rotate, // 旋转(目标角度,绕Z轴) Alpha // 透明度(目标值,需CanvasGroup) } [Serializable] public class UIAnimationStep { [Tooltip("动画类型")] public UIAnimationType type = UIAnimationType.Move; [Tooltip("步骤开始前的延迟(秒)")] public float delay; [Tooltip("动画时长(秒)")] public float duration = 0.5f; [Tooltip("缓动曲线")] public Ease ease = Ease.OutQuad; [Tooltip("位移偏移量(相对当前位置)")] public Vector3 moveOffset = Vector3.zero; [Tooltip("缩放目标值")] public Vector3 targetScale = Vector3.one; [Tooltip("旋转目标角度(绕Z轴)")] public float targetRotation; [Tooltip("透明度目标值(0-1)")] public float targetAlpha = 1f; } [Tooltip("动画步骤列表")] public List steps = new List(); [Tooltip("启动时自动播放")] public bool playOnAwake; [Tooltip("无CanvasGroup时自动添加(Alpha动画需要)")] public bool autoAddCanvasGroup = true; private RectTransform rectTransform; private CanvasGroup canvasGroup; private Sequence sequence; // 初始状态缓存(反向播放与编辑器预览需要) public Vector3 InitialAnchoredPosition3D { get; private set; } public Vector3 InitialLocalScale { get; private set; } public float InitialRotationZ { get; private set; } public float InitialAlpha { get; private set; } public RectTransform RectTransform => rectTransform; public CanvasGroup CanvasGroup => canvasGroup; /// 动画总时长(所有步骤的 延迟+时长 之和) public float TotalDuration { get { float total = 0; if (steps != null) foreach (var step in steps) total += step.delay + step.duration; return total; } } private void Awake() { rectTransform = GetComponent(); canvasGroup = GetComponent(); if (canvasGroup == null && autoAddCanvasGroup) canvasGroup = gameObject.AddComponent(); SaveInitialState(); if (playOnAwake) Play(); } /// 记录当前状态为初始状态(反向播放/预览的基准) public void SaveInitialState() { if (rectTransform == null) rectTransform = GetComponent(); if (canvasGroup == null) canvasGroup = GetComponent(); if (rectTransform == null) return; InitialAnchoredPosition3D = rectTransform.anchoredPosition3D; InitialLocalScale = rectTransform.localScale; InitialRotationZ = rectTransform.localEulerAngles.z; InitialAlpha = canvasGroup != null ? canvasGroup.alpha : 1f; } /// 正向播放 public void Play() => PlaySequence(false); /// 反向播放(从当前状态回到初始状态) public void PlayReverse() => PlaySequence(true); /// 停止并保持当前状态 public void Stop() { sequence?.Kill(); sequence = null; } private void PlaySequence(bool reverse) { sequence?.Kill(); sequence = reverse ? CreateReverseSequence() : CreateForwardSequence(); if (sequence == null) return; sequence.Play(); } private Sequence CreateForwardSequence() { if (rectTransform == null || steps == null || steps.Count == 0) return null; Sequence seq = DOTween.Sequence(); foreach (var step in steps) { Tween t = CreateStepTween(step, false); if (t == null) continue; if (step.delay > 0) t.SetDelay(step.delay); seq.Append(t); } return seq; } private Sequence CreateReverseSequence() { if (rectTransform == null || steps == null || steps.Count == 0) return null; Sequence seq = DOTween.Sequence(); for (int i = steps.Count - 1; i >= 0; i--) { Tween t = CreateStepTween(steps[i], true); if (t == null) continue; if (steps[i].delay > 0) t.SetDelay(steps[i].delay); seq.Append(t); } return seq; } private Tween CreateStepTween(UIAnimationStep step, bool reverse) { switch (step.type) { case UIAnimationType.Move: // 反向时位移取负偏移(回到初始位置) return rectTransform.DOAnchorPos3D(reverse ? -step.moveOffset : step.moveOffset, step.duration) .SetRelative().SetEase(step.ease); case UIAnimationType.Scale: return rectTransform.DOScale(reverse ? InitialLocalScale : step.targetScale, step.duration).SetEase(step.ease); case UIAnimationType.Rotate: return rectTransform.DOLocalRotate( new Vector3(0, 0, reverse ? InitialRotationZ : step.targetRotation), step.duration).SetEase(step.ease); case UIAnimationType.Alpha: if (canvasGroup == null) { Debug.LogWarning("[UIAnimator] Alpha动画需要CanvasGroup,请手动添加或开启autoAddCanvasGroup", this); return null; } return canvasGroup.DOFade(reverse ? InitialAlpha : step.targetAlpha, step.duration).SetEase(step.ease); } return null; } }