AnimationCurve关键帧数值修改小工具

效果图

 

用途:界面动效已经由动效同事做完(假设k了100帧),然后UI同事又把一些节点的位置做了10px的调整。此时一帧一帧去手动改,费事费力还可能出错。

这个工具的用途就是:对相关节点的所有关键帧批量做偏移。

public class AnimClipEditWnd : EditorWindow
{

    [MenuItem("MyTools/AnimClipEdit", false)]
    static void ShowWindow()
    {
        var win = GetWindow(typeof(AnimClipEditWnd), false, "AnimClipEdit");
        win.minSize = new Vector2(500, 300);
    }

    private Vector2 m_ScrollPos;
    private AnimationClip m_Clip;

    private EditorCurveBinding[] m_CurveBindings;

    private List<string> m_PathList = new List<string>();
    private Dictionary<string, List<string>> m_PathPropNamesDict = new Dictionary<string, List<string>>();

    private int m_PathOptionIndex;
    private GUIContent[] m_PathOptions;
    private GUIContent m_LabelPath = new GUIContent("path");

    private int m_PropNameOptionIndex;
    private string[] m_PropNameOptions;

    private AnimationCurve m_Curve;
    private int m_CurveBindingIndex;

    private float m_Delta;

    private void OnGUI()
    {
        m_ScrollPos = EditorGUILayout.BeginScrollView(m_ScrollPos);
        {
            OnGUI_ScrollView();
        }
        EditorGUILayout.EndScrollView();
    }

    private void OnGUI_ScrollView()
    {
        var clip = (AnimationClip)EditorGUILayout.ObjectField("Clip", m_Clip, typeof(AnimationClip), false);
        bool isClipChange = m_Clip != clip;
        m_Clip = clip;

        if (null != clip)
        {
            if (GUILayout.Button($"Refresh"))
                isClipChange = true;
        }

        bool isPathOptionChange = false;
        if (isClipChange)
        {
            isPathOptionChange = true;

            m_CurveBindings = null;

            m_PathList.Clear();
            m_PathPropNamesDict.Clear();

            m_PathOptionIndex = 0;
            m_PathOptions = null;

            m_PropNameOptionIndex = 0;
            m_PropNameOptions = null;

            if (null != clip)
            {
                m_CurveBindings = AnimationUtility.GetCurveBindings(clip);
                foreach (var b in m_CurveBindings)
                {
                    if (!m_PathPropNamesDict.TryGetValue(b.path, out var list))
                    {
                        list = new List<string>();
                        m_PathPropNamesDict.Add(b.path, list);
                        m_PathList.Add(b.path);
                    }

                    if (!IsIgnoredPropty(b.propertyName))
                    {
                        list.Add(b.propertyName);
                    }
                }

                m_PathOptions = new GUIContent[m_PathList.Count];
                for (int i = 0; i < m_PathList.Count; ++i)
                    m_PathOptions[i] = new GUIContent(m_PathList[i].Replace("/", "\\"));
            }
        }

        if (null == m_Clip) return;

        bool isPropNameOptionChange = false;
        int pathOptionIndex = EditorGUILayout.Popup(m_LabelPath, m_PathOptionIndex, m_PathOptions);
        if (isPathOptionChange || m_PathOptionIndex != pathOptionIndex)
        {
            Debug.Log($"PathOptionChange: {m_PathOptionIndex} -> {pathOptionIndex}");
            m_PathOptionIndex = pathOptionIndex;
            string path = m_PathList[pathOptionIndex];
            var propNameList = m_PathPropNamesDict[path];
            m_PropNameOptions = propNameList.ToArray();
            m_PropNameOptionIndex = 0;
            isPropNameOptionChange = true;
        }

        int propNameOptionIndex = EditorGUILayout.Popup("propName", m_PropNameOptionIndex, m_PropNameOptions);
        if (isPropNameOptionChange || m_PropNameOptionIndex != propNameOptionIndex)
        {
            Debug.Log($"PropNameOptionChange: {m_PropNameOptionIndex} -> {propNameOptionIndex}");
            m_PropNameOptionIndex = propNameOptionIndex;
            string path = m_PathList[pathOptionIndex];
            string propName = m_PropNameOptions[m_PropNameOptionIndex];
            UpdateCurveAndBinding(m_Clip, path, propName);
        }

        if (null != m_Curve)
        {
            GUILayout.Space(10);
            EditorGUILayout.CurveField(m_Curve, GUILayout.Height(100));
            Keyframe[] keyFrames = m_Curve.keys;
            EditorGUILayout.LabelField("所有关键帧:");
            EditorGUILayout.BeginVertical("box");
            for (int i = 0; i < keyFrames.Length; ++i)
            {
                var kf = keyFrames[i];
                EditorGUILayout.LabelField($"{i} -> t: {kf.time}, v: {kf.value}");
            }
            EditorGUILayout.EndVertical();
        }
        GUILayout.Space(10);

        m_Delta = EditorGUILayout.FloatField("Delta", m_Delta);
        if (GUILayout.Button("所有关键帧的值+Delta"))
        {
            if (null != m_Curve)
            {
                Keyframe[] keyFrames = m_Curve.keys;
                for (int i = 0; i < keyFrames.Length; ++i)
                {
                    Keyframe kf = keyFrames[i];
                    float old = kf.value;
                    kf.value = TrimFloat(old + m_Delta);
                    Debug.Log($"idx:{i}, t:{TrimFloat(kf.time)}, old:{old} -> {kf.value}");
                    m_Curve.MoveKey(i, kf);
                }

                AnimationUtility.SetEditorCurve(m_Clip, m_CurveBindings[m_CurveBindingIndex], m_Curve);
                EditorUtility.SetDirty(m_Clip);
                AssetDatabase.SaveAssets();
                //AssetDatabase.Refresh();
                Debug.Log($"finish");
            }
        }
    }


    public static float TrimFloat(float f)
    {
        int i = (int)(f * 1000);
        float result = i / 1000.0f;
        return result;
    }

    private void UpdateCurveAndBinding(AnimationClip clip, string path, string propName)
    {
        m_Curve = null;
        int index = 0;
        foreach (var b in m_CurveBindings)
        {
            if (b.path == path)
            {
                //Debug.Log($"path:{b.path}, propName:{b.propertyName}, type:{b.type.Name}, discrete:{b.isDiscreteCurve}, pptr:{b.isPPtrCurve}");
                if (b.propertyName == propName)
                {
                    m_Curve = AnimationUtility.GetEditorCurve(clip, b);
                    m_CurveBindingIndex = index;
                    break;
                }
            }
            index++;
        }
    }

    private bool IsIgnoredPropty(string propertyName)
    {
        switch (propertyName)
        {
        case "m_Maskable":
        case "m_IsActive":
        case "m_RaycastTarget":
        case "m_FillCenter":
        case "m_FillMethod":
        case "m_PixelsPerUnitMultiplier":
        case "m_PreserveAspect":
            //bool类型忽略
            return true;
        }
        return false;
    }

}

 

posted @ 2024-05-18 00:12  yanghui01  阅读(3)  评论(0编辑  收藏  举报