技术分享
一篇文章带你看懂UE5 Motion Matching官方示例
00 分钟
2024-10-8
2024-10-8
type
status
date
slug
summary
tags
category
icon
password

🤔 搜索是个好东西

AnimGraph

Motion Matching

首先我们先看动画状态机从左到右一步一步来
notion image
第一个就是Motion Matching的节点,这里有三个绑定,首先第一个是Blend Time,也就是动画混合的时间,这里绑定了一个Get_MMBlendTime的函数,我们去看一下
notion image
这个方法很简单,首先就是先根据角色的Movement Mode运动方式去判断,这是个枚举,里面只定义了两种类型,On Ground在地面行走以及In Air在空中。在地面的时候又去判断了Movement Mode Last Frame,这个看变量名像是上一步的运动状态,我们去搜索一下看一下这个是如何赋值的
notion image
他在Update States方法中第一步去赋值,把Movement Mode的值赋给他,然后又去角色运动组件哪里获取了角色的运动状态赋值给Movement Mode,那也就是说Movement Mode Last Frame是前一步的运动状态。然后就根据这个变量去设置在地面和在空中不同的混合时间,也是为了动画的流畅吧。
下面一半在空中的时候就是通过Velocity速度变量去判断不同的混合时间。
notion image
OK那让我们回过来看下面的绑定的是什么。
更新时 绑定的是Update_MotionMatching函数
notion image
这里也比较简单,Evaluate Chooser这里就是获取PoseSearch的数据库,中间这里就是Motion Matching的节点获取,下面这个方法Get MMInterrupt Mode,是获取Motion Matching的中断模式,意思就是通过这里的返回值去判断要不要去搜索数据库进行运动匹配。那我们也看一下这个方法。
notion image
虽然节点很多哈,但是我们还是一眼能够看出来他想做什么,就是判断上一个状态和下一个状态是否不同,这里多了几个新的枚举,也一起去看下
Movement Mode:角色运动模式,枚举值OnGround(地面),In Air(空中)
Movement State:运动状态,枚举值Idle(待机),Moving(移动)
Gait:步态,枚举值Walk(行走),Run(跑步),Sprint(冲刺)
Stance:站立,枚举值Stand(站立),Cround(蹲伏)
那这个函数什么意思呢?
我用通俗的话讲一下
就是先判断角色的运动模式在地面的时候,运动状态是否发生变更,步态是否发生变更,站立状态是否发生变更,如果发生了变更就不去中断MotionMatching的匹配,但是如果角色的运动模式发生了变更,也就是从地面跳起来了,无论如何都要去匹配。
notion image
运动匹配状态更新时绑定的是Update_MotionMatching_PoseSelection函数
notion image
这个也挺简单的,意思就是去获取当前Motion Matching匹配到的数据表把他存在了Current Selected Database的变量里,后面会用到的。

运动偏移

