158 lines
5.9 KiB
C#
Raw Normal View History

using System;
using System.Collections.Generic;
using DG.Tweening;
using UnityEngine;
/// <summary>
/// 可视化UI动画组件以步骤列表驱动DOTween播放
/// 支持 位移/缩放/旋转/透明度 四类动画,正向/反向/停止
/// 配合 UIAnimatorInspector 可在 Inspector 中可视化编排并预览
/// </summary>
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<UIAnimationStep> steps = new List<UIAnimationStep>();
[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;
/// <summary>动画总时长(所有步骤的 延迟+时长 之和)</summary>
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<RectTransform>();
canvasGroup = GetComponent<CanvasGroup>();
if (canvasGroup == null && autoAddCanvasGroup)
canvasGroup = gameObject.AddComponent<CanvasGroup>();
SaveInitialState();
if (playOnAwake) Play();
}
/// <summary>记录当前状态为初始状态(反向播放/预览的基准)</summary>
public void SaveInitialState()
{
if (rectTransform == null) rectTransform = GetComponent<RectTransform>();
if (canvasGroup == null) canvasGroup = GetComponent<CanvasGroup>();
if (rectTransform == null) return;
InitialAnchoredPosition3D = rectTransform.anchoredPosition3D;
InitialLocalScale = rectTransform.localScale;
InitialRotationZ = rectTransform.localEulerAngles.z;
InitialAlpha = canvasGroup != null ? canvasGroup.alpha : 1f;
}
/// <summary>正向播放</summary>
public void Play() => PlaySequence(false);
/// <summary>反向播放(从当前状态回到初始状态)</summary>
public void PlayReverse() => PlaySequence(true);
/// <summary>停止并保持当前状态</summary>
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;
}
}