Unity中获取AnimationClip的关键帧信息

  • 引子
  • 解决方案
  • 示例
  • 参考链接

引子

之前在做项目时,经常会遇到:需要模型保持动画中某一帧的状态的需求;或者直接将一系列模型的状态数据做成动画,给到我们。这时我总是会先想着怎么样能从动画中将这些数据(关键帧)提取出来呢?但之前因为思路的方向错误,导致一直都没有找到合适的解决方案,最终这些需求大部分都是,将动画手动分割成对应的小段循环播放,或手动抄写数据。
这几天终于无意间在网上找到了,简单方便的方案来解决这个问题。

解决方案

之前我的错误思路总是在找AnimatorAnimationAnimationClipRuntimeAnimatorController等类中的相应变量、属性或方法,来获取动画中的关键帧数据。这是错的,至少当前的这些类中没有这样的接口。
根据我查到的文章,并查询了官方文档发现通过一个编辑器类可以获取到帧信息AnimationUtility
也就是说它只能在编辑器模式下使用,是无法在程序打包之后使用获取的,也因此我上面提到的思路是错误的。
由此便想到了在编辑器模式下把帧信息序列化(可以保存成数据文件,也可以保存在场景的某物体脚本组件上),然后在运行时直接读取便能解决问题。
核心接口使用:

foreach (var binding in AnimationUtility.GetCurveBindings(clip))
{AnimationCurve curve = AnimationUtility.GetEditorCurve(clip, binding);EditorGUILayout.LabelField(binding.path + "/" + binding.propertyName + ", Keys: " + curve.keys.Length);for (int i = 0; i < curve.length; i++){EditorGUILayout.LabelField("Keys" + i + ":" + curve[i].value);}
}

代码中clip为传入的AnimationClip对象,通过AnimationUtility.GetCurveBindings接口获取与动画剪辑相关的所有动画事件,然后再通过AnimationUtility.GetEditorCurve接口得到绑定所指向的float曲线,之后便可以得到我们想要得到的数据了,这里列举几个主要的:binding.path是模型中所操作的对象节点路径;binding.propertyName是所操作的对象节点属性(例如PositionRotationScaleBlendShape等);curve为关键帧曲线;curve.lengthcurve.keys.Length都是关键帧个数;最后循环中的curve[i].value是关键帧中数值,curve[i].time是关键帧中时间。

示例

以下是我项目中的示例,由于只能是编辑器模式下使用,所以是一个Basic脚本的Inspector窗口的Editor编辑脚本。

