Unity中的GameObjectRecorder类录制动画
记录
首先是,参考及示例视频:Unity制作战神等级的表情动画(游戏,CG,Vtuber适用),相关代码在8分16秒之后。
在观看视频之后,由于项目中有相关需求就使用了,其中的功能,这里进行一下记录,并感谢视频作者功能分享。
GameObjectRecorder
GameObjectRecorder
类是其中的核心代码。Unity可以看到的接口如下:
namespace UnityEditor.Animations
{
//
// 摘要:
// Records the changing properties of a GameObject as the Scene runs and saves the
// information into an AnimationClip.
[NativeHeader("Editor/Src/Animation/GameObjectRecorder.h")]
[NativeHeader("Modules/Animation/AnimationClip.h")]
[NativeHeader("Editor/Src/Animation/EditorCurveBinding.bindings.h")]
[NativeType]
public class GameObjectRecorder : UnityEngine.Object
{
//
// 摘要:
// TODO.
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete("The GameObjectRecorder constructor now takes a root GameObject", true)]
public GameObjectRecorder();
//
// 摘要:
// Create a new GameObjectRecorder.
//
// 参数:
// root:
// The root GameObject for the animated hierarchy.
public GameObjectRecorder(GameObject root);
//
// 摘要:
// The GameObject root of the animated hierarchy. (Read Only)
public GameObject root { get; }
//
// 摘要:
// Returns the current time of the recording. (Read Only)
public float currentTime { get; }
//
// 摘要:
// Returns true when the recorder is recording. (Read Only)
public bool isRecording { get; }
//
// 摘要:
// Binds a GameObject's property as defined by EditorCurveBinding.
//
// 参数:
// binding:
// The binding definition.
public void Bind(EditorCurveBinding binding);
//
// 摘要:
// Adds bindings for all of target's properties, and also for all the target's children
// if recursive is true.
//
// 参数:
// target:
// .root or any of its children.
//
// recursive:
// Binds also all the target's children properties when set to true.
public void BindAll(GameObject target, bool recursive);
//
// 摘要:
// Adds bindings for all the properties of component.
//
// 参数:
// component:
// The component to bind.
public void BindComponent([NotNull] UnityEngine.Component component);
//
// 摘要:
// TODO.
//
// 参数:
// target:
//
// componentType:
//
// recursive:
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete("BindComponent() using a System::Type is obsolete, use BindComponentsOfType() instead (UnityUpgradable) -> BindComponentsOfType(*)", true)]
public void BindComponent(GameObject target, Type componentType, bool recursive);
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete("\"BindComponent<T>() where T : Component\" is obsolete, use BindComponentsOfType<T>() instead (UnityUpgradable) -> BindComponentsOfType<T>(*)", true)]
public void BindComponent<T>(GameObject target, bool recursive) where T : UnityEngine.Component;
//
// 摘要:
// Adds bindings for all the properties of the first component of type T found in
// target, and also for all the target's children if recursive is true.
//
// 参数:
// target:
// .root or any of its children.
//
// recursive:
// Binds also the target's children transform properties when set to true.
//
// componentType:
// Type of the component.
public void BindComponentsOfType(GameObject target, Type componentType, bool recursive);
public void BindComponentsOfType<T>(GameObject target, bool recursive) where T : UnityEngine.Component;
//
// 摘要:
// Returns an array of all the bindings added to the recorder.
//
// 返回结果:
// Array of bindings.
public EditorCurveBinding[] GetBindings();
//
// 摘要:
// Reset the recording.
public void ResetRecording();
//
// 摘要:
// Saves recorded animation to a destination clip.
//
// 参数:
// clip:
// The destination clip. If this clip has animation curves, they will be removed.
//
// fps:
// The frames per second (FPS) for the clip. If no value is specified, by default,
// this method uses 60 FPS.
//
// filterOptions:
// The filtering options for processing the animation curves when saved to the destination
// clip. If no options are specified, by default, this method filters out irrelevant
// keys by applying a light compression of 0.5 for positionError, rotationError,
// scaleError and floatError.
public void SaveToClip(AnimationClip clip, float fps);
//
// 摘要:
// Saves recorded animation to a destination clip.
//
// 参数:
// clip:
// The destination clip. If this clip has animation curves, they will be removed.
//
// fps:
// The frames per second (FPS) for the clip. If no value is specified, by default,
// this method uses 60 FPS.
//
// filterOptions:
// The filtering options for processing the animation curves when saved to the destination
// clip. If no options are specified, by default, this method filters out irrelevant
// keys by applying a light compression of 0.5 for positionError, rotationError,
// scaleError and floatError.
public void SaveToClip(AnimationClip clip, float fps, CurveFilterOptions filterOptions);
//
// 摘要:
// Saves recorded animation to a destination clip.
//
// 参数:
// clip:
// The destination clip. If this clip has animation curves, they will be removed.
//
// fps:
// The frames per second (FPS) for the clip. If no value is specified, by default,
// this method uses 60 FPS.
//
// filterOptions:
// The filtering options for processing the animation curves when saved to the destination
// clip. If no options are specified, by default, this method filters out irrelevant
// keys by applying a light compression of 0.5 for positionError, rotationError,
// scaleError and floatError.
public void SaveToClip(AnimationClip clip);
//
// 摘要:
// Forwards the animation by dt seconds, then record the values of the added bindings.
//
// 参数:
// dt:
// Delta time.
public void TakeSnapshot(float dt);
}
}
首先其存在与UnityEditor.Animations
库中;UnityEditor
表示该功能只能在编辑器下使用;Animations
就是用于录制动画Animations。对于类中的接口,结合下面的示例记录。
示例代码
using UnityEditor.Animations;
using UnityEngine;
public class FaceRecorder : MonoBehaviour
{
private SkinnedMeshRenderer[] skinnedMeshRenderers;
public AnimationClip m_clip;
private GameObjectRecorder m_recorder;
void Start()
{
m_recorder = new GameObjectRecorder(gameObject);
skinnedMeshRenderers = GetComponentsInChildren<SkinnedMeshRenderer>();
for (int i = 0; i < skinnedMeshRenderers.Length; i++)
{
m_recorder.BindComponent(skinnedMeshRenderers[i]);
}
}
private void LateUpdate()
{
if (null == m_clip) return;
m_recorder.TakeSnapshot(Time.deltaTime);
}
private void OnDisable()
{
if (null == m_clip) return;
if (m_recorder.isRecording) m_recorder.SaveToClip(m_clip);
}
}
解析
首先,需要在项目中创建一个Animation,用于储存我们记录的动画数据;与其对应的便是代码中的AnimationClip
对象m_clip
,我们需要在Inspector界面对其进行拖拽设置。
![创建Animation](https://img-blog.csdnimg.cn/2021040620174422.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ZfOTU3OTk1NDkw,size_16,color_FFFFFF,t_70)
之后代码在开启运行并创建完GameObjectRecorder
类相应的对象(m_recorder
)后,需要将场景中需要跟踪记录的对象组件设置到GameObjectRecorder
类的对象中:m_recorder.BindComponent
,示例中我使用的是SkinnedMeshRenderer
组件,除此之外Transform
组件肯定也可以,其余组件以及自己创建的Mono类需要进一步尝试,当然接口中Bind相关的接口有很多,可自行测试。
再之后就是开始进行录制m_recorder.TakeSnapshot
。然后就是将录制的数据存储在动画中m_recorder.SaveToClip(m_clip);
。
检验成果:同视频中的方法一样;保存成功的动画Animation,就可以像平常的Animation一样,使用动画状态机Animator在对应的物体上播放动画即可。