这个很好理解哈,就是添加了运动偏移,这里是一个混合空间,传入了一个X轴的LeanAmount偏移向量
notion image
这个混合空间就是三个Pose,传入的参数可以看到是LeanLR
notion image
好,那我们看下Get_LeanAmount函数看看参数是如何获取的
notion image
首先进来又是一个函数,Calculate Relative Acceleration Amount计算相对加速度,下面的这个Map Range Clamped是Kismet数学库的计算方法,意思就是通过传入的值的范围映射到新的范围区间,比如我现在传入的是200,返回的就是0.5,传入的是500,返回的就是1.0,传入的是400,那就是0.7左右吧(估的)
Calculate Relative Acceleration Amount有点长,就一半一半讲
notion image
首先先判断了角色运动组件中的MaxAcceleration最大加速度和GetMaxBrakingDeceleration最大制动减速,这两个都要大于0才做后面的逻辑,然后他取了Acceleration这是当前的加速度和Velocity这是当前速度注意区别,然后两个数值相乘,也就是dot节点,判断他们是否大于0,大于0的话就说明角色是向前移动,小于0就是向后移动
notion image
然后后半部分,如果前面的结果是大于0的话,也就是向前移动,这里Vector Clamp Size Max节点的意思是限定传入值向量A的最大长度,然后返回个结果出去。
这里传入了个Velocity Acceleration变量,这个看英文是有点难理解,我们直接去看他的赋值
notion image
这么一看就清晰多了是不是,意思很简单,就是获取在1秒之间角色移动了多少向量,Get World Delta Second是获取时间差值,就是上一帧到下一帧用了多少时间,Velocity和Velocity Last Frame相减就是这两帧之间移动了多少,我们拿这个数值除这个时间差值,就得到了一秒移动的距离向量
那这个理解了我们上面的是不是也就很好理解了,拿到每秒钟移动的向量去除最大加速度就得到了角色的相对移动向量,然后和角色的Transform Rotation去求Unrotate Vector未旋转向量就能够得到角色是向哪里偏移的啦。
下面向后移动的也是同理,只不过参数换成了最大制动减速距离。
然后计算出来的偏移量的X轴偏移值就传入混合空间中,然后传入Apply Mesh Space Additive节点,这个就是添加Additive姿势用的,他的启用条件这里绑定了IsMoving函数,就是判断角色是否在移动的
notion image
这里很简单啊,判断Velocity当前速度和Future Velocity未来速度是否大于0就是正在移动了,但是这个未来速度还不知道怎么取得,我们顺便也去看看
也是蛮长的,看蓝图可以看出他是通过运动匹配的生成的轨迹去预估的未来速度,这可能也是Motion Matching的厉害之处了
notion image
先看前半部分,这里有个Pose Search Generate Trajectory看名字是通过姿势搜索去生成轨迹的,这里看到他传入了几个参数,Animinstance动画实例,Trajectory Generation Data轨迹生成数据,DeltaSecond时间,Trajectory轨迹组件,Previous Desiredn Controller Yaw像是轨迹预测的偏移,然后后后面这个是Handle Trajectory World Collisions就是处理轨迹在地图中的碰撞预估之类的了,可以输出调试线条,然后结果都保存到变量中
notion image
这里就是他计算未来速度的地方了,Get Trajectory Sample at Time,获取未来时间预估的轨迹,返回值Facing朝向,Position 位置,Accumulated Seconds累计秒数,他这里是计算了未来0.5秒和0.4秒的预估位置相减得到的0.1秒的移动向量,然后乘10得到1秒的移动向量。所以Future Velocity就是未来一秒可能移动的向量。
IsMoving就是判断当前的移动向量和未来的移动向量都不等于0就说明是正在移动了。
notion image

角色头部朝向

这个做的就是角色头部朝向玩家摄像头方向,同样这里也是一个混合空间,根据bool值判断是否混合,然后Dead Blending死混合后插入
notion image
混合空间这里是绑定了一个函数Get_AOValue,这个是获取偏移的向量的
notion image
这里也很简单,获取了角色控制器的旋转向量Rotation,然后获取根节点的旋转向量,两个向量相减就获得了偏移的角度,但是他这里的Root Transform获取又有点特殊,他是从偏移根骨骼动画蓝图节点中获取当前世界空间变换来的。可以去看一看
notion image
首先有个获取控制台的变量的节点,也就是说可以跳过控制台命令去控制他使用那种获取方式,a.animnode.offsetrootbone.enable 1 这样就是开启,0就是关闭。
如果没开启就还是原来常用的获取角色Actor的Transform,如果开启了就可以看到他这里使用的是OffsetRoot动画节点引用,Get offset Root Transform获取变换目标,这里他把z轴加了90度,上面备注说明是因为默认情况下,骨架网格在编辑器中旋转-90度,因此添加了偏移量,以便更容易与其他变换进行角度比较。
然后Blend Pose这里绑定了一个Enable_AO的函数
notion image
这里也很简单,Get AOValue就是前面的函数,Rotation Mode是旋转模式,这个在角色蓝图中定义,是个枚举,OrientToMovement和Strafe,OrientToMovement就是跟随运动方向旋转,Strafe就是一直保持旋转,英文是扫射的意思。GetSlotLocalWeight的意思就是获取插槽播放蒙太奇的权重,如果没有播放蒙太奇就是0,那这个函数的意思就是,偏移的角度小于115度,并且旋转模式是扫射模式,并且没有播放蒙太奇,那么角色的头部就会跟随旋转
notion image

