UnityEditor工具_手动生成动画片段和数据

  • A+
所属分类:Unity

功能

在运行时对所选物体一键记录信息并保存到动画,可连续记录物体运动。

思路

选中物体,创建animationclip,遍历并获取物体所有子物体,记录每个物体的位置和旋转(可增加其他数据),将当前数据写入到clip中。多次使用时,判断是否存在对应clip,并间隔帧数向后添加。形成动画。

问题:旋向性可能会和预期不符。解决方式:选中clip后修改所有旋转属性的差值方式(在旋转上右键interpolation修改)。ps:还没找到代码怎么改,只能手动改下了

功能点

  1. clip中写入数据需要知道完整路径(获取物体下所有物体的路径):
    这里的思路是:物体下的路径,存在上层路径的重复性,这里使用层序遍历,然后再获取路径(也不知道效率有没有提升)。
    直接思路:对每个物体直接找路径,遍历一遍即可。
  2. 对clip写入数据的操作
  3. 对clip连续写入数据

实现

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

public class CreateAnimationHelperEditor : Editor
{
   
    private static float addFrame = 0.5f;

    static List<Transform> needTrans = new List<Transform>();
    static Queue<Transform> queue = new Queue<Transform>();
    static string[] pathss;
    [MenuItem("追踪数据生成动画/为GameObject创建空Clip[仅根物体]")]
    public static void CreateEmptyAnimationClip()
    {
   
        GameObject obj = Selection.activeObject as GameObject;

        string clipFilePath = "Assets/" + obj.name + "_create.anim";

        //遍历物体下的子物体
        //每一个物体,获取路径,名称
        TreeSortLayer(obj.transform);

        //查找是否以及生成clip
        AnimationClip createClip = AssetDatabase.LoadAssetAtPath<AnimationClip>(clipFilePath);

        if (createClip == null)
        {
   
            //创建clip
            createClip = new AnimationClip();
            createClip.legacy = true;
            AnimationCurve[] acs = new AnimationCurve[7];
           
            for (int i = 0; i < needTrans.Count; i++)
            {
   
                for (int y = 0; y < acs.Length; y++)
                {
   
                    acs[y] = new AnimationCurve();
                }
                //动画帧数据
                Vector3 pos = needTrans[i].localPosition;
                Quaternion rot = Quaternion.Euler(needTrans[i].localEulerAngles);
                acs[0].AddKey(0, pos.x);
                acs[1].AddKey(0, pos.y);
                acs[2].AddKey(0, pos.z);

                //acs[3].AddKey(0, rot.eulerAngles.x);
                //acs[4].AddKey(0, rot.eulerAngles.y);
                //acs[5].AddKey(0, rot.eulerAngles.z);


                acs[3].AddKey(0, rot.x);
                acs[4].AddKey(0, rot.y);
                acs[5].AddKey(0, rot.z);
                acs[6].AddKey(0, rot.w);

                //给clip添加曲线(自动生成EditorCurveBinding)
                createClip.SetCurve(pathss[i], typeof(Transform), "m_LocalPosition.x", acs[0]);
                createClip.SetCurve(pathss[i], typeof(Transform), "m_LocalPosition.y", acs[1]);
                createClip.SetCurve(pathss[i], typeof(Transform), "m_LocalPosition.z", acs[2]);
                createClip.SetCurve(pathss[i], typeof(Transform), "m_LocalRotation.x", acs[3]);
                createClip.SetCurve(pathss[i], typeof(Transform), "m_LocalRotation.y", acs[4]);
                createClip.SetCurve(pathss[i], typeof(Transform), "m_LocalRotation.z", acs[5]);
                createClip.SetCurve(pathss[i], typeof(Transform), "m_LocalRotation.w", acs[6]);
            }
            createClip.EnsureQuaternionContinuity();
            AnimationCurve ac = new AnimationCurve();

            AnimationClipSettings acttt = new AnimationClipSettings();
            //acttt.loopBlendOrientation

            
            AssetDatabase.CreateAsset(createClip, clipFilePath);

            EditorGUIUtility.PingObject(createClip);
        }
        else
        {
   
            List<string> strPath = new List<string>(pathss);
            
            //获取curver
            EditorCurveBinding[] binds = AnimationUtility.GetCurveBindings(createClip);
            for(int i = 0; i < binds.Length; i++)
            {
   
              
                AnimationCurve ac = AnimationUtility.GetEditorCurve(createClip, binds[i]);

                Debug.Log(ac.length);
                //找物体对应部位
                int count = strPath.FindIndex(p => p == binds[i].path);

                switch (binds[i].propertyName)
                {
   
                    case "m_LocalPosition.x":
                        ac.AddKey((float)ac.length * addFrame, needTrans[count].localPosition.x);
                        break;
                    case "m_LocalPosition.y":
                        ac.AddKey((float)ac.length * addFrame, needTrans[count].localPosition.y);
                        break;
                    case "m_LocalPosition.z":
                        ac.AddKey((float)ac.length * addFrame, needTrans[count].localPosition.z);
                        break;
                    case "m_LocalRotation.x":
                        ac.AddKey((float)ac.length * addFrame, needTrans[count].localRotation.x);
                        break;
                    case "m_LocalRotation.y":
                        ac.AddKey((float)ac.length * addFrame, needTrans[count].localRotation.y);
                        break;
                    case "m_LocalRotation.z":
                        ac.AddKey((float)ac.length * addFrame, needTrans[count].localRotation.z);
                        break;
                    case "m_LocalRotation.w":
                        ac.AddKey((float)ac.length * addFrame, needTrans[count].localRotation.w);
                        break;
                }


                AnimationUtility.SetEditorCurve(createClip, binds[i], ac);
            }
            EditorGUIUtility.PingObject(createClip);
        }
    }


