DoTween是一款非常好用的补间动画插件,但是其不支持在非运行状态下预览动画
所以我尝试对DoTween进行扩展,让其能够支持非运行时预览
首先编写一个BaseTween基类
public enum EaseType
{
Ease,
AnimationCurve,
}
public abstract class BaseTween : MonoBehaviour
{
[Header("BaseTween")] [LabelText("动画时长")]
public float tweenTime = 1f;
[LabelText("循环")] public int loopTimes = -1;
[LabelText("动画曲线类型")]public EaseType easeType = EaseType.Ease;
[LabelText("动画曲线")] [ShowIf("@easeType==EaseType.Ease")]
public Ease ease = Ease.Linear;
[LabelText("动画曲线")] [ShowIf("@easeType==EaseType.AnimationCurve")]
public AnimationCurve
animationCurve = AnimationCurve.Linear(0, 0, 1, 1);
[LabelText("动画速度缩放")] public float tweenScale = 1f;
[LabelText("倒放")] public bool isReserve = false;
public Tweener tweener;
[ShowInInspector] [ReadOnly][LabelText("当前百分比")] protected float currentPercent;
public float CurrentPercent
{
get => currentPercent;
set => currentPercent = value;
}
[Range(0, 1)][LabelText("预览百分比")] public float previewPercent;
protected virtual void Awake()
{
currentPercent = 0;
}
[Button("播放动画")]
public virtual Tweener PlayTween()
{
if (tweener != null)
{
tweener.Goto(currentPercent * tweenTime, true);
}
else
{
ResetTween();
}
return tweener;
}
[Button("暂停动画")]
public virtual Tweener PauseTween()
{
if (tweener != null)
{
tweener.Pause();
}
return tweener;
}
[Button("停止动画")]
public virtual Tweener StopTween()
{
if (tweener != null)
{
tweener.Pause();
tweener.Kill();
}
return tweener;
}
[Button("重置动画")]
public virtual Tweener ResetTween()
{
if (tweener != null)
{
tweener.Kill();
}
tweener = DOTween.To(() => isReserve ? 1f : 0f,
value => OnTweenUpdate(value),
isReserve ? 0f : 1f, tweenTime)
.SetLoops(loopTimes);
//percent已经在此倒置
switch (easeType)
{
case EaseType.Ease:
tweener.SetEase(ease);
break;
case EaseType.AnimationCurve:
tweener.SetEase(animationCurve);
break;
}
return tweener;
}
protected virtual Tweener OnTweenUpdate(float percent)
{
currentPercent = percent;
if (tweener != null)
{
tweener.timeScale = tweenScale;
}
return tweener;
}
protected virtual void OnValidate()
{
}
protected virtual void Reset()
{
}
}
其中直接使用了DOTween.To从0到1进行补间,这个值表示动画百分比,然后在OnTweenUpdate中编写对应百分比时执行的操作即可。
编辑器下预览脚本如下,原理就是绑定EditorApplication.update回调,再调用OnTweenUpdate即可
public abstract class BaseTween : MonoBehaviour
{
//
#region 编辑器下预览
#if UNITY_EDITOR
protected float editorTimer; //计时器
protected float editorStartTime; //起始运行时间
protected int editorTimes; //运行次数
[Button("编辑器中预览")][HorizontalGroup("Editor")]
public virtual void PreviewInEditor()
{
editorTimer = 0;
editorTimes = 0;
editorStartTime = Time.realtimeSinceStartup;
EditorApplication.update -= OnEditorUpdated;
EditorApplication.update += OnEditorUpdated;
}
[Button("停止预览")][HorizontalGroup("Editor")]
public virtual void StopPreviewInEditor()
{
EditorApplication.update -= OnEditorUpdated;
}
protected virtual void OnEditorUpdated()
{
editorTimer = Time.realtimeSinceStartup - editorStartTime;
var percentTimer =
MathTool.MapClamped(editorTimer, 0, tweenTime, 0, 1);
//计算动画曲线实际百分比
if (easeType == EaseType.Ease)
{
OnTweenUpdate(EaseManager.Evaluate(ease, null, editorTimer,
tweenTime, DOTween.defaultEaseOvershootOrAmplitude,
DOTween.defaultEasePeriod));
}
if (easeType == EaseType.AnimationCurve)
{
OnTweenUpdate(animationCurve.Evaluate(percentTimer));
}
if (editorTimer > tweenTime)
{
editorTimes++;
editorStartTime = Time.realtimeSinceStartup;
if (loopTimes >= 0)
{
if (editorTimes >= loopTimes)
{
Debug.Log(
$"{name}动画预览完成,动画时长{tweenTime}s,循环{editorTimes}次,实际耗时{editorTimer * loopTimes}s");
EditorApplication.update -= OnEditorUpdated;
editorTimes = 0;
}
}
editorTimer = 0;
}
}
#endif
#endregion
}
下面以一个简单的位置补间为例
public class TweenTransform : BaseTween
{
[Header("TweenTransform")]
[LabelText("目标物体")]public Transform target;
[LabelText("起点")]public Transform from;
[LabelText("终点")]public Transform to;
[TabGroup("位置")]
[LabelText("使用位置")]public bool tweenPosition = true;
[TabGroup("位置")]
[LabelText("使用X坐标")]public bool useX = true;
[TabGroup("位置")]
[LabelText("使用Y坐标")]public bool useY = true;
[TabGroup("位置")]
[LabelText("使用Z坐标")]public bool useZ = true;
[TabGroup("旋转")][LabelText("使用旋转")]public bool tweenRotation = true;
protected override Tweener OnTweenUpdate(float percent)
{
if (target != null&&@from!=null&&to!=null)
{
if (tweenPosition)
{
target.position = new Vector3(
!useX
? target.position.x
: Mathf.Lerp(@from.position.x, to.position.x, percent),
!useY
? target.position.y
: Mathf.Lerp(@from.position.y, to.position.y, percent),
!useZ
? target.position.z
: Mathf.Lerp(@from.position.z, to.position.z, percent));
}
if (tweenRotation)
{
target.rotation =
Quaternion.Lerp(@from.rotation, to.rotation, percent);
}
}
return base.OnTweenUpdate(percent);
}
protected override void OnValidate()
{
base.OnValidate();
OnTweenUpdate(previewPercent);
}
}
面板如图所示
在非运行状态下预览的效果如下
甚至可以继续扩展为曲线路径,然后在非运行状态下预览