找回密码
 立即注册
首页 业界区 业界 Lyra动画系统学习

Lyra动画系统学习

顶豌 7 天前
01 Animation BluePrints

几种播放动画的方法:

  • Sequence Player -> Output Pose

  • 变量传入Sequence Player -> Output Pose


02 Blueprint communication Intermediate

蓝图之间的三种通信方式

Casting(投射)
Interfaces(接口)
Property Access(属性访问)
Blueprint casting

初始化时,Pawn -> Cast to 目标Character -> 拿到移动组件的引用

Update每帧从移动组件里读数据

Blueprint Interfaces

新建一个动画蓝图接口:


在角色动画蓝图中,继承接口:


一个单纯的动画事件、一个有传参的方法


角色蓝图:

Property Access

角色动画蓝图中:
先新建一个线程安全的Update,

然后新建一个函数,

返回节点要勾选Pure(全程只读取数据、不修改任何内容)

回到线程安全的Update,调用新建函数

03 Idle Animations Pro

Input System




先实现数字键123对应打印字符123:

1.数字键2和3加上输入Modifiers修饰器(修改原始输入数据)——Scalar(标量:相当于给输入值乘一个系数)

2.角色蓝图中,初始事件中调用AddMappingContext传入InputMapping,增强输入系统事件打印InputActions的值(按下瞬间触发)

EnhancedInputAction(增强输入动作)的事件参数:

  • Triggered:按住按键时调用的事件。
  • Started:按下按键的瞬间调用的事件。
  • Ongoing:输入动作处于持续激活状态中的事件(类似 Triggered的持续反馈)。
  • Canceled:输入动作被 中断 / 取消时 (比如按了一半突然松开按键)调用的事件。
  • Completed:输入动作 完成触发后 (比如按键按下后完全松开)调用的事件。
  • Action Value:输入动作对应的 具体数值 (比如摇杆的 X/Y 轴值、扳机键的按压程度),是最常用的参数之一。
  • Elapsed Seconds:输入动作从 “开始” 到当前的 已持续时间 (单位:秒)。
  • Triggered Seconds:输入动作处于 “触发状态” 的 累计时间 (可能包含多次触发的总和)。
  • Input Action触发的 输入动作本身的引用 (可以通过它获取动作的配置信息)。
在Game中就实现了:

其他Modifiers:

  • None :不使用任何修饰器,直接传递原始输入值。
  • Dead Zone(死区) :忽略手柄摇杆 / 扳机键的微小输入(比如摇杆漂移),低于阈值的输入会被视为 0。
  • FOV Scaling(视场缩放) :根据当前相机的 FOV(视场角)调整输入值的大小。
  • Negate(否定) :反转输入值(比如将 “1” 变成 “-1”),常用于反转轴方向(如相机 Y 轴)。
  • Response Curve - Exponential(响应曲线 - 指数) :给输入值套指数曲线,让小幅度输入更细腻、大幅度输入更灵敏(适合瞄准类操作)。
  • Response Curve - User Defined(响应曲线 - 自定义) :用用户自己绘制的曲线来调整输入响应。
  • Scalar(标量) :给输入值乘以一个系数,用于调整灵敏度(比如把移动速度放大 2 倍)。
  • Scale By Delta Time(按 DeltaTime 缩放) :输入值乘以每帧的时间间隔,让输入效果不受帧率影响。
  • Smooth(平滑) :对多帧输入做平滑处理,减少输入抖动。
  • Swizzle Input Axis Values(交换输入轴值) :交换输入的坐标轴顺序(比如把 X 轴输入映射到 Y 轴)。
  • To World Space(转世界空间) :将本地空间的输入(比如角色自身坐标系)转换为世界
如何传递参数到动画蓝图中

1.创建一个枚举类型

2.角色蓝图新建该枚举类型的变量


3.用switch on int根据输入的123转换为三个枚举变量,打印

Truncate(截断节点):
把 Action Value可能出现的浮点数转为整数 (比如输入值是 “1.2” 会被截断成 “1”)
效果:

4.枚举类型EquippedGun —>动画蓝图
AnimationBluePrintsInterface中新建一个方法,传入参数为该枚举类型:

动画事件蓝图中定义该方法,

角色蓝图中通过Mesh引一个动画实例传入该方法,

这样EnhancedInputSystem事件中输入的参数就传入了动画蓝图,与前面角色蓝图中直接传递参数的效果一致

解决相机显示问题:
角色组件中,

动画状态机

和unity Animator很像,但因为有事件系统,其实更像是animancer的可视化
动画蓝图中新建一个状态机,

构建跳转关系,

每个State绑定对应的动画资产,

勾选上循环动画,

跳转条件,

把跳转条件promote为shared transition,

效果:

Blend Poses 姿势混合

Blend Poses by Int


Blend Poses by Enum(最常用)


Dynamic sequence with blend inertialization



