LyraALS学习
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°
其他角度的急转都是钝角,也就是点积<0即可

Pivot->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的来回切换条件应该是相同的:
- 速度与加速度反向,也就是单位点积<0
- 速度 与 急转后的加速度 向量不平行
两条规则同时满足

AState

SetupPivotAnims()

这里的Sequence选取动画资产和之前大致一样,但是需要根据加速度的角度来决定
因此,我们需要先获取加速度的角度
获取加速度的角度,加速度的移动方向(E_LocomotionDirection类型变量)
直接参考速度的这两个变量写


整理一下UpdateOrientationData
回到ABP_Layers的SetupPivotAnims()
这里的Sequence获取方式直接复制之前的,把Index换成加速度的移动方向


右键把这个选择动画资产封装为一个函数

勾选为Pure函数以及线程安全

可以在其他Layer中也这样封装选择资产的函数,我就直接操作了,不在这里赘述,后面如果看到调用"SelectXXXAnims"就是这个选择对应资产函数
同样的,回到各个子ABP_Layer中配置动画资产即可
以ABP_PistoLayer为例
注意:
Pivot动画要注意动画的实际表现是什么样的,Lyra的动画资产中,pisto walk pivot Bwd实际上是衔接上一个Bwd和下一个Fwd
而我们上面写的动画资产选取是以当前的加速度移动方向,也就是已经发生实际的转向逻辑,所以应该把Bwd的动画资产放在Fwd的插槽,其他的动画资产同理

UpdatePivotAnims()



注意速度与加速度反向时的距离匹配用的DistanceToTarget是PivotLocation预测节点
距离匹配用的曲线和之前一样操作,对Pivot动画添加DistanceCurve修改器
BState
直接复制AState的SequenceEvaluator

方向适配

然后需要设置一下方向适配节点的参数:

当根运动速度Root Motion Speed低于阈值时,Warping缩放值被强制设为1.0(即不进行缩放),当根运动速度高于阈值时,才根据实际速度比例计算Warping缩放,很多动画的根运动速度可能低于10.0(比如慢走、小幅度移动),这种时候就需要降低这个阈值,更有甚者,对于停下/急转这种速度会降到0的动画,可以把阈值设置为0
当角色移动速度与动画速度不匹配时,本应进行的Warping被禁用,导致角色动画与实际移动不协调,还有一种情况就是:在动画开始或停止时,根运动速度会在阈值附近频繁变化,导致Warping效果频繁开关,产生动画抖动,即使启用了插值,这种频繁的开关也会影响动画流畅度
记得复制到BState
效果:

14 Turn in place Pro
原地转身
导入动画,开启根运动,应用压缩曲线
RotateRootBone
根骨骼旋转节点
UE中的三个旋转角
UE使用的是左手坐标系:

- Pitch:绕Y轴旋转
- Roll:绕X轴旋转
- Yaw:绕Z轴旋转
因此加入新的节点RotateRootBone,原地转身应该改变的是Yaw,新建变量RootYawOffset

UpdateRootYawOffset
获得RootYawOffset
相机水平转动,默认情况时角色朝向保持不变,当水平转角超过90度,朝向同步变化
原先的逻辑是角色朝向随相机同步,我们只需要每帧让传入RotateRootBone节点的参数RootYawOffset减去相机转角就行
前后帧相机转角——DeltaYaw(这个值我们之前就已经在GetRotationData中算过了)

让角色朝向保持不变
传入RotateRootBone节点的参数RootYawOffset减去这个DeltaActorYaw即可

这会导致运动时候角色的朝向也不变,那么转向就不能正常运作了,我们需要让这个逻辑只在Idle时起作用
只有在Idle时角色朝向才会保持不变
用枚举类型区分Idle和其他状态



Debug显示RootYawOffset
S_DebugOptions结构体加一个参数



区分RootYawOffsetMode
回到ABP_Base的IdleState,OutputPose绑定一个OnUpdate回调函数
Update Idle State直接赋值Mode为Accumulate即可,这样就会在Idle状态期间RootYawOffsetMode保持为Accumulate

