Unity中获取AnimationClip的关键帧信息
Unity中获取AnimationClip的关键帧信息
- 引子
- 解决方案
- 示例
- 参考链接
引子
之前在做项目时,经常会遇到:需要模型保持动画中某一帧的状态的需求;或者直接将一系列模型的状态数据做成动画,给到我们。这时我总是会先想着怎么样能从动画中将这些数据(关键帧)提取出来呢?但之前因为思路的方向错误,导致一直都没有找到合适的解决方案,最终这些需求大部分都是,将动画手动分割成对应的小段循环播放,或手动抄写数据。
这几天终于无意间在网上找到了,简单方便的方案来解决这个问题。
解决方案
之前我的错误思路总是在找Animator
、Animation
、AnimationClip
、RuntimeAnimatorController
等类中的相应变量、属性或方法,来获取动画中的关键帧数据。这是错的,至少当前的这些类中没有这样的接口。
根据我查到的文章,并查询了官方文档发现通过一个编辑器类可以获取到帧信息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
是所操作的对象节点属性(例如Position、Rotation、Scale、BlendShape等);curve
为关键帧曲线;curve.length
和curve.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的关键帧信息相关推荐
- 【Unity】【Wwise】在Unity中获取某个Wwise事件的持续时间
[Unity][Wwise]在Unity中获取某个Wwise事件的持续时间 解决方案 注意事项 边边角角 小吐槽 有一次接到这么一个需求:要在界面上显示出一段人物语音的长度,并且在播放的时候进行倒计时 ...
- Unity中获取一个物体下所有的子物体的方法
Unity中获取一个物体下所有的子物体的方法 方法1(获取全部子物体,无论子物体SetActive是否为true): using System.Collections; using System.Co ...
- 从AFN错误码中获取错误code和信息
使用AFNetWorking请求数据时,有时并不能直接拿到错误code和信息,如需获取可以使用以下方式: AFN新版本(>4.0.1) // 获取错误信息mutableUserInfo NSEr ...
- Unity中获取本机IP地址的方法
做OptiTrack局域网数据通信时,需要设置本地IP和动捕数据服务器IP,来实现获取动捕数据.由于局域网搭建时需要手动设置电脑IP,因此想到如果可以获取本机IP,服务器IP通常设置固定后不会更改,如 ...
- Unity在XR设备中获取手柄的按键信息
我们在平常的XR设备开发中,尤其适用VR设备的时候,会用到手柄的操作. 我们知道Oculus SDK提供了OVRInput,能够获取得到手柄的按键信息. // public variable that ...
- 如何在Windows、Linux中获取主机的网络信息和公网地址
在我们的日常工作中,我们会需要进行查询主机当下的网络信息和公网上看到的IP地址信息,他们就像是网路上的名片,让我们找到彼此. Windows下如何获取内网和公网信息 在windows下该如何获取网路信 ...
- 示例 - 10行代码在C#中获取页面元素布局信息
最近研究一个如何在网页定位验证码并截图的问题时, 用SS写了一段C#小脚本可以轻松获取页面任意元素的布局信息 (top, left, width, height). 10行功能代码, 觉得有点用, 现 ...
- Unity中获取地形的法线
序 之前,生成了地形图:(42条消息) 从灰度图到地形图_averagePerson的博客-CSDN博客 那末,地形的法线贴图怎么获取? 大概分为两个部分吧,先拿到法线数据,再画到纹理中去. 关于法线 ...
- Unity中获取游戏对象的组件
本文转载自Unity 得到游戏组件的常用的两种方法 常用的获取游戏对象组件的方法 获取当前挂载游戏对象的组件 index = this.GetComponent<******>(); // ...
- .Net中获取打印机的相关信息
新项目中牵涉到对打印机的一些操作,最重要的莫过于获取打印机的状态,IP等信息,代码量不大,但是也是自己花了一点时间总结出来的,希望能帮助需要的朋友. PrinterCommunicate用于连接打印机 ...
最新文章
- 使用dom4j解析XML例子
- Xamarin Essentials教程实现数据的传输功能实例
- Ajax技术应用方面
- 基础练习 十进制转十六进制 C语言
- SAP Spartacus PersistFocus Directive是采取怎样的数据结构来存储focus信息的
- HDU 6607 Easy Math Problem(杜教筛 + min_25 + 拉格朗日插值)
- java登录界面命令_Java命令行界面(第19部分):jClap
- 【软件测试】测试需求分析
- Java Web实训项目:西蒙购物网(下)
- 电脑亮度多少对眼睛好_激光治疗近视眼大概要多少费用,保护好眼睛要做到哪几点...
- 【渝粤教育】国家开放大学2019年春季 2106宪法学 参考试题
- 【图像隐写】基于matlab FRFT+SVD盲水印嵌入+攻击+提取【含Matlab源码 1757期】
- php图片幻灯片代码,JavaScript_简单常用的幻灯片播放实现代码,幻灯片自动播放图片是当前网 - phpStudy...
- 开学至此时总结。(月末总结好像一直没写)
- Android6.0 Marshmallow运行时权限申请框架
- 日本战国武将绰号与称号一览表
- 【matlab】 matlab的输入和输出
- HEVC/H.265编码HM码率控制
- wfGo 围棋 AI对弈模式
- 浅谈 MySQL 连表查询