带 Inertial Blending 的 Set Sequence 方法:需要有Inertialization才能正常运作
Inertialization(惯性混合节点):
让 “旧待机动画的动量” 在 0.2 秒内衰减,平滑过渡到新的待机动画。
04 Linked Animations Pro

AnimationLayer



IdleLayer中,

AnimationLayer Interfaces

用接口的方式添加AnimationLayer
1.新建AnimationLayer Interfaces——ALI_Lyra



2.在ABP_Base的ClassSettings中添加接口


IdleLayer和上面一样设置好之后加到动画蓝图中即可


Link Anim Class

1.新建一个AnimationBlueprints——ABP_Layers

继承接口ALI_Lyra

在IdleLayer中,新建一个变量IdleAnim(这个会在后面的子ABP中更改值),传入SequencePlayer

在角色事件蓝图中 Link Anim Class Layers,Class设置为ABP_UnarmedLayers

这样就连接到了ABP_Layers
2.创建ABP_Layers的子ABP


设置好各自的默认值



角色事件蓝图中,Link Anim Class Layers到各个子ABP_Layer

ABP_Layers中,


ABP_Layers只用来管理各种Layer,不做更多的事,惯性节点在ABP_Base中添加

总结

ABP_Base 和ABP_Layers继承自 动画层接口ALI_Lyra(规定了各种Layer)
IdleLayer在ABP_Layers中的Idle On Update实现
ABP_Layers的Idle On Update中的变量IdleAnim在每个子ABP_Layer中更改默认值
IdleLayer在ABP_Base中传入(考虑惯性节点)
05 Organizing our work Beginner

整理蓝图
06 Character movement Beginner

角色移动
1.视角的移动

新建IA_Look


在IMC_ALS中加进来

角色时间蓝图中

SpringArm中勾上使用 Pawn 控制旋转

解决一下竖直方向视角移动反向的问题

2.人物的移动

同样的做法

把轴向调正确

角色事件蓝图中


为什么左右移动只连xz,前后移动只连z?
07 Cycle Animation Part01 Intermediate

1.动画蓝图中新建一个LocomotionSM,将会包含Idle、Move等等




2.在线程安全UpdateAnimation函数中,新建一个函数GetVelocityData并调用



注意GetVelocityData函数要勾上线程安全

3.LocomotionSM状态机



记得勾选Loop

跳转条件


动画资产要启用Root Motion和Force Root Lock

效果

4.Aim

Aim input


创建E_Gate枚举类型,包含走路(瞄准时用到)和慢跑(不瞄准时用到)两个状态



在角色事件蓝图中新建E_Gate枚举变量CurrentGate

创建S_GateSettings结构体

六个参数:
最大走路速度
最大加速度
制动减速度
制动制动摩擦力因素
制动摩擦力
使用单独的制动摩擦力

角色事件蓝图中新建一个Key为E_Gate枚举类型,Value为S_GateSettings结构体类型的变量


新建函数UpdateGate(E_Gate Gate)



瞄准Started时更新当前Gate为Walking,Completed时为Jogging

效果:

这里只是根据状态不同更新Character Movement的速度
在动画蓝图中根据当前的状态切换对应动画

在动画蓝图接口BPI_AnimationBlueprintsInterface中新建一个函数RecieveCurrentGate(E_Gate Gate)


动画事件蓝图中定义这个函数是用来设置CurrentGate的

传参:角色事件蓝图中的UpdateGate()中把Set的CurrentGate值通过Anim Instance传入动画蓝图中的事件函数RecieveCurrentGate()

下面只需要在ABP_Base的LocomotionSM中设置好动画资产即可
在ABP_Base中的Cycle中姿势混合(with E_gate),传入参数为CurrentGate


把这个姿势混合逻辑放到ABP_Layers中

ALI_Lyra中加一个CycleLayer

把Cycle(State)的逻辑放到ABP_Layers的CycleLayer中

这里的CurrentGate需要在ABP_Layers新建一个函数GetABPBase(Pure),来获取ABP_Base中的函数和变量

在ABP_Layers的CycleLayer中就可以获取到CurrentGate这个参数了

和IdleOnUpdate一样,逻辑封装到CycleOnUpdate中,然后CycleAnim变量在每个子ABP_Layer中设置对应动画资产


注意:这些Anim变量都不用设置默认值
我们应该在角色事件蓝图的初始化中设置Anim Class为ABP_UnarmedLayer:

各子ABP_Layer的资产设置:
以ABP_Pistol为例,

如何批量设置动画资产的RootMotion:



效果:

08 Debug Options Intermediate

调试
DebugPrint





效果:

DebugDrawVector

1.需要在角色脚下画箭头就需要先获取到角色的世界坐标



勾上线程安全后连到:

2.Debug Draw Vector

线的起始和最终位置只需要xy两个维度



效果:

建立一个Debug系统管理上面的Debug方法

新建一个结构体S_DebugOptions


ABP_Base中新建该结构体变量

以及函数Debug,把之前事件蓝图中的Debug方法移进来,用DebugOptions结构体变量做if判断执行对应的Debug方法