回到UpdateRootYawOffset,让mode的默认值为BlendOut

封装一个函数专门用于设置RootYawOffset的值

并且,如果当前的Mode是BlendOut,就重置RootYawOffset

这样就做到了Idle的时候摄像头移动不改变角色朝向,只有角色不在idle状态时才会改变朝向
效果:

存在缺陷:角色朝向与相机朝向存在差异时,移动的时候会立刻重置RootYawOffset导致角色朝向突变,需要加一个插值过渡一下
插值过渡(RootYawOffset,0)
Update Root Yaw Offset函数需要先新建一个Input参数DeltaTime,在安全线程中传入


效果:

Float Spring Interp(浮点弹簧插值)节点
- Current :弹簧插值的当前浮点数值(起始值)。
- Target :弹簧插值最终要趋近的目标浮点数值。
- Spring State :需变量存储的弹簧状态容器,记录跨帧的速度、加速度等动态数据。
- Stiffness :弹簧的硬度,数值越大,趋近目标的速度越快。
- Critical Damping Factor :控制回弹幅度,1.0 是无回弹的临界阻尼,<1 回弹、>1 缓慢趋近。
- Delta Time :当前帧的时间增量,保证不同帧率下运动节奏一致。
- Mass :弹簧末端物体的质量,数值越大,惯性越强、响应越慢。
- Target Velocity Amount :目标速度的权重,数值越大,弹簧对目标速度变化的响应越灵敏。
存在缺陷,RootYawOffset过渡到0期间方向适配OrientationWarping会失效,需要修复

这个带偏移的angle传入ABP_Layers的各个Layer的方向适配





效果:

起步会有滑步,以及移动时转动视角同时按下相反方向不会进到PivotState(待修复)
曲线附加动画实现原地转身
MotionExtractorModifier
动作提取修改器:
从动画序列中,提取出角色的位移、旋转、速度等运动数据,用于驱动角色的实际移动
暂时关闭RootMotion,可以看到转身动画是根骨骼的z轴在旋转,从0到-90(nearly)

并且需要将曲线最终的值设置为0,可以把曲线都上下翻转一下,然后手动在各个动画资产中对曲线进行上下限的重定义
因此,添加MotionExtractorModifier如下设置:

进入每个资产手动调整曲线上下限,目的是让最终的值为0,以90度和180度的转身动画资产为例:
90度

注意上下限要严格精确到-90(或-180)和0


180度

加一条IsTurning曲线,并标注两个关键帧
第二个关键帧是root_rotation_z完全不变的第一个点,值为0
第一个关键帧是第二个关键帧的前一个点,值为1
这样,1和0对应true和false,true代表正在转身,false代表已经转身结束


最终呈现先1后0的突变函数

如果想更自然的过渡,可以选中IsTurning曲线,右键Auto

得到更平滑的曲线

TurnInplace States
回到ABP_Layers,在IdleLayer新建一个IdleSM,用来管理IdleLayer下的逻辑


原来Idle的SequencePlayer放进Idle State中

Idle->TurnInPlaceEntry
跳转条件:RootYawOffset超过一个阈值

从RootYawOffset的变化可以看出,如果RootYawOffset>50左转,<-50右转是较为合适的值
因此转身的阈值可以设置为50

左/右转由RootYawOffset的正负决定,>0左转,<0右转
当进入TurnInPlaceEntry State时,执行该判断


TurnPlaceEntry State的SequenceEvaluator
回到TurnPlaceEntry State,用SequenceEvaluator播放动画

SetupTurnInPlaceEntryAnims()

选择资产函数SelectTurnInPlaceAnims()


然后在各子ABP_Layer中配置TurnInPlace动画资产
以ABP_PistolLayer为例

UpdateTurnInPlaceEntryAnims()


这里新加了一个随DeltaTime自增的变量TurnInPlaceTime,传入ExplicitTime之后动画播完需要重置为0,因此需要回到SetupTurnInPlaceEntryAnims设置它为0