    [MenuItem("追踪数据生成动画/测试_物体路径打印")]
    public static void TestFunc()
    {
   
        GameObject obj = Selection.activeObject as GameObject;

        TreeSortLayer(obj.transform);

        foreach(var x in pathss)
        {
   
            Debug.Log(x);
        }
    }

    [MenuItem("追踪数据生成动画/测试_打印动画路径")]
    public static void AnimaShow()
    {
   
        GameObject obj = Selection.activeObject as GameObject;
        //Animation objAni = obj.GetComponent<Animation>();
        AnimationClip[] acs = AnimationUtility.GetAnimationClips(obj);
        for(int i = 0; i < acs.Length; i++)
        {
   
            AnimationClip ac = acs[i];
            Debug.Log(ac.name);
            EditorCurveBinding[] ecbs = AnimationUtility.GetCurveBindings(ac);
            for(int m = 0; m < ecbs.Length; m++)
            {
   
                AnimationCurve acc = AnimationUtility.GetEditorCurve(ac, ecbs[m]);
                string str = string.Format("【{0}】/【{1}】/【{2}】", ecbs[m].path, ecbs[m].propertyName, acc.keys[0].value);
                Debug.Log(str);
            }
        }
    }



  

    //层序遍历物体 记录每层
    private static void TreeSortLayer(Transform obj)
    {
   
        queue.Clear();
        needTrans.Clear();
        pathss = null;
        queue.Enqueue(obj);
        Que(queue);

        //获取路径
        pathss = new string[needTrans.Count];
        int nowObj =0 ;
        for (int i = 0; i < needTrans.Count; i++)
        {
   
            //第一个
            if (i == 0)
            {
   
                //pathss[i] = needTrans[i].name;
                pathss[i] = "";
                nowObj = i;
                continue;
            }
            //当前是父物体
            if(needTrans[i].parent == needTrans[nowObj])
            {
   
                if (nowObj == 0)
                {
   
                    pathss[i] = needTrans[i].name;
                }
                else
                {
   
                    pathss[i] = pathss[nowObj] + "/" + needTrans[i].name;
                }
               
            }
            else
            {
   
                //nowObj++;
                //找到父物体
                nowObj = needTrans.FindIndex(nowObj,i-nowObj, p => p == needTrans[i].parent);
                pathss[i] = pathss[nowObj] + "/" + needTrans[i].name;
            }
        }
    }


    private static void Que(Queue<Transform> que)
    {
   
        //obj in
        while (que.Count > 0)
        {
   
            //out (needdata)
            Transform obj = que.Dequeue();
            needTrans.Add(obj);

            //in
            for (int i = 0; i < obj.childCount; i++)
            {
   
                que.Enqueue(obj.GetChild(i));
            }
        }
    }


}

w3cjava