using UnityEngine;
using UnityEditor;[CustomEditor(typeof(Basic), true)]
public class BasicEditor : Editor
{protected Basic m_script;protected SerializedProperty scriptProp;protected float width_view;protected GUILayoutOption width_whole;protected GUILayoutOption width_half;private void OnEnable(){m_script = (Basic)target;scriptProp = serializedObject.FindProperty("m_Script");}public override void OnInspectorGUI(){width_whole = GUILayout.Width(width_view);width_half = GUILayout.Width(width_view / 2);//base.OnInspectorGUI();serializedObject.Update();GUI.enabled = false;EditorGUILayout.PropertyField(scriptProp);GUI.enabled = true;EditorGUILayout.Space();m_script.m_clip = (AnimationClip)EditorGUILayout.ObjectField("基础动画", m_script.m_clip, typeof(AnimationClip), true);if (m_script.m_clip != null){BasicExpressionsInfo info = m_script.m_info;if (GUILayout.Button("读取动画数据")){foreach (var binding in AnimationUtility.GetCurveBindings(m_script.m_clip)){//Debug.Log(binding.path);AnimationCurve curve = AnimationUtility.GetEditorCurve(m_script.m_clip, binding);string[] temp = binding.propertyName.Split('.');string name = temp[temp.Length - 1];name = name.Split('_')[0];if (!m_script.m_baseExpressionName.Contains(name)){m_script.m_baseExpressionName.Add(name);m_script.m_baseExpressionValue.Add(0);FloatArr floatArr = new FloatArr(){m_value = new float[curve.keys.Length]};info.m_baseExpressionFloat.Add(floatArr);StringArr stringArr = new StringArr{m_value = new string[curve.keys.Length]};info.m_baseExpressionString.Add(stringArr);info.m_baseExpressionIndex.Add(0);}int index = m_script.m_baseExpressionName.IndexOf(name);for (int i = 0; i < curve.length; i++){info.m_baseExpressionFloat[index].m_value[i] = curve[i].value;info.m_baseExpressionString[index].m_value[i] = curve[i].time.ToString() + " --- " + curve[i].value.ToString();}}}bool isWidth = false;info.m_foldoutAnimation = EditorGUILayout.BeginFoldoutHeaderGroup(info.m_foldoutAnimation, "BlendShape(动画:帧数 --- 数值)");if (!isWidth && info.m_foldoutAnimation){isWidth = true;}if (info.m_foldoutAnimation){string[] names = m_script.m_baseExpressionName.ToArray();for (int i = 0; i < names.Length; i++){info.m_baseExpressionIndex[i] = EditorGUILayout.Popup(names[i], info.m_baseExpressionIndex[i], info.m_baseExpressionString[i].m_value);}}EditorGUILayout.EndFoldoutHeaderGroup();EditorGUILayout.Space();if (GUILayout.Button("重置所有数据为动画数据")){string[] names = m_script.m_baseExpressionName.ToArray();for (int i = 0; i < names.Length; i++){m_script.m_baseExpressionValue[i] = info.m_baseExpressionFloat[i].m_value[info.m_baseExpressionIndex[i]];}}info.m_foldoutPredefine = EditorGUILayout.BeginFoldoutHeaderGroup(info.m_foldoutPredefine, "BlendShape(预设值)");if (!isWidth && info.m_foldoutPredefine){isWidth = true;}if (info.m_foldoutPredefine){string[] names = m_script.m_baseExpressionName.ToArray();for (int i = 0; i < names.Length; i++){GUILayout.BeginHorizontal();EditorGUILayout.LabelField(names[i], width_half);if (GUILayout.Button("重置为动画数据", width_half)){m_script.m_baseExpressionValue[i] = info.m_baseExpressionFloat[i].m_value[info.m_baseExpressionIndex[i]];}GUILayout.EndHorizontal();m_script.m_baseExpressionValue[i] = EditorGUILayout.Slider(m_script.m_baseExpressionValue[i], 0, 100);}}if (isWidth){width_view = EditorGUIUtility.currentViewWidth - 39;}else{width_view = EditorGUIUtility.currentViewWidth - 30;}}serializedObject.ApplyModifiedProperties();if (GUI.changed){//当Inspector 面板发生变化时保存数据EditorUtility.SetDirty(target);}}private void OnDestroy(){}
}

在将Basic脚本放到场景中,拖入动画,点击读取动画数据按钮,便会将动画中的BlendShape数据读取并保存到了脚本中,再点击重置所有数据为动画数据按钮,即,将这些脚本动画数据,覆盖真正程序运行的所要使用的变量属性,保存场景之后这些数据也就会保存在场景中,以便程序运行时使用。
下面是示例脚本的其他相关代码及Inspector窗口效果:

using System;
using System.Collections.Generic;
using UnityEngine;/// <summary>
/// 基础表情
/// </summary>
public class Basic : MonoBehaviour
{#if UNITY_EDITORpublic BasicExpressionsInfo m_info = new BasicExpressionsInfo();
#endifpublic AnimationClip m_clip;public List<string> m_baseExpressionName = new List<string>();public List<float> m_baseExpressionValue = new List<float>();
}#if UNITY_EDITOR
/// <summary>
/// 编辑器信息
/// </summary>
[Serializable]//便于编辑器储存,可视化操作
public class BasicExpressionsInfo
{public bool m_foldoutAnimation;public bool m_foldoutPredefine;public List<FloatArr> m_baseExpressionFloat = new List<FloatArr>();public List<StringArr> m_baseExpressionString = new List<StringArr>();public List<int> m_baseExpressionIndex = new List<int>();
}
[Serializable]//便于编辑器储存,可视化操作
public class FloatArr { public float[] m_value; }
[Serializable]//便于编辑器储存,可视化操作
public class StringArr { public string[] m_value; }
#endif

参考链接

地址:https://blog.csdn.net/qq_36292069/article/details/88601302

Unity中获取AnimationClip的关键帧信息相关推荐

  1. 【Unity】【Wwise】在Unity中获取某个Wwise事件的持续时间

    [Unity][Wwise]在Unity中获取某个Wwise事件的持续时间 解决方案 注意事项 边边角角 小吐槽 有一次接到这么一个需求:要在界面上显示出一段人物语音的长度,并且在播放的时候进行倒计时 ...

  2. Unity中获取一个物体下所有的子物体的方法

    Unity中获取一个物体下所有的子物体的方法 方法1(获取全部子物体,无论子物体SetActive是否为true): using System.Collections; using System.Co ...

