功能
在运行时对所选物体一键记录信息并保存到动画,可连续记录物体运动。
思路
选中物体,创建animationclip,遍历并获取物体所有子物体,记录每个物体的位置和旋转(可增加其他数据),将当前数据写入到clip中。多次使用时,判断是否存在对应clip,并间隔帧数向后添加。形成动画。
问题:旋向性可能会和预期不符。解决方式:选中clip后修改所有旋转属性的差值方式(在旋转上右键interpolation修改)。ps:还没找到代码怎么改,只能手动改下了
功能点
- clip中写入数据需要知道完整路径(获取物体下所有物体的路径):
这里的思路是:物体下的路径,存在上层路径的重复性,这里使用层序遍历,然后再获取路径(也不知道效率有没有提升)。
直接思路:对每个物体直接找路径,遍历一遍即可。 - 对clip写入数据的操作
- 对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));
}
}
}
}