回到事件蓝图中连上Debug即可

Debug慢动作模式

角色事件蓝图中用Set Global Time Dilation:时间膨胀系数越小越慢

09 Cycle animation Part02 Pro

先获取RotationData



更新当前朝向(2d,只有xy)



在Debug中显示当前朝向

前后左右分别是:

下面就可以根据这个朝向设置不同的转向动画资产
新建一个移动方向的枚举类型



然后在ABP_Base中创建该枚举变量

计算移动方向

后:

前:

更新朝向的时候计算移动方向

Debug显示出来

左右方向的计算同理:

模块化该函数

保持UpdateOrientationData函数的单一功能

把计算移动方向放到外面来

效果:

方向死区Direction DeadZone

转向滞后:
如果只是小幅度变化(在死区范围内),动画不改变
增加Input参数——CurrentDirection ,  DeadZone


各方向的死区计算


前后方向的死区就是往直角坐标系的水平方向靠,左右方向的死区就是往竖直方向靠
前:[前min - 死区, 前MAX + 死区]                        比如原来的前:[-50,50]                                        死区20的前:[-70,70]

后:[后min + 死区, 后MAX - 死区]                        比如原来的后:[-∞,-130]∪[130,∞]                死区20的后:[-∞,-110]∪[110,∞]

左:[后min - 死区, 前min + 死区]                        比如原来的左:[130,-50]                                死区20的前:[-150,-30]

右:[前MAX - 死区, 后MAX + 死区]                        比如原来的右:[50,130]                                死区20的右:[30,150]

用Sequence分离 原逻辑和死区逻辑


这里我传错了一个参数

应该是CurrentDirection

整理一下

Select Animations with structures

根据得出的移动方向,配置动画资产
1.新建一个结构体变量S_DirectionalAnimations



2.在ABP_Layers中创建该结构体变量传入Cycle On Update



在各个子ABP_Layers中配置资产

以Pistol为例:

效果:

修复滑步现象——Stride Warping

姿势适配:动态调整角色的动画步幅来匹配胶囊体移动速度
通过前面做的慢放功能可以看出移动的时候存在滑步,因此引入姿势适配Stride Warping

在CycleLayer中加一个Stride Warping节点

Stride Warping节点的配置

效果:

修复45°左右的动画融合问题——Orientation Warping

朝向适配
在Stride Warping节点中加一个Orientation Warping节点

Orientation Warping的配置:

效果:

慢放:

现在各种角度的移动动画能正常融合了,但因为缺少起步和停步的动画,起步和停步的时候还是会滑步,下面加入起步和停步的逻辑
10 Lean animations Intermediate

初探起步倾斜动画
导入资产

导入步枪的倾斜姿势:

三个姿势都设置为局部空间叠加(Local Space),基准姿势是Center

创建混合空间

创建一个混合空间(BlendSpace)——BS_Lean

x轴为倾斜角度,y轴对应不同的权重大小

混合树中设置好资产:

回到Axis Settings,把平滑过渡时间Smooth设置为0.5

按住ctrl在混合树中预览混合效果:

在动画蓝图中应用

ABP_Layers中的CycleLayer,在warping之前应用叠加(Apply Additive)

BS_Lean混合空间的Gate权重设置好,还需要角色自身的偏航角作为倾斜角度。
因此回到ABP_Base的GetRotationData方法,原先得到的WorldRotation是世界旋转角,Break出一个偏航角Yaw,前一步新建一个变量为LastFrameActorYaw(上一帧的偏航角)。

现在得到了上一帧的偏航角和当前的偏航角
相减得到DeltaYaw

再除以DeltaTime,适当放缩后收敛到±90°区间内,得到基于DeltaTime的倾斜角LeanAngle

其中DeltaTime是GetRotationData的输入变量

然后传入ABP_Layers的CycleLayer的BS_Lean

修复后退时的倾斜角度会反向的问题:

根据移动方向的不同,乘以±1

效果:

下面开始起步和停步动画
11 Stop Animations Pro

导入资产

导入动画
新建动画压缩曲线CurveCompression


批量设置所有Stop动画为该曲线,后面“距离匹配”中会用到

停下动画的触发时机应该由加速度来决定,因此下面先获取角色的加速度
获取角色的加速度

新建函数GetAccelerationData



放到安全线程中

debug出这个加速度2D:

效果:

ABP_Base状态机中新建Stop状态以及跳转条件




ALI_Lyra接口下加一个Stop Layer


为什么用Sequence Evaluator?

如果直接用Play Sequence播放Stop动画,会发现由于开启Root Motion导致Stop动画的前几帧会有一段位移,而Sequence Evaluator可以指定动画资产在特定帧开始播放,也就是Explicit Time这个参数:

之后就可以用距离匹配只匹配一些细微的位移变化
什么是距离匹配?

在S_DebugOptions中添加ShowDistanceMatching

添加一个插件

