这是个人学习ALS过程中的一点笔记,不对的地方谢谢指出~
从
开始看
在跑步动画中进行脚落地,摄像机摇动,和左右脚区分,这个weight——gait曲线用于区分动画,使用见下面
针对小跑和快跑,使用当前的相对加速正前方分量x值【0-1】来进行混合
制动时的最大减速度
UnrotateVector将世界坐标系变成局部坐标系,再来一次 RotateVector 将 会把局部坐标系变成世界坐标系。
UnRotateVector作用是让Vector A绕着原点逆时针旋转 B。
RotateVector作用是让Vector A绕着原点顺时针旋转 B。 【AdvancedLocomotionSystemV】使用 UnrotateVector 和 UnrotateVector 增加向量值的原因_Tanzq的博客-CSDN博客
得到基于当前角色旋转值的相对加速度【所以后面要UnRotateVector,因为是当前旋转的相对值】,并将其归一化到-1到1,以表示最大制动减速度和最大加速度
前进和后退,跑步和走路的所有混合动画,LR指定左右,F前进,B后退,指定Stride步幅和walkrun 走路跑步混合值,standing play Rate立姿状态下动画速度。这里为各个姿势都设立了同步组。
https://docs.unrealengine.com/4.27/zh-CN/AnimatingObjects/SkeletalMeshAnimation/SyncGroups/
计算WalkRunBlend,简单的0和1判断
计算步幅大小
计算步幅混合。该值在混合空间中用于缩放步幅(脚移动的距离),以便角色可以以不同的移动速度行走或跑步。它还允许行走或跑步步态动画独立混合,同时仍将动画速度与移动速度相匹配,防止角色需要播放半行走+半跑步混合。曲线用于将步幅量映射到速度以实现最大控制。
strideBlendNWalk和strideBlendNRun是两个曲线
这里使用get anim curve clamped自定义的宏,获取weight_gait曲线值, walk是1,run是2, sprint是3,这里加上了-1的偏移,所以walk用上面n walk 的曲线,run和sprint用run的曲线
后续通过下蹲的动画中CLF曲线去混合跑步走路/下蹲状态的步幅,下蹲状态下步幅为walk的大小
计算立姿状态下动画速率,通过将角色的速度除以每个步态的动画速度来计算播放速率。lerpd alpha由存在于每个anim中的“weight_gait”动画曲线确定,因此播放速率始终与当前混合动画同步。该值还除以步幅和mesh scale,使得随着步幅或比例变小,播放速率增加。
这里speed都是提前定义好的,walk speed为150,run speed为350,animated sprint speed为600,由于sprint的weight值是3,所以偏移为-2
通过将角色的速度除以动画速度来计算蹲起播放速率。该值需要与站立播放速率分开,以改善运动中从蹲下到站立的混合效果。
整体逻辑和计算立姿状态差不多,crouch speed为150
回到
中
混合计算所有移动动画的循环,先将各个姿势保存起来以便重用。针对前进动画先判断是普通forward还是冲刺,然后判断是否应用附加着陆动画中的mask_sprint曲线,不用mask可能会着陆时压迫膝盖导致ik问题,看下来是着陆时先播放普通forward动画,再过渡到现在的动画
接着计算方向
该状态机基于运动方向和“HipOrientation\u Bias”曲线将方向循环混合在一起,以便在切换方向或更改hip旋转时实现更好的混合。
定义了一个方向枚举-前后左右,和六个移动方向,左前、前、右前、左后、后、右后
这里动画中使用了 过渡规则共享(transition rule sharing) 以提高效率, 即将一个过度规则设置(提升)为可共享的,那么当遇到了相同的过度规则,我们只需要设置为这个过渡规则的引用就可以了,同时更改其中一个引用规则,其它使用该共享规则的地方也会同步更新了。每种规则是一种颜色的箭头。详见大神: [玩转UE4/UE5动画系统>基础篇] 之 过渡规则共享(Transition Rule Sharing)-提升效率的小技巧_开发游戏的老王-CSDN博客
还使用了过渡渐入渐出共享,使用的changedirection blend profile中将下肢的权重设为2,比上肢优先级更高
这里黄色dir==right,绿色为dir == forward, 红色为dir = backward, 紫色为dir == left,同一大方向的左右转换还是比较简单的。同一侧方向的前后也通过forward和backward进行切换,而同一竖直方向上,左右无法直接切换,左前无法直接切换到右前,有中间一个过渡,而左前可以将方向标记为右直接切换到右后,改变竖直方向和水平方向。
↑粉色
条件只在前进和后退的左右状态下使用,这里用到了曲线Feet_Crossing,该曲线标识角色两腿是否交叉,==0为不交叉的状态,hiporientation_bias为臀部朝向曲线,小于-0.5朝左,大于0.5朝右,所以这里会同时根据dir值或者 脚步交叉+臀部方向去判断前后,同理
靛蓝色节点也是如此,在左侧动画下,双脚未交叉,臀向右,为后退,臀向左,为前进,在右侧动画下,双腿未交叉,臀向左为后退,臀向右为前进。这里白色的条件为非共享规则:双腿非交叉,臀部曲线绝对值在0.5内,当前动画混合权重为1,测了下不知道怎么触发这个规则。
进入具体的动画节点,MoveF为例:
这里使用了自定义结构体velocity blend 【F,B,L,R】四个方向的分量,去混合各个方向的动画,然后为Yaw Offset曲线设置当前角色的Yaw值,这个yaw值根据角色当前速度方向与控制方向的差值,在曲线中拿到。
这里的yaw值用于附加计算角色旋转插值速度【Update Grouded Rotation】,Smooth Character Rotation 用于平滑的旋转角色,Target Interp Speed是当前旋转到当前目标旋转的插值速度,Actor Interp Speed是当前角色旋转到目标旋转值的插值速度。这个Yaw Offset用于控制角色实现更真实自然的转动
这个velocity blend是这样计算的,取方向的单位向量【unrotate取当前旋转值的相对值】,将x方向映射到f和b分量上,将y方向映射到l和r分量上,通过这个变量计算去使用multiblend节点进行混合以实现比混合空间更好的方向混合效果。
这个函数则是去计算上文中提交的movement dir和yaw值。设置yaw offset。这些值会影响动画图形中的“YawOffset”曲线,并用于偏移角色旋转以实现更自然的移动。曲线允许对每个移动方向的偏移行为进行精细控制。
这里前进后退方向和左右方向的yaw 曲线值不尽相同,左为fb,右为lr。通过计算控制器与角色的yaw差值,在曲线中选择对应yawoffet值去设置曲线值。
接着计算movement dir,该值表示角色在“Cirection / Aiming”旋转模式期间相对于摄影机移动的方向,并在循环混合动画层中用于混合到适当的方向状态。
这里aiming rotation其实就是get control rotation值,和计算yaw值部分相同,计算角色自身yaw和控制器yaw差值,算出角色运动方向,这个calculate quadrant是自定义函数,通过yaw值计算出方向,
angle in range,increase buffer为true 使用max a + buffer,min a - buffer,为flase,使用max a - buffer, min a + buffer。 这个对于angle in range函数是否使用增涨缓冲区比较疑惑,这几个or条件必然是true的,结果是必然使用增长缓冲区的,奇怪为什么这样写。
看后半段,apple diagonal scaling针对脚部ik进行使用缩放值diagonal scale amount进行缩放设置,这个缩放量基于当前的forward速度绝对值去取曲线值0-1,然后基础缩放 这里设为的是1.4,就增加了ik的对角线上的检测距离。个人理解就是速度越快腿越开,检测距离越大。
后面als_n_lean针对跑步和冲刺的动画添加转弯时的倾斜,这里使用了倾斜量的变量LeanAmount,两个成员变量代表前后和左右的偏移,这两个值是通过当前相对加速度插值得到的,ground lean interp speed为提前定义好的4
这一部分差不多看完,即移动循环
总结下来是对站姿的移动状态还有转弯时的倾斜做设置
接下来看移动细节
此状态机根据角色的移动方式将附加细节动画应用于移动周期。对上一步得到的locomotion cycles进行处理
这里run start 和 walk->run节点内容差不多,就是为当前的动画附加一个缓冲的动画,前者startposition为0.15,后者为0.1,即走路状态下比起静止状态下,切换到跑步状态的缓冲更快
GetRelevantAnimTimeRemaining获得当前动画的剩余时间,上面的pivot是设置急刹车状态的回转动画的缓冲,例如一直向前,突然往后,进入第一次回转,这时再突然向前,进入第二次回转,多次则在一二次之间进行循环。
基于动画通知pivot事件,当前速度xy分量length小于trigger pivot speed limit【200】则认为是进行了回转,并在0.1s后取消回转状态。这里回转event是在之前的移动循环状态机中标识的,中间这六个大角度转向会触发pivot
first和second pivot中的动画和runstart等中完全一致,只是附加动画的起始位置为0.25,缓冲开始时间更短。这两个回转动画节点在pivot为1且上一个动画剩余时间大于0.1时进行反复切换以模拟在短时间内的反复制动回转状态。
这里使用了过渡规则共享,看看shouldmove是怎么计算的,现在正在移动并且有移动的输入,或者速度大于150,这个移动输入是当前加速度除以最大加速度0到1,ismoving是当前速度xy分量长度大于1
not moving
修改曲线foot lock,这两个曲线其实代表的就是脚的固定,为1时脚是固定的,为0则是运动的,enable_transition是用来判断是否能原地转身的,只有当角色是以第三人称朝向摄像机并且et曲线权重为1才可以原地转身。这个slot(n turn/rotate)是旋转动画的slot,将普通动画资源动态播放成蒙太奇,并指定slot为这个,这里带前缀n,是只播放站姿旋转。后面设置了旋转量RotationAmount曲线的值,这个曲线值定义了动画每一帧的旋转量,在Update Grounded Rotation中使用,这个rotation scale是由原地旋转时计算得出的,旋转比例因子,来看看怎么计算这个比例
第一步,计算目标旋转与当前角色旋转的差值,这个就是要旋转的角度。第二步,用旋转角度与180度转向的阈值【130度】进行比较,同时区分站立和蹲伏,得到提前定好的旋转资源。第三步,检查是否在播放当前资源的动画,是否覆盖动画,将当前动画当作蒙太奇播放并指定slot为Turn/Rotate。第四步计算旋转比例,如果在旋转的资源中勾选scaleturnangle,则也会把期望转动角度和实际转动角度之比算进去
而外层也会有一个Turn in Place Check 去管理转身动画的调用,当瞄准角度和瞄准的旋转角速度大于阈值后,会在一定的延迟后进行旋转。这个延迟可能是为了让动作更加真实自然?将其去掉也未见明显区别
notmoving中的转身逻辑只在第三人称,并且旋转方式为lookingdirection才启用,而aiming是瞄准模式或者第一人称下,角色实时旋转,使用下面的rotate left、right 状态节点去实现,修改旋转曲线值还有动画播放速率,
这个aiming angle是当前角色需要旋转的角度【control rotation - actor rotation】,通过>-50到>50来判断左右,再将旋转角速度aim yaw rate映射到1.15到3,这个旋转角速度就是每个delta之间yaw的差值
moving 使用上一步locomotion detial的缓存
stop pre-stop跳转到down和up两个状态之一,使用feet position曲线确定脚是否落下,-1为左脚着地,1为右脚着地,当曲线绝对值大于0.5认为还未着地,跳转到up状态,反之跳转到down状态,lock foot中设置曲线的值标记当前正确的落在地上的脚
这里根据臀方向和当前速度方向去混合脚着地的动画,使用blendperbone指定影响thigh骨骼【只影响下肢】,混合后脚着地,然后将着地脚的曲线设置为1。这里Hips direct其实也是根据之前directional states 那个六方向状态机标识的事件去设置的。
moving->stop->not moving之间依据state 权重和should move变量去进行过渡。stop中还标识了->stop r \ l 的event
Locomotion States看完了,主要是为动画设置静止状态和原地转身、旋转的状态
接着看蹲伏状态(CLF)
Locomotion Cycles
结构与立姿状态下的locomotion cy 差不多,这里通过crouching play rate 去控制动画播放速率,然后将六方向的移动动画混合在一起,这个混合的状态机也和站姿相差无几,也是通过movement direction ,脚步交叉feet crossing、hiporientation 臀方向来进行方向状态切换。
这里计算蹲伏动画播放速率和站姿状态差不多,用当前xy分量的速度除以150的固定蹲伏移动速度再除以步幅,这个步幅和站姿使用的值是同一个,前文有说,蹲姿的话步幅与walk是一致的,最后除以mesh scale,得到rate。
这里使用步幅插值混合普通蹲伏动画是模拟从静止到运动的状态。后面计算ik的scale和添加倾斜与前面的站姿状态部分一致。
接下来是
(CLF)Locomotion States,主要也是为动画设置静止状态和原地转身、旋转的状态,与站姿的state相差无几
接下来是
主要地面状态,上文两个姿态的动画缓存起来在这个states中使用
main ground states状态机中,Entry点进来会重置GroundEntryState,触发Reset-GroundedEntryState事件,假如当前进入状态是Roll,则会切到From Roll状态锁定左右脚。
在翻滚动画中使用了自定义的notify去通知翻滚事件,同时还加入了镜头抖动的通知去增强动感。
standing和crouchingLF状态内容差不多,都是拿到站姿或者蹲姿的缓存,将姿势的标记曲线值设为1
和
中分别是站立到蹲下,蹲下到站立的动画。
则是对当前动作的过滤,只有在非特殊情况下才能在站立和蹲下之间切换,例如:滚动,攀爬,等状态下就失效
红线是运动状态下的路线,蓝线是静止状态下的,可以看见在静止状态下会播放一个状态的过渡动画。从standing到n->c ,和从crouching LF 到 c->N的判断条件是stance枚举,这个在角色蓝图中依据输入进行设置,黄色过渡规则则是判断是否有移动、旋转
,这个规则优先级是1,stance判断优先级是2,运动状态下跳过n->c。前面的from roll是判断翻滚,stance==roll,这里是进行翻滚完成的设置,设置为最后一帧,双脚着地
。最后外面使用grounded slot,并将动作进行缓存
这里的Ground slot用于在EventGraph中播放附加的转换动画
,
在walking停止或者着陆时会附加这些过渡动画,或者时持物时从releaxed->ready \ ready -> releaxed 在非移动的站立姿态下都会附加这个过渡动画。还有一个playDynamicTransition也是播放过渡动画,只不过使用了一个sequence、delay、gate的小组合到达了一个循环调用也能间隔n秒才执行一次的效果。
总结下来ground states是对站姿和蹲姿的切换处理, 接下来看主要移动状态
grounded主要是开启足部ik,注意所有地面动作都需要开启足部ik,所以这里直接设置Enable_foot_ik_L/R = 1,来看看jump
从entry到jump left/right foot,依据曲线foot position判断着地脚【-1左脚 1右脚】,在jump left中,依据speed混合走路起跳和跑步起跳动画,这个jump play rate 也是依据起跳的speed进行映射的,并且在跳起时将标志bool jumped 设为1, 0.1s后设为0。后续jump loop和下落则是需要jump动画播放完毕即在空中1.6s还未落地才进行播放。
外层,先是设置倾斜,和之前一样,然后是根据fall speed下落速度设置着陆动画,这个速度是负值,越小速度越高,所以是clmap的-1000->-500,同时这个fall speed其实就是当前速度的z值,然后计算预着陆时间
这些混合单帧动画应该都是为了选择一组合适的曲线
主要是看后半段,根据射线检测和定义的曲线拿到0-1的预估值,从曲线中取值再去插值mask_landpredicition曲线,这个动画曲线前半截为1后半截为0,最后个人理解是前半截动画期间几乎不可能着陆,权重小,landprediction更可能返回0,后半截可能更会着陆,权重大返回1,从而混合着陆动画。
看看掉落fall状态,和jump相差不多,先是依据fall speed混合掉落速度和抽搐程度,再附加倾斜,最后依据预掉落权重混合着陆姿势。
同时注意,这里附加掉落的倾斜步态Lean Amount是判断在空中状态后重新计算的,和陆地的倾斜计算区别在于使用一个预制的曲线值Lean in Air Curve去平滑和反转一个跳跃动作 从上升到下落的倾斜状态。
->InAir到jump和fall状态的切换,先判断bool isjump == true 则是jump【规则权重为1】,否则再判断movement state = inair则是fall。在jump的状态切换中,都使用的是 惯性过渡,惯性混合是一种混合类型,但惯性化系统本身并不从源动画采样。相反,开始切换到新动画时,运动中固有的速度和加速度将被用于推进运动。 传统的过渡混合会同时计算源动画和目标动画的Transform,2者根据权重变化叠加成最后的结果,而惯性化则是在请求混合的时候记录当前动画的状态以及到目标动画的差值,后面直接用多项式计算过渡动画的Transform值,效率至少提升1倍
https://docs.unrealengine.com/4.27/zh-CN/AnimatingObjects/SkeletalMeshAnimation/StateMachines/TransitionRules/
Land 状态节点中也差不多,在->Land中判断当前人物已经着地,然后在静止状态下进入Land,在其中依据fall speed混合着陆动画,并打开足部ik,以及标记着地脚。
而在Land Movement中则是根据当前的动画apply additive 着地动画并设置足部ik
这个main movement state状态机的切换主要是依据movement state枚举去切换的
,也可以添加如fly,swim等状态在这个状态机中实现。由于前面使用了惯性化过渡,这里要使用惯性化结点
去启用。这个Main Movement States主要还是定义了地面动画和空中动画,以及他们之间的着陆和切换等。
接着来看一下overlayLayer,每个红框中的状态都大同小异,先来看看下半部分,这里的附加状态大部分结构一致,先决定步态,在决定站姿,再混合空中的姿态,最后附加idle动画并持续输出, 而且那些选取某一时刻的pose,感觉真正的意义在于选择恰当时刻的一组曲线值。
barrel较为简单,也是基于抱桶的姿态,决定步态、决定站姿,然后附加idle。
看看box,前半部分类似,这里根据overlay override state 覆盖层覆盖值去选取基础姿势输出,这个覆盖值是通过动画通知资源animnoitfy去设置的,1是低攀爬,2是翻滚,3是起身。这个地方Layering_Spine\Head目标是用来标记分层变色的。
看看binoculars,只是多了一个附加瞄准姿势的部分,这里通过将瞄准角度Aiming Angle 的y值从-90度到90度映射到0-1,变成AnimSweepTime值,从而标定瞄准动画的角度。我注意到了这里是使用Apply Mesh Space Additive,Additive动画中有选择是Local Space还是Mesh Space,一般是选择Local Space,即该骨骼的空间坐标受父骨骼影响, MeshSpace可以说就是所有骨骼的旋转值沿着骨骼树换算到模型空间后的空间,实际效果下图,所以一般瞄准资源都是使用的mesh space以保证绝对的瞄准方向而不受骨骼方向的影响。
Torch State与Binoculars差不多。
来看Rifle,Rifle Relaxed是持步枪放松下的状态,状态中主要是根据weight_gait曲线去选择持枪姿态、再选择站姿,附加idle动画。而Rifle Ready中是持枪准备姿态的简单混合,而Rifle Aiming则是和望远镜Binoculars类似,其中依据Aim Sweep Time值附加了垂直方向上的瞄准动作。整个状态机的循环主要是依据Rotation Mode去判断,当前旋转模式瞄准Aiming时,就进行瞄准偏移,取消后重新->Ready->Releaxed,而Ready->Releaxed过程中,需要Ready动作保持了3秒或者切换成了空中\冲刺状态
后续的Pistol H 和 Bow其实和Rifle都大同小异。总结下来OverlayLayer主要是针对各种额外姿势的附加。依旧是使用了Inertialization惯性化过渡节点去提高动画效果和性能。
来看看BasePoses层,就很简单,单纯输出当前的Idle姿态。接着再来看看LayerBlending
这里先将前面的三个大层输入缓存起来,生成局部空间的附加动画和mesh空间的附加动画,手部使用局部空间更好,可以更贴合脊椎引发的运动旋转。
根据 LayerBlending和项目提供的动画颜色分层功能_Add后缀是additive层和Layer层的权重区分,而其他Layering_曲线则是标定是使用基本动作层【0、-1】还是覆盖动画层【1】,默认情况下是1,在攀爬、起身动作下是-1,是在其动画资源中用曲线标定的,如下图这个攀爬动作蒙太奇中的曲线,在实际动作时是-1的曲线值.{不太懂为啥要-1不是0}
其实说到这想起前面的疑惑,我一直没弄清楚作者将许多帧组合在一起成为一个资源,然后基本pose从里面调某一帧是什么意思,然后发现比如Pistol1H,在攀爬状态下取第0.11s为基本pose,进入资源发现
Layering_Arm_R_Add值基本为0.1左右,Layering_Arm_L_Add为0.75左右,结合上面,这个值越高,则base additive层使用越多,右手持枪,所以右手部分需要独立出来,所以权重低,所以在2H双手持枪的资源中0.11s处,两个Add曲线值都基本接近0,因为双手都要受影响,更多的是使用覆盖层overlay。
继续说回LayerBlending,在为身体各个大部位设定完slot以及选择完播放的动画层后,使用layered blend per bone 进行混合,从而达到一个骨骼身体的不同位置可以分别播放自己的动画的效果。注意到这里手臂blend有使用LS值和MS值,Arm_L_LS值取自曲线Layering_Arm_L_LS值,Arm_L_MS则是1-floor(LS)【向下取整】,来看看这里使用MS和LS值进行per bone混合有何含义:LS和MS必定有一个为0一个为1,即必定是有时切换mesh space进行混合有时切换local space,以上文的Pistol1H资源为例看看什么适合切换,在overlay的pistol1h的瞄准aiming state中使用了0.14s的pose,在资源中此时Layering_Arm_L_LS值为0,则在per bone 混合时手部使用mesh space,这就清晰了:瞄准动作,手部要使用mesh space以排除父骨骼位置对动画的影响【前文有说】。所以pistol2H双手资源中,瞄准部分0.18s两手LS值均为0,其他瞄准动画亦是如此。
这里Hands格外覆盖了一层是防止持物时additive 动画层arm的动画影响到手指以及ik骨骼。
最后是混合不同层的曲线,将Curves槽覆盖到VB Curves虚拟骨骼上,以便于直接从基础层和overlay层取曲线值,以排除曲线间的相互影响【即取曲线值的合】。
总结下来LayerBlending层就是将前几部分的动画混合在一起并为身体不同部位进行分层。
至此分层混合LayerBlending部分看完了,呱唧呱唧~~~,接下来看瞄准偏移部分
这里如果旋转方式旋转VelocityDir则是在LookTowardsInput状态,否则就是在LookTowardsCamera状态,先看前者,Input、not input之间通过bool hasmovement input进行切换,无输入则头偏向正中间,斗则使用Input Yaw offset Time去标定正确的头朝向,而这个值是当前的加速度相对值【(当前速度-上一帧速度)/ detla time】【这样计算加速度是因为movement组件返回的加速度是input的加速度,并不能代表角色的实际物理加速度】的旋转值减去当前角色的旋转值从-180 到180 映射到0-1。
接着来看LookTowardsCameraStates
这里no offset是使用了摇头动画的混合空间转换成了单帧动画空间,normalized time 0.5代表头没有左右偏移,pitch = 0代表头没有上下偏移,看看looking forwards
这里依据yaw time去决定左右偏移,依据smoothed aiming angle 去决定上下偏移。smoothed aiming angle是根据smoothed aiming rotation【依据aiming rotation 插值计算,以排除角色旋转的影响,以及提供一个平滑的旋转】计算的,而yaw time决定左右偏移依据aiming angle的x值去判断,yaw time 0-0.5为左,0.5-1为右,根据图中三色的共享过渡规则和方向图,整个look朝向就比较清晰了感觉,和之前向前移动计算方法的方式差不多,但这个计算switch side blend pose ,当一次转动角度过大时,x值会有一个-180到180的跳转【这个rotation是相对值】,这时就判断换方向转身,所以蓝框内条件是相反的。{这个状态机唯一疑惑的点是白色的那四个规则,这四个规则就是单纯的同一条过渡线上的规则额外添加了一个state weight != 1的条件,但白色普通规则与颜色过渡共享规则之间是或的关系,这样白色规则又有什么意义呢,不是很清楚。。。}
同时需要注意的是,AimOffsetBehaviors的动画additive space都是mesh,以排除骨骼自身的旋转值影响,正确依据yaw、pitch值设置朝向
AimOffsetBehaviors看完了,这里是依据rotation mode和瞄准yaw、pitch值去正确输出扭头动画和设置朝向。接着来看大的 Apply Aim Offsets部分。
对上一步缓存的基本动作,依据enbale aim offset 值去附加AimOffsetBehaviors 扭头朝向动画。这个enbale aim offset通过曲线Mask_AimOffset去插值计算,以ALS_N_Mantle_1m_Montage为例【粉色】,在动作的中间段,mask值为1,即enable aim offset 为0,无法转头{不太懂为什么要lerp一下而不直接用曲线,,,感觉有点脱裤子放屁?}。后半段依据脊椎旋转值去旋转骨骼,然后根据enable_spineRotation曲线去进行混合。这里spine rotation是依据【Aiming Angle X / Numspine+pelvis bones】去计算的,以算出水平脊椎的扭曲量;而曲线Enable_SpineRotation则是在持物瞄准的动画中进行设置【黄色】,以ALS_Props_Piustol_1H为例,0.17s处为瞄准状态,这时曲线值为1,开启脊椎旋转,实验了一下如果瞄准不设置脊椎旋转,则动画会缓慢旋转,同时腰部不会扭动,完全无法满足实时瞄准的动画要求。
Apply Aim Offsets 看完了,这里主要设置了角色的扭头动画和朝向,同时设置了瞄准准备下的脊椎旋转,接下来终于准备看IK了!!!
手部ik,这里根据Enable_Hand_IK_ 去设置ik的开启和关闭,看了下这里IK的使用只有在持步枪和双手持枪下启用了,并且开启和关闭的时机没找到规律,不知道是什么意思。而且这里hand ik 是基于虚拟骨骼去做的,手持物体是attach到了vb上,而且左手关节会制约右手,反之则不会
足部ik。。。好复杂。。先来看看每一tick都调用的Update Foot IK函数,先是设定脚锁定Set Foot Locking,在其中更新Footlock的值【只有为1或者小于上一tick值,即只能混出或者锁定到新位置时才更新Foot Lock Alpha值】,并设置ik bone 的location、rotation设置为lock loc、rot,这里面有个SetFootLockOffsets设置足部偏移以确保脚步着地。这个foot lock系统也是一个重要的子系统,锁定一些情况下的脚步避免出现滑动和平移的现象【 当重心脚着地的一瞬间,重心脚的FootLock曲线值要设置为1 】,通过将角色的脚部transform 减去 角色Capsule 的transform,从而lock住脚的位置。红框处是计算两帧之间角色的世界坐标系下的偏移距离,然后unrotate转换到局部空间内。详见大神 [玩转UE4/UE5动画系统>应用篇>功能模块] 之 Foot Lock系统(ALS V4实现方案详解)_开发游戏的老王-CSDN博客。
接着更新非空中模式下足部的ik偏移
结合Set Foot Offsets中函数名做的注解,同时注意脚步旋转只根据红黄两轴进行旋转
这里,Set Foot IK 的 Step2 插值是为了防止高度落差过大出现瞬移。计算完Foot Offset 后计算Pelvis IKOffset,盆骨偏移值计算依赖Enable_foot_L / R,当两条曲线的平均值大于0才进行计算,取左右脚ik偏移中z值大的为盆骨偏移,最后进行插值。
event graph中计算出offset后将其应用到VB IK_foot_l/r_Offset上,这是effector,再设置盆骨位置,再设置knee target ik极目标指定ik过程中膝盖的弯曲方向,这是joint target,最后使用two bones ik设置即可,这里参考了大神的ik解析,详见 [玩转UE4/UE5动画系统>应用篇>功能模块] 之 Foot IK系统(ALS V4实现方案详解) - 知乎
最后这里Ragdoll Override 简单的混合了一下布娃娃系统的默认动画【以physical linear speed为play rate】,这里使用了Pose SnapShot姿势快照去混出。这里布娃娃系统开启后需要设置角色胶囊体和骨骼位置一致,SetActorLocationDuring RagDoll 计算过程图解如上,从而保证胶囊体一直触地维持一个高度,同时这里+2的offset是因为mesh组件在characterbp中相对位置是capsule half height + 2。
注意角色初始化前几帧 Delta time x 可能为0
动画蓝图终于大概看完了,呱唧呱唧,接下来看看 角色蓝图 剩余的部分。
先看角色蓝图基类ALS_Base_CharacterBP,在begin play中调用了 Add Tick Prerequisite Actor,以确保角色bp在mesh和animInstance之前更新以使其获取最近的值。接着读取了一组预制的数据,给定角色的各个运动速度和曲线,然后对各个状态flag和rotation值进行重置。
在角色着陆时依据是否有input去设置了movement comp的Braking Friction Factor 制动摩擦系数,以防止着陆时打滑和翻滚。{不过这个Breakfall状态不是很清楚什么意思}
攀爬系统实现: [玩转UE4/UE5动画系统>应用篇>功能模块] 之 攀爬系统(二)基于原地运动的攀爬系统(ALS V4实现方案详解)_开发游戏的老王-CSDN博客 c++ [玩转UE4/UE5动画系统>C++篇>功能模块] 之 C++版攀爬系统(附项目代码)_开发游戏的老王-CSDN博客
角色依据movement组件去判断是否在空中
动画部分最后看看没看的Notify
MovementAction_NotifyState 、OverlayOverride_NotifyState、GroundedEntryState_AnimNotify 三个前面已经看过了,主要就是在动画中通知相关状态的改变。
看看EarlyBlendOut,就和名字一样,依据角色的Movement State 和 Standce 以及Movement Input 去判断是否将当前的蒙太奇动画直接混出,让动画变得更加自然【个人理解】
CameraShake_Notify,在动画某些位置调用镜头晃动。
Footstep_AnimNotify,在notify时播放脚步声。
相机系统
来看看ALS的相机系统,主要由动画bp ALS_AnimBP 和 PlayerCameraManager BP 的 ALS_PlayerCameraManager 组成。
先看ALS_AnimBP,MainCameraStates中定义了三种view mode下的参数,状态之间的切换单纯依据Rotation Mode 去判断,内容也大同小异,只是具体参数值不同,以velocity Direction 为例,其中设置了相机位置的偏移和中心点移动速度,根据步态Gait去具体选择参数,接着设定中心点的z值偏移,站立下是50,蹲姿下是30,蹲下中心点向下偏移,合理。最后设置该种选择方式下的镜头旋转速度Rotation Lag Speed用以控制相机旋转的插值速度,然后输出Pose并将其保存为Main Camera States。
接着设置相机Y轴偏移,这个地方就是设置相机放在左肩还是右肩,注意这里modify Curve是scale模式,在原值上直接乘以1或-1进行Y轴上偏移量的反转。接着在Movement Action Override中依据当前角色动作覆盖重写中心点移动速度,在First Persion中,假如当前相机模式是第一人称,就将Weight First Person曲线设为1以供蓝图使用,Ragdoll Override 则是在布娃娃状态下的参数覆盖设置,最后依据debug标志位去设置Override Debug曲线的值,然后输出最终pose。这个动画蓝图无任何具体动画姿势输出,但是使用动画曲线的方式去设置和转换一堆复杂的参数,感觉很巧妙。
而ALS_PlayerCameraManager中最关键的就是Custom Camera Behavior函数,定义了相机每次更新的位置、旋转、FOV。
第一步:借助蓝图接口拿到相机的参数,具体接口在ALS_Base_CharacterBP中实现,具体参数含义如图,TPFOV\FPFOV为提前定义好的90。
第二步:根据RotationLagSpeed曲线的值去插值控制器与相机的旋转,假如当前开启了debug模式,那么Overrid_Debug曲线=1,使用提前定义好的Debug View Rotation【这个旋转值是看向角色】 为目标旋转去插值,最后设置相机目标旋转。
第三步:计算平滑中心目标Transf,CalculateAxisIndependentLag只是简单的依据动画蓝图中设置的中心点三轴移动速度去进行 当前中心目标spt 与 中心目标pt 的插值,最后重新设置当前平滑中心目标spt位置
第四步:根据spt去计算中心点位置pl,单纯在spt上附加曲线值中设置的三轴上的偏移得到,一般xy无偏移,只有z轴上有偏移,所以这个目标点位置pl一般是在spt正上方。pt 、spt、pl关系如上
同时这里额外计算pl而不直接使用spt是为了允许实现更多精细的操作。
第五步:为当前中心点位置pl附加上曲线中三轴的偏移量,然后进行debug模式判断,最后插值设置目标摄像机位置。简而言之相机位置就是依据中心点位置pl加上一定偏移进行设置的。debug模式下只是单纯的将目标摄像机位置设置在了角色中心点的正前方以使得相机能够从正面看见角色动作。注意相机正中间观察的永远是Pivot Location [pl],而中心目标pt则可能发生偏移,spt 去追赶pt,pl根据spt去计算从而实现一种摄像机滞后追赶角色的镜头感【个人理解】
第六步:进行角色与相机位置之间的射线检测【注意这个次序很重要,是角色到相机】,模拟弹簧臂spring arm功能。当检测到碰撞时,将相机放在碰撞点前Radius距离的地方从而避开障碍物,但是这里Location - Trace End + Target Camera Location 很奇怪,经测试Trace End 和 TCL值是相等的必然,最后直接使用Location 作为Target Camera Location也是正确的,很奇怪为什么这么写。
第七步:画DebugSphere,没什么好说的
第八步:依据Weight_FirstPerson曲线,Override_Debug曲线去判断是否是第一人称,或者是debug模式,输出选择的值。
相机系统就这样差不多了。
Animation Modify
最后来看看项目中用来定义动画曲线的Modify
Calculate_RotationAmount,用来计算曲线RotationAmount的值,RotationAmount是动画中Root骨骼每一帧之间的旋转量差值。前面部分是简单的去重,如果已有此曲线就删除,添加曲线,略过。图中遍历了动画的每一帧,计算此帧与下一帧的旋转量差值,如果Rate是负值,则是此帧与上一帧的差值,最后将差值绝对值乘以Rate设为该帧的曲线浮点值。{不知道为何要调用FinalizeBoneAnimation,去掉未见影响}
Copy_Curves,用来复制特定动画中的某些指定的曲线值,比较简单,就遍历source曲线将每一time的值复制到target曲线。
Create_Curves,用来为动画创建曲线,可以指定为每帧时间创建一个默认值,或者是选择某几帧创建特定的值
Create_LayeringCurves,与上一个Create_Curves差不多,只不过创建的是Layer_的曲线
呱唧呱唧,终于看完了~~~