RootMotion源码简单解析
前言
由于平时经常使用RootMotion
动画, 经常遇到一些问题, 所以还是凑空看了一下源码, 大概整理了一下RootMotion
动画的运行机制和关键问题
伪代码
先用伪代码简单的描述一下RootMotion
的大概运行逻辑, 以单机蒙太奇动画示例
1 | st=>start: 移动组件::PerformMovement |
这当中涉及到的内容非常多, 下面慢慢整理
动画实例部分
上面伪代码中需要的参数RootMotionParams
来自于动画资源, 也就是来自动画实例部分
1 | USTRUCT() |
参数主要就上面3个属性
这里先讨论多数情况下的RootMotion
, 一般都是由蒙太奇触发, 那么起点就是动画实例(AnimInstance
)内的UAnimInstance::Montage_Play()
播放蒙太奇的时候, 会创建一个FAnimMontageInstance*
对象来真正的播放蒙太奇动画
也就是是说, 播放蒙太奇会创建一个对象来管理这个蒙太奇动画, 同时也会存放在动画实例里面的数组和Map中
下面要去看动画实例中的函数UAnimInstance::UpdateAnimation
看调用栈, 就是来自PerformMovement()
(稍后再看)的调用
然后就调用到UpdateMontage(DeltaSeconds);
Montage_UpdateWeight();
这个刷新混合值
在 UAnimInstance::Montage_Advance()
中重点做了这个事
1 | MontageInstance->Advance(DeltaSeconds, RootMotionParams, bUsingBlendedRootMotion); |
然后在FAnimMontageInstance::Advance()
中重点做了下面的事情
ExtractRootMotionFromTrackRange()
函数获取了蒙太奇的位置插值信息, 这个计算内容也比较复杂, 会有片段以及slot等检测, 这里不展开. 得到的信息是本地空间的, 即是一个位移而不是位置, 最后计算的时候会经过转换到世界空间等计算;
那么动画实例这一部分的任务就是计算出
RootMotionParams
, 等待着移动组件的使用
移动组件部分
从UCharacterMovementComponent::PerformMovement()
开始
先判断是否开启了RootMotion
, 如果开启那么就TickCharacterPose(DeltaSeconds);
,同时清理掉当前的RootMotionParams
参数
RootMotionParams
参数都是用完就清理, 包括在SkeletalMesh
中也有类似操作
然后是TickCharacterPose()
这里对Mesh
的Tick会调用到之前动画实例部分的代码
下面通过ConsumeRootMotion()
获取参数并清理了Mesh
中的参数
Mesh
中保存里AnimScriptInstance(就是当前的AnimInstance)
, AnimScriptInstance::ConsumeExtractedRootMotion()
可以获取并清理ExtractedRootMotion
然后设置缩放以后设置到当前的RootMotionParams
1
2 UPROPERTY(Replicated)
float AnimRootMotionTranslationScale;这个缩放参数 目前没有扩展出方便蓝图调用的API, 如果有需要可以手动设置此变量来动态的控制蒙太奇的运动, 参考UE5的
WarpMotion
组件的思路
接下来跳过一大堆代码,转到下面这里
Velocity
变量直接决定下一次渲染我们角色的偏移位置, 那么实际上这里就可以简单理解为之前计算出来的RootMotionParams
参数在这里经过了空间转换, 然后通过若干计算转换成了Velocity
偷懒具体计算就不去看了
动画序列RootMotion
用动画序列来执行根骨个运动, 即开启RootMotion From Everything
开启此模式以后, PerformMovement()
会一直调用TickCharacterPose()
随后会执行如下代码
这里就要提到动画实例里面的一个多线程辅助类FAnimInstanceProxy
,如果当前的RootMotionMode
为RootMotion From Everything
,那么我们在主线程Tick的时候就会立刻去更新FAnimInstanceProxy::TickAssetPlayerInstances()
,这样是为了能及时获取到每一帧的Rootmotion
信息
随后通过FAnimInstanceProxy
类来一直获取RootMotionMovementParams
参数
总结
- 其实RootMotion本质上走的还是移动组件的处理流程,只不过其移动数据是从动画里面提取的。
- Rootmotion只支持Montage的同步; UE4的状态机太复杂不容易同步
- 除非是常规的线性运动的Rootmotion,其他的不规则的运动几乎无法预测, 导致同步效果不理想
- 从性能上说,减少数据的同步和校验可以减少服务器的CPU和内存的压力,所以Rootmotion在网络游戏中的使用要慎重考虑。