回到ABP_Base的Debug,加一个分支情况


效果:

回到ABP_Layers的StopLayer添加Sequence Evaluator

播放停止动画只需要在切到Stop状态才会调用,因此On Become Relevant
On Become Relevant:当该对象 / 节点 从 “无关状态” 变为 “相关状态” 时触发


Sequence的逻辑和之前Cycle一样:

在子ABP_Layer中配置动画资产即可
以PistoLayer为例:

设置Sequence Evaluator的Explicit Time参数


让Stop动画逐帧更新播放



在逐帧更新Stop动画的函数UpdateStopAnim()中应用距离匹配


停下的距离StopDistance > 0,就应用距离匹配,否则就是已经停下了只需要按固定速度播放

注意到距离匹配还需要距离曲线

因此需要在动画资产中用AnimationModifier新建一个距离曲线修改器

我们可以进行批量操作:

回到ABP_Layers的UpdateStopAnim,距离曲线名字设置为Distance

效果:

按角度停下Stop At Angle

直接从CycleLayer中复制过来这个节点即可

12 Start Animations Pro

起步动画
导入Start动画,开启根运动,设置曲线压缩为CC_UniformIndexable

记得最后在CC_UniformIndexable中点击应用压缩(这会应用到所有引用它的设置)

在ABP_Base新建Start状态及其跳转条件


Idle -> Start: 正在加速

Start -> Cycle:角色速度到达最大速度

Start->Stop: 不在加速(和Cycle->Stop一样)
Stop->Start:在加速(和Idle->Start一样)
Start -> Cycle的新加一个条件:前后帧的速度移动方向改变

需要计算的参数

获取上一帧的速度移动方向,并判断前后帧速度移动方向是否改变


新加跳转条件

Start -> Cycle的新加一个条件:前后帧的速度移动方向改变
也就是如果Start动画期间速度没有到达最大值,改变移动方向也会跳转到Cycle

Start -> Cycle的新加一个条件:前后帧的Gate改变

需要计算的参数

把原来RecieveCurrentGate设置的变量换成IncomingGate

新建一个安全线程的函数GetCharacterState

获取上一帧的Gate,当前的Gate,前后帧的Gate是否改变


新加跳转条件

Start -> Cycle的新加一个条件:前后帧的Gate改变

同一帧应当只允许一次状态切换

因为上面跳转条件中有的参数是基于前后帧某个值是否改变

用StartLayer来管理动画资产

操作和StopLayer中一样
ABP_Lyra接口中新建StartLayer




在各子ABP_Layer中配置Start的动画资产
以ABP_PistoLayer为例:

让Start动画逐帧更新播放(从这里开始会和StopLayer的有些不同)



这里会出现所有方向的起步动画都更新的是前进的起步动画:

在UpdateStartAnim()中确保要更新的动画是正确的起步动画

比较SequenceEvaluator中的值和设定的值,一样就Advance Time播放,不一样就设置为一样的

效果:

在逐帧更新Start动画的函数UpdateStartAnim()中应用距离匹配

Advance Time(普通推进时间)
Advance Time by Distance Matching(距离匹配推进时间)

获得上一帧的角色位置和DeltaLocation

回到ABP_Base的GetLocationData方法

DeltaLocation

回到ABP_Layers的UpdateStartAnims()

为Start动画批量添加距离匹配修改器(和Stop动画的操作一样)


回到ABP_Layers的UpdateStartAnims()

Blend Logic

动画过渡时候的混合逻辑
Start->Cycle过渡的混合逻辑全部改为惯性(Inertialization),混合时间改为标准的0.25

这会改善两个动画切换时因为迈出脚不一样导致的停顿,但是还是会有不流畅的感觉,最好还是要保证动画资产的连贯性
Warping的设置

直接从CycleLayer复制过来

倾斜

方向适配、步幅适配

步幅适配加上插值混合,解决滑步问题

如果出现Warping不起作用,可以看是不是忘记取消勾选Teleport to Explicit Time:
由于Stop和Start用的都是Sequence Evaluator,和Sequence Player不一样,需要取消勾选Teleport to Explicit Time
并且由于都是短动画不需要循环,取消勾选 Loop

效果:

13 Pivot animations Pro

移动时急转
导入动画资产,设置为根运动、应用曲线压缩

PivotState

Alias
相当于把那些要跳转到同一个State的State集合到一个Alias,然后只需要让这个Alias跳转到目标State即可


PivotAlias -> Pivot

如果是180度急转:移动速度与加速度反向,也就是二者的单位向量的点积为-1,也就是cos180°

其他角度的急转都是钝角,也就是点积Stop

直接应用stop的共享条件
Pivot->Cycle

跳转条件1

进入急转状态时,需要保存当前的加速度
在PivotState的输出动画姿势上加一个回调函数SetupPivotState,这个函数在当前节点激活时调用

获取 急转状态时候的加速度

Pivot->Cycle的跳转条件1:角色速度 与 急转后的加速度 向量不平行

