【编辑器】Unity编辑器里DOTween动画曲线的预览器

时间:2024-03-22 21:32:29

在Unity开发过程中,我们都可能会需要用到DOTween来做一些补间动画,其中,这些补间动画往往会有一些曲线效果。那么,如何来查看这些曲线的轨迹以及曲线的作用效果呢?DOTween插件里好像没有对应的预览器,所以我在闲杂时间便撸了个代码,方便后面编辑动画时,查看动画曲线的效果。直接上代码:


using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using DG.Tweening;

public class EasingDemoPreviewWindow : EditorWindow
{
    [MenuItem("Window/Dotween Easing Demo")]
    public static void ShowWindow()
    {
        GetWindow<EasingDemoPreviewWindow>("Dotween Easing Demo");
    }

    const int POINT_COUNT = 60;
    const float TIME_DELTATIME = 0.02f;
    const int POINT_LENGTH = 10;

    Ease _ease = Ease.Linear;
    private int _easeEnumLength = 0;

    Vector2 _rectStartPos = new Vector2(50,200);

    private Vector3 _ballTmpPos = new Vector3(200, 200, -10);
    private float _ballMoveTimer;
    private float _ballMoveDuration = 5;
    private bool _isBallMove = false;
    private Vector2 _ballTargetPos;
    private Vector3 _ballStartPos;
    private Texture _ballTex;
    private Texture _bgTex;

    private float _overshootOrAmplitude = DOTween.defaultEaseOvershootOrAmplitude; // 过冲或振幅
    private float _period = DOTween.defaultEasePeriod; // 周期

    private void OnEnable()
    {
        var easeArray = System.Enum.GetValues(typeof(Ease));
        _easeEnumLength = easeArray.Length - 2;
        _ballTex = GetBallTexture();
        _bgTex = GetBGTex();

        EditorApplication.update += Update;
    }

    private void Update()
    {
        if (_isBallMove)
        {
            _ballMoveTimer += TIME_DELTATIME;
            if (_ballMoveTimer > _ballMoveDuration)
            {
                _ballMoveTimer = _ballMoveDuration;
                _isBallMove = false;
            }
            var lerp = Evaluate(_ease, _ballMoveTimer, _ballMoveDuration, _overshootOrAmplitude, _period);
            _ballTmpPos.x = _ballStartPos.x + (_ballTargetPos.x - _ballStartPos.x) * lerp;
            _ballTmpPos.y = _ballStartPos.y + (_ballTargetPos.y - _ballStartPos.y) * lerp;
            Repaint();
        }
    }

    private void OnGUI()
    {
        HandleEvent();

        DrawBgGridUI();

        DrawCurveUI();

        DrawballUI();
    }

    private void OnDisable()
    {
        EditorApplication.update -= Update;
    }

    private void OnDestroy()
    {
        DestroyImmediate(_ballTex);
        DestroyImmediate(_bgTex);
    }

    void HandleEvent()
    {
        if(Event.current.type == EventType.KeyDown)
        {
            int e = (int)_ease;
            switch (Event.current.keyCode)
            {
                case KeyCode.UpArrow:
                    e--;
                    if (e < 0)
                    {
                        e = _easeEnumLength - 1;
                    }
                    _ease = (Ease)e;
                    break;
                case KeyCode.DownArrow:
                    e++;
                    if (e > _easeEnumLength - 1)
                    {
                        e = 0;
                    }
                    _ease = (Ease)e;
                    break;
            }
            _isBallMove = false;
            Repaint();
        }

        if (Event.current.type == EventType.MouseDown && Event.current.button == 0)
        {
            if(Event.current.mousePosition.y < _rectStartPos.y)
            {
                return;
            }
            _isBallMove = true;
            _ballStartPos = _ballTmpPos;
            _ballTargetPos = Event.current.mousePosition;
            _ballMoveTimer = 0;
        }
    }