TurnInPlaceEntry->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
跳转条件:角色在重力加速度的情况下到达最高点所需要的时间<一个阈值
z轴速度/重力加速度,最后取反就是到达最高所需时间


动画分层管理
ALI_Lyra中新建JumpStartLayer、JumpStartLoopLayer、JumpApexLayer

ABP_Base中对应State连上对应Layer
然后在ABP_Layers相应的Layer中用SequencePlayer播放
JumpStartLayer

JumpStartLoopLayer

JumpApexLayer


构建落地阶段的状态机
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
跳转条件:GroundDistance<一个阈值
通过debug可以看出150是个较为合适的值


SetupJumpFallLandAnim()
在OnBecomeRelevant中设置从0播放动画

UpDateJumpFallLandAnim()
前面提到由于落地需要实时距离匹配,所以用SequenceEvaluator
因此,在OnUpdate中实现这个距离匹配,并推进动画播放
添加z轴的距离曲线修改器

确保曲线的最后的值为0


Advance Time by Distance Matching和Distance Match to Target的区别:
因此对于停下或者落地这种有目标位置的,应当使用
Distance Match to Target
落地时候的DistanceToTarget就是0

效果:

还需要最后一个收尾
EndOnAir

跳转规则:




为了防止同时触发,->CycleAlias的优先级设置为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设置同步组
因为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能力只覆盖了这个最大速度


👆同时我整理了下这个函数的节点排版,清爽很多
效果:

修复:移动时切换武器,有时会出现脚部没同步的情况
在ABP_Base的各个Link Layer,默认的Blend In/Out值是-1,也就是这些链接层默认不进行渐入渐出


因此,我们需要设置一下他们各自的渐入渐出值,一个比较合适的值是0.15

渐入渐出的Profile还可以设置是否启用FastFoot,暂时效果还行,可以不用启用FastFoot
效果:

23 Audio Intermediate
音效
导入音频资产
开火声音
播放声音
播放声音的位置应该是枪管



让Fire声音更加随机化
创建一个MetaSoundSource


创建这个MetaSoundSource的预设


找到InputSource,可以Override其预设模板,替换为各自的资源


每个武器分配完InputSource Sounds后,回到前面设置声音资产的地方

子弹轨迹线

起点是我们视线的中心点,也就是相机的位置
是吗?
终点是是从起点出发向正前方很远的点
终点可以做很多文章,这会影响子弹的下坠等等效果
如果击中目标,则返回受击点相关数据,未击中,则返回子弹轨迹线的终点



效果:

在受击点播放子弹撞击声音


这里还可以根据ImpactPoint的信息来判断击中点具体是什么东西,分配不同的击中声音,后面再加
受击点的声音随距离衰减
创建一个SoundAttenuation(声音衰减)


打开受击MS的Source选项,选择声音衰减


勾上这个debug,运行游戏后,按下~键,输入
即可看到声音衰减的调试信息:
不同类型的声音衰减的调试画面不同,可以多试试
根据受击物体的类型播放不同的受击点声音
在项目设置中添加一个表面类型——Glass

新建物理材质,设置表面类型为Glass


新建材质,设置该材质的物理材质


在场景中附上材质

回到角色蓝图,封装一下播放受击点声音的逻辑——PlayImpactSoundWithDebris


换弹声音
一个换弹蒙太奇动画有多个阶段(ClipIn,ClipOut,Slide),因此需要在不同帧播放不同的声音资产
只需要在换弹蒙太奇中添加Notify即可

TODO:手枪换弹时切换武器会出现播放蒙太奇错误
根据地面材质类型播放对应的落地声音
新建一个AnimNotify类(注意:不是AnimNotifyState)

在JumpRecoveryAdditive动画中添加AN_Land

回到AN_Land,添加ReceiveNotify函数

从Pelvis处向下发射检测射线


这会在目标动画的AN_Land触发时发射检测射线

根据落地面不同的表面材质播放不同MetaSound




记得把三种Wepon的落地动画资产中都添加AN_Land通知