跳转条件2

新建一个蓝图类AnimNotify


Pivot->Cycle的跳转条件2:当前的Pivot动画播放完了

因此要在每个Pivot动画资产的末尾加上这个动画通知ANS_Exit_Pivot

PivotLayer

ALI_lyra新建一个PivotLayer

ABP_Base的PivotState连上PivotLayer

PivotStateMachine

回到ABP_Layer的PivotLayer
由于急转存在一个方向的急转与另一个方向的急转之间也会存在跳转关系,比如:
玩家会在转向没结束的时候转到另一个方向,我们需要一个状态机来处理这种到处乱转的情况


跳转条件

A与B的来回切换条件应该是相同的:
<ol>速度与加速度反向,也就是单位点积TurnInPlaceEntry

跳转条件:RootYawOffset超过一个阈值


从RootYawOffset的变化可以看出,如果RootYawOffset>50左转,0左转,TurnInPlaceRecovery

跳转条件:动画曲线IsTurning的值为0
前面在转身动画资产中的曲线IsTurning由1跳变为0,0的时候就是转身完成,也就是进入TurnInPlaceRecovery

注意要回到IdleSM把每帧最大跳转数改为1

并且这个跳转不需要过渡,因为播放的是同一个动画的不同阶段

下面设置TurnInPlaceEntry的动画播放器
TurnInPlaceRecovery State


前面UpdateTurnInPlaceEntryAnims()中设置的变量TurnInPlaceTime反映在这个曲线中就是会一直自增,在跳变到0的时候会积累到一个值,这个值我们在TurnInPlaceRecovery State中将作为动画播放的长度?

动画播放的时间解决了,选择的动画也要在TurnInPlaceRecovery State中调用
因此UpdateTurnInPlaceEntryAnims()中把最终选择的转身动画存进一个单独的变量FinalTurnInPlaceAnim中

回到TurnInPlaceRecovery State

让角色的转向同步转身动画曲线

这个转向同步逻辑可以写在ABP_Base的Idle State的UpdateIdleState()中,
新建函数ProcessTurnYawCurve()

逻辑是:
如果此时曲线接近0,说明已经转身结束,那就重置这两个帧的值
反之,正在转身过程中,我们就保存每一帧曲线关键帧的值,然后算出前后帧的曲线值差,最后RootYawOffset -=前后帧的曲线值差从而更新角色实际转向
每一帧曲线关键帧的值 = root_rotation_Z曲线值




SafeDivide:在执行除法前先判断除数,若除数为 0,会直接返回0
TurnInPlaceRecovery -> Idle

TurnInPlaceRecovery播放完自动跳转到Idle即可
下面这种方法会存在bug,我们不用

可以手动获取当前动画剩余时间==0的时候跳转即可

TurnInPlaceRecovery -> TurnInPlaceEntry

直接套用idle->TurnInPlaceEntry的跳转条件即可,并且这里和TurnInPlaceEntry-> TurnInPlaceRecovery一样由于播放的是同一个动画,不需要混合

最终效果:

转身的时候还存在缺陷,180度转身时,有的时候会播放两段90度的转身
貌似是因为相机视角的原因,后面做相机部分的时候再考虑修不修?(已经在Couch时的TurnInPlace章节解决了)
15 Crouch gate Intermediate

蹲伏
导入动画资产,RootMotion

Gate

在E_Gate中新加一个Gate——Crouching

角色蓝图BP_LyraCharacter中GateSettings

后面我们只需要进到每一个Layer中为对应的SelectAnims函数增加对应的Anims变量即可
Input

创建输入IA_Crouch,值类型为bool
在IMC_ALS中加进来

回到角色事件蓝图,添加input事件
由于蹲伏的时候还需要改变胶囊碰撞体的高度,因此需要向Character发出通知“Crouch / UnCrouch”

去characterMovement组件上打开Can Crouch

蹲下后的碰撞体高度值修改为合适的值



Idle蹲伏状态区分

蹲伏动画一般会分为Crouch和UnCrouch,我们需要根据蹲伏状态判断来决定播放哪个动画,通过判断前后帧的IsCrouching是否相等来决定播放Crouch还是UnCrouch
比如:
上一帧没蹲,这一帧蹲了,那就播放蹲下
如果上一帧蹲,这一帧没蹲,那就播放起身
回到ABP_Base的GetCharacterState()编写逻辑

之前的Idle Anim只包含Idle站立的动画,需要做个区分:Idle Stand和Idle Crouch(蹲伏又分为Crouch 和 UnCrouch)

因此,
IsCrouching用来区分Idle Stand和Idle Crouch,以及CrouchEntry和CrouchExit
CrouchStateChanged用来判断是否从Idle过渡到Crouch
Idle State

IsCrouching用来区分Idle Stand和Idle Crouch


在各个子ABP_Layer中设置CrouchIdle动画资产

效果:

StanceTransition State