  3. 从AFN错误码中获取错误code和信息

    使用AFNetWorking请求数据时,有时并不能直接拿到错误code和信息,如需获取可以使用以下方式: AFN新版本(>4.0.1) // 获取错误信息mutableUserInfo NSEr ...

  4. Unity中获取本机IP地址的方法

    做OptiTrack局域网数据通信时,需要设置本地IP和动捕数据服务器IP,来实现获取动捕数据.由于局域网搭建时需要手动设置电脑IP,因此想到如果可以获取本机IP,服务器IP通常设置固定后不会更改,如 ...

  5. Unity在XR设备中获取手柄的按键信息

    我们在平常的XR设备开发中,尤其适用VR设备的时候,会用到手柄的操作. 我们知道Oculus SDK提供了OVRInput,能够获取得到手柄的按键信息. // public variable that ...

  6. 如何在Windows、Linux中获取主机的网络信息和公网地址

    在我们的日常工作中,我们会需要进行查询主机当下的网络信息和公网上看到的IP地址信息,他们就像是网路上的名片,让我们找到彼此. Windows下如何获取内网和公网信息 在windows下该如何获取网路信 ...

  7. 示例 - 10行代码在C#中获取页面元素布局信息

    最近研究一个如何在网页定位验证码并截图的问题时, 用SS写了一段C#小脚本可以轻松获取页面任意元素的布局信息 (top, left, width, height). 10行功能代码, 觉得有点用, 现 ...

  8. Unity中获取地形的法线

    序 之前,生成了地形图:(42条消息) 从灰度图到地形图_averagePerson的博客-CSDN博客 那末,地形的法线贴图怎么获取? 大概分为两个部分吧,先拿到法线数据,再画到纹理中去. 关于法线 ...

  9. Unity中获取游戏对象的组件

    本文转载自Unity 得到游戏组件的常用的两种方法 常用的获取游戏对象组件的方法 获取当前挂载游戏对象的组件 index = this.GetComponent<******>(); // ...

  10. .Net中获取打印机的相关信息

    新项目中牵涉到对打印机的一些操作,最重要的莫过于获取打印机的状态,IP等信息,代码量不大,但是也是自己花了一点时间总结出来的,希望能帮助需要的朋友. PrinterCommunicate用于连接打印机 ...

最新文章

  1. 使用dom4j解析XML例子
  2. Xamarin Essentials教程实现数据的传输功能实例
  3. Ajax技术应用方面
  4. 基础练习 十进制转十六进制 C语言
  5. SAP Spartacus PersistFocus Directive是采取怎样的数据结构来存储focus信息的
  6. HDU 6607 Easy Math Problem(杜教筛 + min_25 + 拉格朗日插值)
  7. java登录界面命令_Java命令行界面(第19部分):jClap
  8. 【软件测试】测试需求分析
  9. Java Web实训项目:西蒙购物网(下)
  10. 电脑亮度多少对眼睛好_激光治疗近视眼大概要多少费用,保护好眼睛要做到哪几点...
  11. 【渝粤教育】国家开放大学2019年春季 2106宪法学 参考试题
  12. 【图像隐写】基于matlab FRFT+SVD盲水印嵌入+攻击+提取【含Matlab源码 1757期】
  13. php图片幻灯片代码,JavaScript_简单常用的幻灯片播放实现代码,幻灯片自动播放图片是当前网 - phpStudy...
  14. 开学至此时总结。(月末总结好像一直没写)
  15. Android6.0 Marshmallow运行时权限申请框架
  16. 日本战国武将绰号与称号一览表
  17. 【matlab】 matlab的输入和输出
  18. HEVC/H.265编码HM码率控制
  19. wfGo 围棋 AI对弈模式
  20. 浅谈 MySQL 连表查询

热门文章

  1. 极致小巧的画图工具,只有548K,有20多种工具,图层功能 类PS
  2. 计算机专业大学分数线低的,成绩徘徊在985、211分数线边缘,这4所大学考上容易,退档率极低...
  3. python培训班-python培训班哪家好
  4. Pt100铂电阻测温电路设计——
  5. 小米平板2刷哪个系统更流畅_大神教你小米平板2如何刷Windows 10系统
  6. 链游:不要只争朝夕,要寄望百年
  7. Asio源码分析(2):Asio用到的C++技巧和优化
  8. mysql 字符串转日期
  9. 最适合程序员敲代码用的显示器,包邮送!
  10. 数据蜂巢架构演讲之路读后感