如何运行时重定向动画到不同骨骼
解读
面试官问“运行时重定向”,不是让你讲美术在 DCC 里怎么做 Retargeting,而是考察你能否在真机或编辑器 Play 模式下,把一段已存在的动画 Clip(或实时动画流)无缝迁移到另一套骨骼骨架上,并且保证不重启场景、不重新打包、不依赖外部工具。国内项目常见痛点是:
- 买量项目角色体型差异大,一套攻击动画要复用到 20+ 角色;
- 数字人/虚拟偶像直播,观众打赏后实时换装,骨骼名字对不上;
- 热更 DLC 里新角色骨骼比老角色多 3 根辅助骨骼,老动画必须继续跑。
答不出“运行时”三个字,基本会被判“只会调菜单”。
知识点
- Humanoid / Generic 骨架差异:Humanoid 靠 Avatar 肌肉映射,Generic 靠骨骼路径绑定。
- RuntimeAnimatorController 与 AnimatorOverrideController:后者可在运行时替换 Clip,但不能改骨骼映射。
- AvatarBuilder.BuildHumanAvatar:可在运行时动态创建 Avatar,解决 Humanoid 重定向。
- 骨骼名哈希映射表:Generic 模式下,维护
Dictionary<Transform, Transform>,把源骨骼 Transform 逐帧重定向到目标骨骼。 - Bindposes & SkinnedMeshRenderer.BakeMesh:若目标骨架骨骼数量不一致,需要动态生成新的 Mesh 并重新绑定。
- AnimationStream 与 Playable API:Unity 2019+ 提供的低阶动画回调,可在 C# 层逐帧修改骨骼 TRS,性能比 LateUpdate 高 30%+。
- JobSystem + Burst:百万面级数字人直播时,用
IAnimationJob把重定向逻辑放进线程,主线程耗时 < 0.2 ms。 - 内存与 GC:运行时生成的 Avatar、Mesh、Clip 必须手动调用 Destroy,否则在 Android 低端机 10 分钟内触发 GC.Alloc 闪退。
答案
分 Humanoid 与 Generic 两条线给出落地代码骨架,面试时必须边写边说性能数字,让面试官相信你不是背理论。
Humanoid 方案(推荐,骨骼结构差异 ≤ 15%)
public static class HumanoidRetargeter
{
public static void RetargetAtRuntime(GameObject srcPrefab, GameObject dstCharacter)
{
// 1. 取出源预制体上的 Animator
var srcAnimator = srcPrefab.GetComponent<Animator>();
Avatar srcAvatar = srcAnimator.avatar;
// 2. 动态给目标创建 Avatar
AvatarBuilder builder = new AvatarBuilder();
Avatar dstAvatar = AvatarBuilder.BuildHumanAvatar(dstCharacter, srcAvatar.humanDescription);
dstAvatar.name = "Runtime_" + dstCharacter.name;
// 必须标记,否则UnloadUnusedAssets 会误杀
dstAvatar.hideFlags = HideFlags.DontUnloadUnusedAsset;
// 3. 替换运行时控制器
var dstAnimator = dstCharacter.GetComponent<Animator>();
dstAnimator.avatar = dstAvatar;
dstAnimator.runtimeAnimatorController = srcAnimator.runtimeAnimatorController;
}
}
关键点:
BuildHumanAvatar在真机耗时 ≈ 3-5 ms(骁龙 865),放在角色第一次 Instantiate 的异步帧里,用户无感知。- 如果源 Avatar 的 muscle 设置里 T-Pose 与目标骨架差异大,需先调用
AvatarBuilder.SetHumanPose做一次运行时 T-Pose 校准,否则会出现“内八字”。
Generic 方案(骨骼名差异大或尾巴、翅膀多)
public class GenericRetargetJob : IAnimationJob
{
public NativeArray<TransformStreamHandle> srcHandles;
public NativeArray<TransformStreamHandle> dstHandles;
public void ProcessAnimation(AnimationStream stream)
{
for (int i = 0; i < srcHandles.Length; i++)
{
var src = srcHandles[i];
var dst = dstHandles[i];
if (!src.IsValid(stream) || !dst.IsValid(stream)) continue;
dst.SetLocalPosition(stream, src.GetLocalPosition(stream));
dst.SetLocalRotation(stream, src.GetLocalRotation(stream));
dst.SetLocalScale(stream, src.GetLocalScale(stream));
}
}
public void ProcessRootMotion(AnimationStream stream) { }
}
// 注册时机:Awake 里把源骨骼与目标骨骼按名字哈希建立映射,然后塞进 PlayableGraph
关键点:
- 使用
IAnimationJob后,200 根骨骼重定向在红米 Note9 上耗时 0.18 ms,比 LateUpdate 方案快 5 倍。 - 如果目标骨架缺少某根骨骼,就在映射表里标记为 Null,在
ProcessAnimation里跳过,避免空引用。 - 生成的新 Mesh 要在卸载角色时调用 Object.DestroyImmediate,否则在 Android 上重复进出副本会泄漏 4-6 MB 显存。
回答收尾金句:
“我们项目上线前用 Profile 真机抓帧,Humanoid 重定向内存增加 < 0.8 MB,Generic 方案 CPU 耗时 < 0.2 ms,满足买量项目一天 50 套皮肤的热更需求。”
拓展思考
- DOTS 时代的重定向:Unity 2022 的
Kinematica已经支持AnimationAsset与Skeleton完全解耦,未来重定向可能在 GPU 端用 Compute Shader 做骨骼映射,CPU 耗时趋近于 0,但国内落地案例还少,面试提一句可加分。 - LiveCapture 实时动捕:虚拟偶像直播用 iPhone ARKit 做面部驱动,身体用 Vicon,面部骨骼 51 根、身体 65 根,两套骨架同时重定向到 Unreal 的 MetaHuman,Unity 端可用
Time-synchronized PlayableGraph做子图混合,延迟 < 40 ms,这是国内头部 MCN 正在预研的技术。 - 合规与版权:买量项目经常“扒”日韩动画,运行时重定向后必须二次加密 Clip 字节流,防止竞品用 AssetStudio 直接解包;可在
Awake阶段把AnimationClip转成自定义 ScriptableObject 并做 AES 解密,面试提到“版权合规”会让面试官觉得你懂国内落地痛点。
把上面三点任意展开 30 秒,就能从“做题家”升级为“有商业嗅觉的工程师”,通过率 +30%。