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 AnimationCurveanimationCurve = 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_EDITORprotected 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);}
}
面板如图所示
在非运行状态下预览的效果如下
甚至可以继续扩展为曲线路径,然后在非运行状态下预览