做Unity动画相关工作的人,肯定都遇到过这种需求:角色在编辑器里刚刚摆好一套动作,或者在游戏里跑出了一段完全没法复现的物理表演,想把这段过程完整记录成AnimationClip,方便回放、剪辑,或者拿去做表现对比。这件事在Unity圈子里有一个很常见的叫法:Animation Recorder。文章不打算只讲按钮在哪,而是想把编辑器捕获和运行时捕获两条路都拆开,把原理、代码、坑位放到一起讲清楚。不管你是刚写第一行Unity脚本的新手,还是已经在折腾动画系统的老手,这篇文章应该都能帮上忙。
Unity本身对“录制动画”这套需求支持得其实挺早,但官方的封装往往偏黑盒,编辑器模式下点几个按钮就能导出Clip,看着很省心,一旦想要在运行时里自动录制、想要控制关键帧精度、想要录完立刻做数据后处理,很容易一头扎进文档里出不来。这篇文章我会按项目实现的完整路径来写:先从方案选型讲起,再分别拆编辑器模式和运行时模式的做法,最后给到我在真实项目里踩过的坑和排查思路。代码部分以C#为主,使用的API都是Unity 2020 LTS以上版本通用的,老版本也能平移过去。
1. 项目整体设计与方案选型
1.1 先想明白三个核心问题
做动画捕获,本质上只干三件事:确定数据来源、确定采样频率、把采样结果转成AnimationClip。听起来简单,但每一步都有坑。
数据来源指的是你要记录哪些对象哪些属性。最常见的肯定是Transform,也就是物体的位置、旋转、缩放。但实际项目里,动画捕获往往不只是记录Transform这么简单。比如你想记录一个手臂IK的权重变化,或者记录一个程序的参数曲线,这时候只靠Transform就不够了,必须支持自定义组件属性的采样。这意味着你在设计录制器的时候,不能把代码写死在Transform上,要留出扩展属性绑定的空间。
采样频率则是精度和性能的平衡点。60Hz是人眼感知的临界值,做普通动作回放60Hz足够。但物理交互、布料模拟、高速运动物体,60Hz可能不够,容易出现细节丢失。我一般会做成可配置项,默认60,特殊场景可以拉高到120甚至240。采样频率直接决定了最终关键帧数量,也决定了Clip的体积,这块后面会专门讲。
把采样结果转成AnimationClip,Unity提供的核心API是AnimationClip.SetCurve。单条曲线用一个AnimationCurve对象承载,把采样到的时间点和值填进去,就成了一个可用Animator播放的Clip。整个过程不复杂,但要处理好旋转拆分量、切线模式、循环设置这些细节,否则录出来动画轻则抖动,重则直接跳变。
1.2 编辑器录制和运行时录制的本质区别
很多人以为编辑器录制和运行时录制只是调用时机不同,实际差别比想象中大得多。
| 对比维度 | 编辑器模式 | 运行时模式 |
|---|---|---|
| 录制环境 | Editor进程内,可借助Unity Recorder、ScriptableObject | 打包后的Player环境,只能靠Runtime脚本 |
| 数据来源 | 编辑器场景中可见的全部对象 | 运行中的GameObject及其组件 |
| 采样方式 | 固定帧率回调,通常由EditorApplication.update驱动 | Update/FixedUpdate或自建定时器 |
| 典型用途 | 美术制作动画资源、离线烘焙 | 玩家操作回放、物理过程记录、上传服务器做结算 |
| 调试复杂度 | 可在Animation窗口直接预览曲线 | 需要自己实现预览与验证流程 |
| 可扩展性 | 依赖Editor API,无法发布后使用 | 纯Runtime API,打安卓iOS包一样能跑 |
编辑器模式的核心优势是所见即所得。你可以先在场景里摆好角色、拖好时间轴预览,然后一键录制。Unity官方Unity Recorder包在编辑器模式下提供了非常顺滑的体验,输出AnimationClip、图像序列、视频都行。
运行时模式则完全不同。录制脚本会跟着游戏一起打包发布,游戏过程中随时可以开启录制,把玩家实际操作产生的动画状态保存下来。这种需求在动作游戏回放、体育游戏判定复盘、甚至AI行为数据采集中非常常见。运行时模式没办法依赖编辑器里的任何工具,一切都要用AnimationClip.SetCurve这类运行时API手动拼。
选型的时候不要盲目追求统一方案。如果只是给美术做个离线烘焙工具,编辑器模式就够了,硬要在运行时写一套反而浪费时间。如果目标是录制玩家操作,那就必须走运行时方案。也有两者共存的场景,我的做法是录制采样的核心逻辑共用一套,只是数据写入Clip的入口不同,这个架构后面会展开。
1.3 为什么我不建议完全依赖现成插件
Unity Asset Store上动画录制插件不少,官方也出了Unity Recorder。但我在实际项目里越来越少直接套用现成工具,原因有两个。
第一,黑盒难排查。插件在编辑器里表现良好,一旦出现录制精度不准、物体层级变化导致Clip错乱、运行时环境报错等问题,你很难定位是插件的问题还是自己使用姿势的问题。如果你心里对底层实现有数,排查起来会快很多。
第二,定制空间有限。比如我要录制一个自定义组件里的float字段,还要在两个模式里走同一套后处理逻辑,很多插件做不到这么灵活。自己动手实现一套底层的帧采样和关键帧生成逻辑,前期多花一点时间,后期扩展起来很舒服。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 编辑器模式:场景动画烘焙成AnimationClip
2.1 官方Unity Recorder的快速上手姿势
如果你只是偶尔需要把Editor场景里的动画导出成Clip,官方Unity Recorder是效率最高的选择。通过Package Manager搜索“Unity Recorder”安装,然后在Window菜单下打开Recorder窗口。
配置时有几个关键项需要注意。Output Format选“Animation Clip”,Recorded Properties选择要录制的GameObject,建议勾选子物体层级,因为一个角色动画通常涉及多个骨骼节点。Frame Rate默认60,可以根据需要调整。录制开始前,把Scene视图的播放头拖到起点,点击录制按钮,Recorder会按照你设置的帧率在后台采样,结束后自动生成一个.anim文件。
在使用这套流程时,我吃过一个亏:Recorder生成的Clip默认是Clamped Auto切线,回放时曲线会自动平滑。但如果你录制的是程序化生成的物理动作、或者带有明显节奏感的关键帧打击动作,这种平滑反而会改变手感。所以录制完成之后,我习惯在Animation窗口里把所有关键帧切线改成Linear或者Constant,具体取决于原始动作的性质。
官方Recorder的底层逻辑并不复杂:编辑器环境下通过EditorApplication回调做定时采样,采样结果写入AnimationClip。它能记录的属性范围比较广,只要在Inspector里能看到的可动画属性,基本都能录下来。
2.2 自己动手写一个编辑器烘焙器
官方Recorder虽好用,但遇到批量烘焙需求时还是得写工具。比如你有100个不同姿势的Prefab,要分别生成对应的动画Clip,手动点按钮肯定不现实。这时候用Editor脚本遍历处理才是正解。
下面这个简化版烘焙器,思路是选中一个GameObject,以60帧率烘焙其所有Transform属性到新的AnimationClip。实际项目里你可以在这个基础上扩展自定义属性录制。
csharp复制#if UNITY_EDITOR
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
public static class EditorAnimationBaker
{
[MenuItem("Tools/Animation Recorder/Bake Selected Transform")]
public static void BakeSelected()
{
GameObject target = Selection.activeGameObject;
if (target == null)
{
Debug.LogWarning("请先选中要烘焙的对象");
return;
}
AnimationClip clip = new AnimationClip();
clip.frameRate = 60f;
// 这里以Transform的三个属性为例,自行扩展其他组件属性同理
List<EditorCurveBinding> bindings = new List<EditorCurveBinding>();
AddTransfor