根骨骼应用偏移

这就是Motion Matching的一个比较重要的地方了,就是通过修正根骨骼来实现动作的流畅的,我们这里传的参数是告诉他是否要应用便宜的,可以看到又三个参数
Translation Mode:偏移模式
Rotation Mode:旋转模式
Translation Halflife:偏移半衰期
notion image
Get_OffsetRootTranslationMode
notion image
这个也很简单,就是先判度是否在播放动画蒙太奇,如果在播放的话就不应用偏移,就是释放。如果没有播放蒙太奇就再根据移动模式来判断,是再地面还是在空中,如果在地面的话就再判断是否在移动中,如果在移动中就进行应用,就是插值,否则就释放。如果在空中就一直是释放状态。
Get_OffsetRootRotationMode
notion image
这个就更加简单了,只判断了插槽是否正在播放蒙太奇,但是他这里的返回值根上一个不一样,这里是累加。
Get_OffsetRootTranslationHalflife
notion image
这个就更更更简单了,就是根据角色的运动状态来判断了,在待机和移动下的半衰期不同
notion image

腿部IK

就是个简单的腿部IK实现,效果并不怎么样
notion image
notion image

轨迹追踪

这个是和Motion Matching一起使用的,Trajectory是轨迹组件,这里需要传入历史姿势
notion image
notion image

运动状态

前面讲解了AnimGraph的内容,基本上动画蓝图大部分的内容都包含在了里面,但是还有几个单独的运动状态是在Motion Matching的运动匹配中使用的
我们在动画蓝图中可以看到有个Movement Analysis的函数分组,但是里面的方法有的并没有在蓝图中被调用,以下的函数返回的都是布尔类型,都是在运动匹配数据库中使用的
notion image
我们可以先看下运动匹配的选择器,这个选择器使用的地方就在Update Motion Matching函数中,我们前面讲过这个函数。里面有个Evaluate Chooser节点,里面就指定了Pose Search的选择器
notion image
选中这个节点,在右边详细分栏中就能看到指定的选择器是哪个了
notion image
点击文件夹搜索的图标就能定位到文件了
notion image
那我们打开看看,里面比较简单,又分了两个个不同的选择器,右边有一个条件浮点值范围的条件,那这玩意又是啥呢
notion image
我们点击它可以看到在角色的动画蓝图中的属性,那我们再去搜索一下
notion image
可以看到这个属性就是简单的获取控制台参数,默认值是0
notion image
那回到数据库这边可以看到有两个Dense(密集)和Sparse(稀疏)两个选择器是通过这个属性来判断的,根据这个值的不同,走不同的数据库。还有一个没设置任何条件的就是默认值,可以不用管,但是默认指向的也是Dense,这两个数据库呢逻辑都是一样的,就是用的动画数量不同
我就就随便进一个,可以看到这里设置了四个不同的筛选器,分别是Idle,Walk,Run,InAir,就是待机,走跑跳的状态,然后后面的筛选条件也很清晰哈,Movement Mode就是运动模式,Stance就是姿态,是站立还是蹲伏,Movement State就是运动状态,是待机还是移动,Gait就是步态,是走路还是奔跑。
notion image
这里就进Walk的筛选器里面吧,刚好跟那几个方法都对上了,点击编辑进入,这里面就是指定的真正的角色动画的数据库了,但是同时也又很多的条件哈。
notion image
初看像是变量,但是点击一看的时候就能够看到他指定的是函数,这些就是这个官方示例的运动状态了,数据库就是根据运动的状态的不同去匹配不同的数据库,再根据数据库中指定的骨骼的移动,去自动识别适配的运动动画
notion image
ok那我们就一个一个来

Is Moving

是否移动函数,这个很简单就是通过判断当前速度向量和未来速度向量都不等于0
notion image

Is Starting