IsCrouching用来区分CrouchEntry 和 CrouchExit
把原来的Idle State用状态机再次拆分




新建两个动画序列变量CrouchEntryAnim 和 CrouchExitAnim

SetupStanceTransitionAnim()



在子ABP_Layer中配置动画资产
以ABP_PistolLayer为例:


Idle-> StanceTransition

CrouchStateChanged用来判断是否从Idle过渡到Crouch

StanceTransition -> Idle

跳转条件1:和Idle-> StanceTransition一致
跳转条件2:StanceTransition动画播完(注意这里要区分规则的优先级,自动完成播放恢复Idle优先级要比状态改变要低)


StartLayer加入Crouch

动画资产处理:
Crouch Start设置曲线压缩和距离曲线修改器
设置身体侧倾的Crouching权重

SelectStartAnims()


CycleLayer加入Crouch

动画资产处理:
只需要确保Crouch Walk 开启了RootMotion



发现蹲伏的速度和我们实际设定好的速度不一致,是因为CharacterMovement在进入蹲伏函数Crouch()时,会有自己的最大移动速度,这个最大移动速度和我们所选择的不一致,因此我们需要将其调整回来

或者因为我们只需要Crouch()/UnCrouch的设置胶囊碰撞体高度这一个功能,所以其实我们可以不用这个CanCrouch,自己设置Crouch时的胶囊体高度也可以
StopLayer加入Crouch

动画资产处理:
Crouch Stop设置曲线压缩和距离曲线修改器
注意,如果动画出现跳帧的情况,勾上距离曲线修改器的Stop at End,这样就不会在末尾帧打上曲线




PivotLayer加入Crouch

动画资产处理:
Crouch Pivot设置曲线压缩和距离曲线修改器



注意:Pivot急转动画资产前后左右命名是相反的

回顾之前Pivot的对动画资产的处理,还需要在动画末尾加上回调通知notify,通知动画已经播完

Crouch时的TurnInPlace

蹲伏状态的原地转身
把原来选择原地转身动画的函数封装一下

根据CurrentGate选择要传入SelectTurnAnims()的转身动画资产

其中,把转身动画序列用结构体S_TurnInPlaceAnimations封装起来


在SelectTurnAnims()中按原先逻辑break出对应动画序列传入即可


然后我们需要和前面站立时候一样对动画资产进行处理——MotionExtractorModifier动作提取修改器、补偿曲线
这部分不展示了,可以回去看14 Turn In Place Pro章节
修复之前转身时就已经发现的Bug——180度转身时,有的时候会播放两段90度的转身


最终Crouch效果:

16 Jump Animations Pro

跳跃
导入动画资产,Root Motion,压缩曲线

输入事件

新建IA_Jump,bool类型,两个Trigger(按下和松开,后面会用来区分短按和长按的起跳加速度)


添加到IMC_ALS中

调用CharacterMovement的函数Jump和StopJumpping

设置CharacterMovement.Jump相关参数

构建起跳阶段的状态机

Jump Start ->Jump Start Loop->JumpApex
JumpApex
分析动画资产:Jump Start ->Jump Start Loop->JumpApex->JumpFall_Loop->JumpFall_Land->JumpRecoveryAdditive
有主动跳跃和被动跳跃两种情况:
1.主动跳跃:完整的起跳落地:起跳-起跳循环-下落
2.被动跳跃:当从边沿到空中且无起跳输入时,只有落地
因此,Jump的起跳阶段状态机有两种入口——JumpStart和JumpApex

Conduit(管道)
可用于创建1对多、多对1或多对多的跃迁
最常用的是用它来分散状态机的入口点,相当于一个单向的中继站
跳转条件

JumpAlias->JumpSelector以及JumpSelector本身的规则先都设置为真(后面会修改)


跳转条件参数的计算

IsOnAir

IsJumping和IsFalling


测试一下两种Jump入口是否都能正常进入

跳转正常
JumpStart->JumpLoop

跳转条件:动画播完自动跳转
JumpLoop->JumpApex

跳转条件:角色在重力加速度的情况下到达最高点所需要的时间JumpFallLoop->JumpFallRecovery

JumpApex->JumpFallLoop


JumpFallLoopLayer


JumpFallLandLayer

JumpFallLoop->JumpFallLand:需要实时计算地面的距离匹配,所以JumpFallLandLayer用SequenceEvaluator播放


JumpFallLoop->JumpFallLand

采用向下的球体追踪,本质上就是实时向下发射(球型范围的)射线,从射线碰撞(若能碰撞到)返回的信息与角色的当前的位置相减得到距离
既然是向下发射检测射线,起点肯定是脚部,那么就先要获取角色脚部的z轴位置
获取角色脚部的z轴位置

角色位置 - (0,0,胶囊体高度的一半)

射线检测脚部距离地面高度



击中地面的时候返把击中距离Distance传给动画蓝图

怎么在角色蓝图和动画蓝图通信?
可以回顾02 Blueprint communication Intermediate/Blueprint Interfaces
用接口BPI_AnimationBluprintsInterface传输