    void DrawBgGridUI()
    {
        GUI.DrawTexture(new Rect(_rectStartPos.x, _rectStartPos.y, POINT_COUNT * POINT_LENGTH, POINT_COUNT * POINT_LENGTH), _bgTex);
    }

    void DrawCurveUI()
    {
        for (int i = 0; i < POINT_COUNT; i++)
        {
            var time = i * 1f;
            var value = POINT_COUNT * Evaluate(_ease, time, POINT_COUNT, _overshootOrAmplitude, _period);

            var p1 = new Vector2(_rectStartPos.x + time * POINT_LENGTH, _rectStartPos.y + POINT_COUNT * POINT_LENGTH - value * POINT_LENGTH);
            if (i >= POINT_COUNT - 1)
            {
                continue;
            }
            var time2 = (i + 1) * 1f;
            var value2 = POINT_COUNT * Evaluate(_ease, time2, POINT_COUNT, _overshootOrAmplitude, _period);
            var p2 = new Vector2(_rectStartPos.x + time2 * POINT_LENGTH, _rectStartPos.y + POINT_COUNT * POINT_LENGTH - value2 * POINT_LENGTH);


            Handles.color = Color.red;
            Handles.DrawLine(p1, p2);
        }
        Handles.color = Color.white;
    }

    void DrawballUI()
    {
        _ease = (Ease)EditorGUILayout.EnumPopup("Easing:", _ease);
        _ballMoveDuration = EditorGUILayout.FloatField("duration:", _ballMoveDuration);
        _overshootOrAmplitude = EditorGUILayout.FloatField("overshootOrAmplitude:", _overshootOrAmplitude);
        _period = EditorGUILayout.FloatField("period:", _period);

        var rect = new Rect(_ballTmpPos.x - _ballTex.width/2, _ballTmpPos.y - _ballTex.height/2, _ballTex.width,_ballTex.height);
        GUI.DrawTexture(rect, _ballTex);
    }

    Texture GetBallTexture()
    {
        var r = 32;
        var tex = new Texture2D(r * 2, r * 2, TextureFormat.ARGB32, false);
        var center = new Vector2(r, r);
        Color c1 = Color.white;
        for (int y = 0; y < tex.height; ++y)
        {
            for (int x = 0; x < tex.width; ++x)
            {
                var dis = Vector2.Distance(new Vector2(x, y), center);
                c1.a = 1 - Evaluate(Ease.InExpo, dis, r, _overshootOrAmplitude, _period);
                tex.SetPixel(x, y, c1);
            }
        }
        tex.Apply();
        tex.filterMode = FilterMode.Point;
        tex.hideFlags = HideFlags.HideAndDontSave;
        return tex;
    }

    Texture GetBGTex()
    {
        var tex = new Texture2D(64, 64);
        Color c1 = new Color(0.5f, 0.5f, 0.5f, 0.82f);

        for (int y = 0; y < tex.height; ++y)
        {
            for (int x = 0; x < tex.width; ++x)
            {
                tex.SetPixel(x, y, c1);
            }
        }
        tex.Apply();
        tex.filterMode = FilterMode.Point;
        tex.hideFlags = HideFlags.HideAndDontSave;
        return tex;
    }

    float Evaluate(Ease easeType, float time, float duration, float overshootOrAmplitude, float period)
    {
        return DG.Tweening.Core.Easing.EaseManager.Evaluate(easeType,null,time, duration, overshootOrAmplitude, period);
    }

}
 

使用方法:把脚本放到Editor目录下,打开Window->DoTween Easing Demo

【编辑器】Unity编辑器里DOTween动画曲线的预览器

效果如下:

点击任意地方,小球会根据选择的曲线类型进行测试移动,更直观查看曲线运动轨迹。

【编辑器】Unity编辑器里DOTween动画曲线的预览器

 

注意:因为是DOTween曲线预览器,所以,工程里必须要有DOTween插件库,不然放到Unity里会报错