走路和慢跑时的脚步声音
回顾17 Sync Groups Intermediate时做的:SyncMarkerAnimModifier在动画曲线中标记了动画的左右脚

先创建AnimNotify——AN_FootOnGround(内部逻辑和AN_Land一样,只是替换了BoneName和MetaSound资产)


新建MetaSound:MS_FootOnGround,与MS_PlayRandomSound的唯一区别是在Out Mono加一个的音量调节,并设置好默认值和区间



然后创建四个MSP
因为动画曲线中标记了动画的左右脚,因此需要两个动画通知来管理,这两个通知类是AN_FootOnGround动画通知的子类


为了在子类中能Override变量BoneName,需要公开出来
而声音资产也对左右脚声音做了区分,因此也需要公开

在子类中Override


Locomotion用的动画通知设置好了,下面需要把通知加到所有的动画资产中
这里我开发了一个用于批量处理动画资产的编辑器工具。它可以根据动画中已有的 Anim Notify (含Sync Marker同步标记)的名字,在同一帧自动添加指定的 AnimNotify 类
github:https://github.com/EanoJiang/AnimNotifyBatchTool
对于一些Sync Marker修改器无法标记的动画资产,比如原地转身,则需要手动添加通知
并且由于Sync Marker只在脚部完全落地时才标记左右脚,因此还需要对自动添加的通知所在帧进行略微向前调整(这一部分重复度太高,后面再做)
改变脚步音量大小
在基类AN_FootOnGround把原来的PlaySoundAtLocation节点换成SpawnSoundAtLocation,这会返回一个AudioComponent,然后用SetFloatParameter设置其参数,因为前面在MS中加入了Volume音量调节输入,因此这里的InName填Volume,下面的InFloat稍后根据当前速度处理

我们已经在动画蓝图ABP_Base中获取过当前角色的速度,那么如何在其他蓝图中访问这个参数?
我们需要在BPI接口中添加方法,并添加Output


然后在ABP_Base中实现这个方法

回到AN_FootOnGround,用AnimIntance调用动画蓝图中的方法,并根据DT_WeaponGates中的速度范围,钳制0-500映射为0-2.0,存在为Volume传入SetFloatParameter节点的InFloat



24 Visual effects Intermediate
特效
枪焰特效
在枪模型Skeleton的Root下,新建一个Socket——Muzzle,摆放到枪口位置

在枪的开火动画资产中,添加Play Niagara Particle Effect动画通知


设置SocketName和特效资产

记得勾上特效的USER.Trigger

效果:

弹壳弹出特效
方法相同




为其他武器做同样的操作
枪线轨迹特效



整理代码


受击材质破碎特效




要Set的值有

效果:

25 Weapons UI Intermediate
武器切换UI
弹夹和子弹图标
创建材质,把贴图Promote为参数

创建材质实例,更换Icon贴图


准星UI部件
创建WidgetBlueprint




效果:

Debug——显示子弹数量
从ABP_Base复制Debug函数到角色蓝图中




这几个变量需要在Fire和Reload事件中更新
开火后更新子弹数量
如果子弹数量PistolBulletAmount > 0,自减1,并返回有子弹,否则返回无子弹


效果:

换弹后更新子弹数量



效果:

开枪和换弹时的UIBackGround
UI布局

UI面板大小

Border,半透明黑色背景,白色边框

子弹Icon




部件蓝图
创建User Widget



回到WBP_PistolUI,把子弹和弹夹所在的容器设置为变量


打开蓝图模式


Widget和角色蓝图通信
Interface



回到WBP_PistolUI


回到角色蓝图,新建Widget,并设置为屏幕空间,设置WidgetClass


在枪的骨骼上新建Socket——Widget



在角色蓝图的Fire中调用BPI_Widget接口的UpdateBulletAmount,在Reload中调用UpdateClipAmount



并在BeginPlay的时候初始化这两个接口函数

设置一下武器弹药UI什么时候显示
设置标志位



拿枪瞄准时显示,换弹时不显示


效果:

为其他武器做同样的事情
效果:
修复bug——换弹期间不能开火





























浙公网安备 33010602011771号