这个接口的函数在BP可以直接调用,在ABP中需要继承才能使用
角色蓝图

ABP_Base

JumpFallLoop->JumpFallLand

跳转条件:GroundDistanceCycleAlias的优先级设置为1,->IdleAlias的优先级设置为2

效果:

最后需要在落地后加一个缓冲附加动画
JumpInterupts

Jump提前落地机制
除了JumpFallLand可以回到Idle和Cycle,还会存在一些情况
比如跳到一个高的平台,不需要JumpFallLand也应该立刻回到Idle和Cycle

这些JumpInterupts只需要保证不在空中就跳转回Idle和Cycle

效果:

JumpFallLandRecoveryAdditiveLayer

落地缓冲附加动画
JumpFallLandRecoveryAdditiveSM

ALI_Lyra






JumpFallLandRecovery->Default



动画资产设置为Additive



获得下落的时间


JumpFallLandRecovery State需要一个权重参数LandRecoveryAlpha

LandRecoveryAlpha这个参数在状态机到达JumpFallLandRecovery状态实时更新,也就是LandRecoveryStart(),通过debug发现Idle起跳时FallingTime通常是0.4,所以先把FallingTime输入限制在0-~0.4,映射到0.1-1.0输出到LandRecoveryAlpha,如果是移动状态下就再乘以0.5放缩一下,避免alpha过大导致落地脚部畸形

考虑到如果起跳过一次后直接从高处落下,这个FallingTime需要在JumpApex的时候也要重置为0


效果:

存在缺陷:蹲伏状态无法进入Jump状态机,并且空中Crouch后落地会出现滑步,后面有空可以看LyraStarter工程的实现
17 Sync Groups Intermediate

Sync Groups同步组
为什么用同步组?
Sync Groups是ue动画系统中用于 同步多个动画播放 的重要机制。它允许不同的动画节点按照相同的时间进度进行播放,确保动画之间的协调性。当起步动画结束的时候,我们希望将其混合到Cycle动画中,本质是 确保多个动画在相同的时间点播放 ,通过领导者选择和位置同步机制,实现动画的协调一致。这解决了动画不同步导致的视觉不协调问题,让角色动作更加自然流畅。
如果没有Sync Groups,我们只能在混合的时候将摄像头放大,用来掩盖脚部的穿帮
常见应用场景:
场景1:角色移动动画同步

  • 需求 :行走、奔跑、跳跃等动画需要同步播放
  • 配置 :所有移动动画使用相同的Sync Group名称
  • 效果 :动画切换时保持时间连续性
场景2:武器攻击动画同步

  • 需求 :左右手武器攻击动画需要精确同步
  • 配置 :左右手动画使用相同的Sync Group,角色类型为CanBeLeader
  • 效果 :双手攻击动作完美同步
场景3:表情动画同步

  • 需求 :面部表情动画需要与身体动画同步
  • 配置 :表情动画作为跟随者,跟随身体动画的Sync Group
  • 效果 :表情与身体动作协调一致
SyncMarkerAnimModifier

同步标记动画修改器
ue会自动帮我们标记好动画的左右脚
一定要先关闭ForceRootLock再应用同步标记动画修改器,否则标记将不准确,应用完修改器再启用ForceRootLock
正确的流程:

  • 关闭ForceRootLock
  • 应用同步标记动画修改器
  • 启用ForceRootLock
接下来,为所有的Crouch、Cycle、Start、Stop、Pivot动画做这些操作

关闭ForceRootLock,保存

应用同步标记动画修改器,保存

启用ForceRootLock,保存

为每个Layer设置同步组

这一部分更详细的解释:UE5 骨骼动画 停步方案探讨(已完结) - DarkFlameMaster的文章 - 知乎
因为CycleLayer占据了最大的时间,所以AlwaysFollower


Stop的同步组必须单独设置:从Start单向过渡到Cycle,Start作为领路者,是Cycle去调整自己的“StartPosition”来配合当前角色动画播放到的位置。而当Cycle单向过渡到Stop时,按理来说是要Stop动画去调整自己的“StartPosition”来配合相位的,但倘若Stop作为领路者,从第一帧开始自然播放,本来的Cycle动画就得因此发生跳变了——自然,混合的结果也会出现跳变。



Blend Options

混合参数


开启后可以更加自然,常见情况是停步和急转时开启
18 Aim offset Intermediate

瞄准偏移
MeshSpace和LocalSpace

按照官方文档的指导:
对于采用BlendSpace实现混合时,通常会选择LocalSpace
而对于采用AimOffset实现时,通常需要选择MeshSpace



  • Mesh Space是 “基于网格初始姿势的全局叠加”;
  • Local Space是 “基于骨骼当前姿势的局部叠加”,被叠加的动画会受到叠加前的骨骼位置的影响
