Unity构建中Animancer非人形Avatar运行时SetCurve失效求助
问题描述
我正尝试在Unity中使用Animancer在运行时生成并播放动画。当前实现通过AnimationClip.SetCurve创建动画,编辑器中运行正常,但构建后失效,因为SetCurve是编辑器专属函数。
我的代码功能是读取GameCube的BCK二进制动画文件中的关键帧,通过SetCurve添加到AnimationClip中,核心代码如下:
public AnimationClip CreateAnimationClipFromBCK(BMD bmd, BCK bck, string startBone = "##STOP##") { AnimationClip clip = new AnimationClip(); clip.name = bck.Name.Replace(".bck", ""); if (bck.LoopMode == LoopType.Loop) clip.wrapMode = UnityEngine.WrapMode.Loop; if (bck.LoopMode == LoopType.Once) clip.wrapMode = UnityEngine.WrapMode.Once; List<SkeletonJoint> pose = bmd.JNT1Tag.AnimatedJoints; if (bck.m_animationData == null) return null; int numJoints = Math.Min(pose.Count, bck.m_animationData.Count); AnimationCurveHolder animationData = new AnimationCurveHolder(); for (int i = 0; i < numJoints; i++) { string boneName = pose[i].Name; if(startBone != "##STOP##") if (!IsDescendantOf(pose[i], startBone)) continue; var jointAnim = bck.m_animationData[i]; string path = GetFullPath(pose[i]); animationData.PositionCurvesX[path] = new AnimationCurve(); animationData.PositionCurvesY[path] = new AnimationCurve(); animationData.PositionCurvesZ[path] = new AnimationCurve(); animationData.RotationCurvesX[path] = new AnimationCurve(); animationData.RotationCurvesY[path] = new AnimationCurve(); animationData.RotationCurvesZ[path] = new AnimationCurve(); animationData.RotationCurvesW[path] = new AnimationCurve(); } for (int j = 0; j < bck.AnimLengthInFrames; j++) { float ftime = j; for (int i = 0; i < numJoints; i++) { if(startBone != "##STOP##") if (!IsDescendantOf(pose[i], startBone)) continue; var jointAnim = bck.m_animationData[i]; string path = GetFullPath(pose[i]); OpenTK.Vector3 translation = new OpenTK.Vector3( bck.GetAnimValue(jointAnim.TranslationsX, ftime), bck.GetAnimValue(jointAnim.TranslationsY, ftime), bck.GetAnimValue(jointAnim.TranslationsZ, ftime) ); OpenTK.Vector3 rot = new OpenTK.Vector3( bck.GetAnimValue(jointAnim.RotationsX, ftime), bck.GetAnimValue(jointAnim.RotationsY, ftime), bck.GetAnimValue(jointAnim.RotationsZ, ftime) ); pose[i].Rotation = OpenTK.Quaternion.FromAxisAngle(new OpenTK.Vector3(0, 0, 1), WMath.DegreesToRadians(rot.Z)) * OpenTK.Quaternion.FromAxisAngle(new OpenTK.Vector3(0, 1, 0), WMath.DegreesToRadians(rot.Y)) * OpenTK.Quaternion.FromAxisAngle(new OpenTK.Vector3(1, 0, 0), WMath.DegreesToRadians(rot.X)); animationData.PositionCurvesX[path].AddKey(ftime, translation.X); animationData.PositionCurvesY[path].AddKey(ftime, translation.Y); animationData.PositionCurvesZ[path].AddKey(ftime, translation.Z); animationData.RotationCurvesX[path].AddKey(ftime, pose[i].Rotation.X); animationData.RotationCurvesY[path].AddKey(ftime, pose[i].Rotation.Y); animationData.RotationCurvesZ[path].AddKey(ftime, pose[i].Rotation.Z); animationData.RotationCurvesW[path].AddKey(ftime, pose[i].Rotation.W); } } foreach (var kvp in animationData.PositionCurvesX) clip.SetCurve(kvp.Key, typeof(Transform), "localPosition.x", kvp.Value); foreach (var kvp in animationData.PositionCurvesY) clip.SetCurve(kvp.Key, typeof(Transform), "localPosition.y", kvp.Value); foreach (var kvp in animationData.PositionCurvesZ) clip.SetCurve(kvp.Key, typeof(Transform), "localPosition.z", kvp.Value); foreach (var kvp in animationData.RotationCurvesX) clip.SetCurve(kvp.Key, typeof(Transform), "localRotation.x", kvp.Value); foreach (var kvp in animationData.RotationCurvesY) clip.SetCurve(kvp.Key, typeof(Transform), "localRotation.y", kvp.Value); foreach (var kvp in animationData.RotationCurvesZ) clip.SetCurve(kvp.Key, typeof(Transform), "localRotation.z", kvp.Value); foreach (var kvp in animationData.RotationCurvesW) clip.SetCurve(kvp.Key, typeof(Transform), "localRotation.w", kvp.Value); _animationCurveHolders.Add(animationData); return clip; }
由于未使用人形Avatar,不确定如何适配官方讨论中的方案,求无需SetCurve的运行时动画生成播放方案,尤其是适配非人形骨架和Animancer Pro的实现建议。
解决方案
1. 自定义Animancer状态直接驱动Transform
跳过AnimationClip,直接基于Animancer的Playable架构,手动采样动画曲线并设置Transform属性:
- 核心思路:保留原代码生成的
AnimationCurveHolder数据,创建继承自AnimancerState的自定义状态,在每一帧根据播放时间采样曲线,直接更新关节的localPosition和localRotation。 - 示例代码框架:
public class RuntimeBoneAnimationState : AnimancerState { private Dictionary<string, Transform> _boneTransforms; private AnimationCurveHolder _animationData; private float _duration; private WrapMode _wrapMode; public RuntimeBoneAnimationState(Dictionary<string, Transform> boneTransforms, AnimationCurveHolder animationData, float duration, WrapMode wrapMode) { _boneTransforms = boneTransforms; _animationData = animationData; _duration = duration; _wrapMode = wrapMode; } protected override void OnUpdate() { base.OnUpdate(); float time = GetWrappedTime(Time, _duration, _wrapMode); foreach (var kvp in _boneTransforms) { string path = kvp.Key; Transform bone = kvp.Value; // 采样位置曲线 Vector3 pos = new Vector3( _animationData.PositionCurvesX[path].Evaluate(time), _animationData.PositionCurvesY[path].Evaluate(time), _animationData.PositionCurvesZ[path].Evaluate(time) ); bone.localPosition = pos; // 采样旋转曲线 Quaternion rot = new Quaternion( _animationData.RotationCurvesX[path].Evaluate(time), _animationData.RotationCurvesY[path].Evaluate(time), _animationData.RotationCurvesZ[path].Evaluate(time), _animationData.RotationCurvesW[path].Evaluate(time) ); bone.localRotation = rot; } } private float GetWrappedTime(float time, float duration, WrapMode wrapMode) { if (wrapMode == WrapMode.Loop) return time % duration; else if (wrapMode == WrapMode.Once) return Mathf.Clamp(time, 0, duration); return time; } }
- 使用方式:加载BCK数据后,将关节路径与对应Transform映射,创建该状态并添加到AnimancerComponent中播放。
2. 用PlayableGraph构建运行时动画轨道
利用Unity的Playable API,将每个关节的曲线转换成独立的AnimationCurvePlayable,构建动画图后连接到Animancer:
- 步骤:
- 为每个关节的位置/旋转曲线创建
AnimationCurvePlayable。 - 将这些Playable绑定到对应Transform的属性。
- 将整个PlayableGraph包装成
AnimancerState,纳入Animancer的播放管理。
- 为每个关节的位置/旋转曲线创建
- 优势:利用Unity原生动画系统的插值优化,性能比纯手动驱动更稳定,适合复杂动画场景。
3. Animancer Pro专属优化
Animancer Pro提供了更灵活的Playable扩展:
- 使用
PlayableWrapperState快速封装自定义Playable,无需手动继承AnimancerState。 - 利用
AnimancerLayer的Playable属性直接插入自定义动画轨道,与现有动画混合。 - 对于批量关节动画,可使用
GenericAnimationState的底层API直接注入曲线数据。
内容的提问来源于stack exchange,提问作者Finn
相关产品推荐
相关产品推荐