是否启动函数,是判断起步动作的时候用的,首先肯定是已经在移动状态了,其次就是未来的移动向量XY轴比当前的移动向量XY轴的长度多100及以上,第三个就有意思了,他判断了当前运动状态中的标签不能包括Pivots才是启动状态
notion image
取值也很简单,就是从当前匹配到的运动数据库中获取所有的标签
notion image
那这个标签在哪里呢,既然是Pivots,那我们就去查看Pivots的数据库看看有没有这个东西,搜索一线Dense里面的姿势搜索数据库
notion image
我们打开第一个跑步的看看,搜索Tag,欸,还真有个Pivots的标签。这就说明只要我们姿势匹配进入了这个PSD_Dense_Stand_Run_Pivots的数据库的筛选器里,就不会再进入Starting状态,然后Walk_Pivots数据库里就没有这个标签,那就说明在走路的状态下我们可以Starting状态,我感觉就是角色走着走着转向冲刺可以触发Starting动画,而跑步是否转向不会触发Starting动画
notion image

Is Pivoting

这个就是枢轴旋转了,逻辑也比较简单,就是把未来速度向量和当前速度向量相减然后取绝对值,然后根据角色的旋转模式去判断角度,如果是Orient to Movement(跟随运动旋转)那就是偏移得大于等于60度才会触发转向动画,如果是Strafe(扫射)那就是大于等于40度就触发
notion image

Should Turn in Place

这个是原地转身,如果角色的旋转向量和根旋转向量相减的绝对值大于50,并且角色处于瞄准状态下(按住鼠标右键),或者角色当前处于待机,即将准备运动的时候就触发原地转身
notion image
这里瞄准状态使用了线程安全的绑定哈,就在角色蓝图中
notion image

Just Landed Light

这个是轻轻的落地,也很简单,就是去判断了角色的落地加速度
首先是角色蓝图中的Just Landed变量,
notion image
角色蓝图中On Land事件中对Just Landed变量去赋值,这里还使用了GAS的标签,同时设置了Land Velocity下落速度变量,On Land事件是角色运动状态发生变换的时候自动触发的哈
notion image
然后这个Heavy Land Speed Threshold重型陆地速度阈值就是定义的常量,这里定义的是700,也就是小于700的时候就是轻落地,大于700那就是重型落地

Just Landed Heavy

那么这个就很好理解了哈,就是判断Heavy Land Speed Threshold重型陆地速度阈值这里不同
notion image

Just Traversed

穿越,这个就是官方实例中翻墙的动画状态了,就是判断现在没有播放动画蒙太奇,并且Get Curve Value(获取动画曲线值)当前的动画中的MovingTraversal曲线的值要大于0才能够触发穿越状态
notion image

Should Spin Transition

旋转过度,这个啥意思呢,就是角色现在面朝前方,但是我需要向后方移动,是不是就是需要一个转身动画呢,这个函数就是判断这个的
首先也是判断角色旋转向量和跟节点的旋转向量Z轴的差值的绝对值要大于130,也就是旋转角度必须大于130度,然后速度要大于150,并且当前的姿势数据库中不能包含Pivots标签,也就是跑步的时候不会触发这个动画(我们前面看过的)
notion image
 
notion image
 

完结

以上就是UE5 Motion Matching官方示例的重要内容部分了,其余的角色蓝图很简单,跟我们之前做的也没什么太大的区别,这里也就不过多赘述了。
官方示例里面的动画非常多,但是我们想自己实现一个这样的效果,对动画资源的需求非常高,首先必须动画自带跟运动,如果不带的话,就得自己手动去添加关键帧让动画根节点动起来,这也是我飞行状态的实现原理,大家感兴趣的可以自己去尝试尝试,可以先使用虚幻争霸:莫瑞尔的动画资源,比较适合女性角色,SuperHero的动画包我试过了,他的飞行动画方向移动不是完整的动画,而是通过additive实现的,所以非常不适合用在Motion Matching中。
 
 

📃参考文章

 
 
致谢:
💡
欢迎您在底部评论区留言,一起交流~
notion image
微信公众号 - 六六姐的小喇叭
CG交流Q群 - 369748859
游戏群 - 485939898
学习途中的一些随笔,
希望对大家有用
💖😎😉🤣😘💕
 
notion image
 
 
 
上一篇
maya视图不以选中物体为中心旋转的解决方法
下一篇
超动态天空控件添加