瞄准附加动画不需要根据当前姿势进行叠加,应当直接附加到初始姿势上,因此选择MeshSpace
可以参考这篇博客:浅谈MeshSpace和LocalSpace - 贺志武的文章 - 知乎
动画资产处理


选_CC作为基础pose

AimOffset混合空间


动画资产命名规则
C:Center
B:Back
U:Up
D:Down
因此LBC就是LeftBackCenter

AimOffset(Layer)

ALI_Lyra

获取AimOffset的Yaw和Pinch




配置资产

修复问题——蹲下时瞄准会站起来


效果:

19 Weapons Pro

武器
武器模型资产

ORM贴图:R-AmbientOcclusion阴影,G-Rough粗糙度,B-Metallic金属度

在角色SketalMesh中添加Soket,添加预览Mesh用于调整位置:


武器添加到角色class

新建结构体S_Sockets


注意命名规范

设置结构体的参数默认值

回到角色class,把武器添加到角色Mesh的子级

在角色蓝图中编写持枪的逻辑(这部分可以用C++重构)



用Attach Component To Component

IK解决持枪时手部摆放问题


HandIkRetargeting

Two Bone IK

修复移动时手部摆放问题



新建一个CopyBone节点,让hand_l复制前面设置的虚拟骨骼VB

把相机弹簧臂设置为越肩视角,效果如下:

这一章节如果要重定向为Meta human,可以参考:
【虚幻5 Metahuman 教程 - 如何完美重定向 Lyra 动画?
效果:

20 Foots Intermediate

脚部放置
FootPlacement



LegIK


效果:

跳起时将FootPlacement节点权重设置为0


番外篇:参考Lyra改进当前的动画表现

设置RootYawOffset时,需要考虑偏转角过大时,原地转身动画与瞄准动画的来回抽搐

实测效果:移动时转身还是会有问题,这个后面有空再看Lyra中是怎么写的

21 Weapons animations Intermediate

武器动画——开火、换弹
开火

导入动画

只开启武器动画的根运动,不需要开启人物动画的根运动,因为人物动画是一个附加动画
人物动画设置为附加动画

Input





动画蒙太奇

创建动画蒙太奇




渐入0,渐出0.3,曲线设置为cubic
把蒙太奇设置为ALS.FullBodyAdditive

回到ABP_Base,设置想要蒙太奇的slot


在角色事件蓝图中播放蒙太奇


效果:

换弹

导入动画

只勾选武器的根动画
Input



动画蒙太奇

创建动画蒙太奇

换弹动画的蒙太奇slot设置为ALS.UpperBody

渐入渐出参数

在角色事件蓝图中播放蒙太奇


Blend Mask

混合蒙版

在这个ALS_UpperBody的BlendMask中只需要把手部相关的全设置为1(除了手部的虚拟骨骼),剩下的可以参考下面的图



回到ABP_Base把BlendSlot“UpperBody”混合到整个LocomotionSM状态机

LocomotionSM创建一个副本,然后用LayeredBlendPerBone来把BlendSlot“UpperBody”混合到整个LocomotionSM状态机上

LayeredBlendPerBone节点设置为BlendMask,添加ALS_UpperBody进来,并启用Mesh旋转

我们还需要让播放换弹动画时禁用两个TwoBoneIk,否则换弹时的左手会很奇怪
可以添加动画曲线DisableReloadHandIK来实现,动画开始后0.03和结束前0.03s为0,其余为1

也就是换弹动画开始后0.03和结束前0.03手部ik的权重都为0,最大权重值1减去DisableReloadHandIK动画曲线的值 再传入手部的每个节点的权重alpha



效果:

22 Updates Section Intermediate

一些优化工作
修复:瞄准时的逻辑

Crouch时瞄准保持Crouch状态

瞄准时设置放缩相机



效果:

手持不同武器对应不同的Movement参数

空手、手枪、长枪时的速度加速度摩擦力这些都应当不同
Data Table

数据表
基于S_GateSettings创建一个数据表,这样就可以根据不同的武器来设置Movement参数


参考值:

手枪和空手的身体权重差异小,各项值较为接近
长枪重惯性大,速度更小,制动时的减速度更大
回到角色事件蓝图,先把原来配置Gate各项Movement参数的逻辑折叠为函数

为什么不用Macro(宏)?对于复杂逻辑不太适配

删去原来GateSettings的配置来源


这种{枚举,结构体}的字典用来管理大量数据不够优雅

因此换成我们前面的新加的数据表DT_WeaponGates

整体:

运行后发现更换Weapon的时候Gate没有变
在SwitchWeapon的事件中更新Gate

运行后发现更换Weapon的时候切换到Gate为Crouch状态时,数据表设置的每个武器的Crouch时的属性并没有实际作用,这是因为在Character Movement里面的Couch能力会覆盖我们的设置

因此我们需要改造SetGateSettings这个函数,加一个分支用来赋值Crouch最大速度即可,因为Character Movement里面的Couch能力只覆盖了这个最大速度


<blockquote>

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册