Echo Quest
Demo 展示
技术实现
GAS (Gameplay Ability System)
为什么要使用 GAS ?
模块化与解耦:Gameplay Ability (GA) 只管逻辑流程、状态机、Gameplay Effect (GE) 只管数值变化、Gameplay Cue (GC) 只管表现层、Attribute Set 只管角色基础属性数值、Gameplay Effect 用于配置技能的约束条件 (Cost/Cooldown) ;
Tag-Based的状态管理:彻底告别 Bool 变量地狱,可以非常方便的管理不同状态间的触发、屏蔽逻辑关系;
复杂的数值运算系统 (Attribute Set):可以通过 GE 随时添加角色基础属性的影响 (Add/Multiply/Override),避免算法逻辑重构;
网络同步与预测:在联机游戏中可以很好的处理客户端预测,实现 Ability 激活、Attribute(血量/蓝量)和 Effect 的同步。
GAS 核心组件解析:
Gameplay Ability:在 GAS 系统中,当满足特定触发条件(即技能本身或角色持有指定的 Gameplay.Tag )时,对应的 Gameplay Ability 会被激活,并执行其关联蓝图 (Blueprint) 中的具体逻辑;
Gameplay Cue:用于配置表现层逻辑(例如受击反馈、闪避特效等),通常在对应的 Gameplay Ability 执行过程中被触发;
Gameplay Effect:用于配置技能的约束条件(Cost/Cooldown),动态的向角色赋予 Gameplay.Tag(以配合 Gameplay Ability 的触发判定),修改角色属性(如闪避时的耐力消耗、冷却时间),以及应用特定标签(如 Being Hit)来触发相应的受击效果。
以闪避为例,说明 GAS 在项目中的使用
2. 接管玩家输入,通过玩家输入触发对应的 Gameplay Abiility - GSC_InputBinding:
将玩家输入 (Player Input) 迁移至 GSC_InputBinding ,实现对玩家输入的宏观统筹管理,并为每一个角色状态分配对应的 Gameplay State Tag;
在玩家按下 Shift (IA_Dodge) 时,会运行 GA_Dodge (Gameplay Ability) 中的逻辑;
可以理解为玩家的所有游戏性动作输入(如攻击、闪避、放技能等),都会激活一个对应的 Gameplay Ability,由 GA 统一接管具体的逻辑实现与状态判断。
3. 在 GA_Dodge 中执行闪避具体逻辑:
能力激活后,首先判断玩家当前是否有方向输入,若输入向量的 XY 轴长度 > 0 为 False(无水平输入),则开始判断玩家是否在空中 (IsFalling) ,若两者都为否,则可认定当前玩家角色静止在原地;若有,则计算玩家最后输入的方向方向(决定闪避朝向)- 左上角注释部分;
若玩家有方向输入:则瞬间旋转玩家 Actor 到上一帧的输入向量方向(这确保了Dodge 会朝着玩家希望的方向执行),播放向前闪避的动画蒙太奇,获得玩家按下闪避时的位置 - 播放闪避粒子特效(Gameplay Cue),通过 Apply Root Motion Constant Force 节点接管角色的 CharacterMovementComponent,在设定的持续时间内持续不断地给角色施加一个向前力(好处是可以后期随时修改闪避的距离及持续时间,并且支持网络预测和同步);
若玩家静止在原地:则直接播放向后闪避的动画蒙太奇,获得玩家按下闪避时的位置 - 播放闪避粒子特效(Gameplay Cue),通过 Apply Root Motion Constant Force 节点接管角色的 CharacterMovementComponent,在设定的持续时间内持续不断地给角色施加一个向后力(这里可以看到角色向后闪避的位移比向前闪避的要小,即体现了使用该节点接管的好处)。
7. 两种 Gameplay Effect 的配置(处理耐力消耗和技能冷却时间):
左图为 GE_Cost _Dodge 逻辑,技能启动时自动触发该 GE,为步骤一中定义的 Attributes 中的 Stamina(耐力值)减 5;
右图为 GE_Cooldown _Dodge 逻辑,即在当前技能结束后为玩家添加 Cooldown.Dodge 的标签,持续1秒,在此期间玩家不可以再次触发该 GA 。
战斗系统实现(Combo Graph)
为什么要使用 Combo Graph ?
ComboGraph 在项目中被定位为 表现层 (动画/连招) 与 逻辑层 (GAS) 间的桥梁;
ComboGraph 负责处理角色的连招逻辑。它集成了连招输入及缓冲 (Input Buffering) 与攻击动画的播放管理,并负责与 GAS 系统对接,以分发和执行核心的攻击事件(包括造成伤害、计算技能消耗 Cost、以及触发 Gameplay Cues 的表现反馈);
系统构成:ComboGraph 系统主要由以下两个核心组件构成:
ComboGraphCollision:负责处理运行时的碰撞检测逻辑(见下文);
Primary ComboGraph Asset:定义连招节点与流程的数据资产。
为什么要与 GAS 对接?
明确分工:ComboGraph 管节奏,比如哪一帧可以取消后摇?哪一帧可以派生下一个攻击?这是动画状态机擅长的。而 GAS 管数值,扣多少角色属性、造成多少伤害、挂什么 Buff?这是 GAS 擅长的;
当 ComboGraph 判定攻击命中(Collision)或连招触发时,它不直接修改血量,而是发送一个 Gameplay Event 给 GAS,或者直接 Apply Gameplay Effect。这样保证了逻辑的统一性。
以轻攻击 Combo 为例,说明 Combo Graph 在项目中的使用
左边的部分解释了连招的逻辑,当玩家输入 LightCombo 的按键(鼠标左键)后,会在 PlayerBP 中执行这个 ComboGraph(从 Entry 开始)并播放第一段动画蒙太奇。下面的连线表示输入不同的按键会按顺序播放的对应动画蒙太奇(Combo 连段),轻攻击 Combo 共有四段,最后一段支持左右键轻重击转换(图中最下)。
右边的部分则是需要执行的具体内容,从上到下分别是:
动画:播放对应的蒙太奇(蒙太奇中的 AN 与 ANS 也会一同被执行)并设置播放速率;
Gameplay Effects:为 OnHit 物体发送 Event.Montage标签(上文有提到过),并且激活 GE_Damage_Melee (GameplayEffect) 用来处理伤害计算,-5.0 是这段攻击的伤害值,并通过 GE_Cost_LightAttack 来声明耐力消耗;
Gameplay Cues:在收到 Event.Montage 标签后处理的表现层效果,用以配置如受击位置的声音播放、粒子效果播放等。
碰撞检测 ComboGraphCollision:
ComboGraphCollision 是挂在在发攻击 Actor 上的一个组件,作用是处理攻击时的碰撞检测;
这个组件不仅仅是检测碰撞,它实际上充当了一个碰撞管理器的角色。它解决了使用 UE 原生碰撞 (Overlap/Hit) 难以解决的几个痛点(上一个项目的踩坑点):
去重 (Hit Deduping):一次挥剑动画持续 x 秒,每一帧都在做检测,如果前面已经打中过敌人,后续的帧就不应该再触发伤害,否则敌人会被秒杀。这个组件负责维护一个 HitActors 列表,确保一次挥击,每个敌人只受一次伤;
数据封装:它可以直接把命中结果 (Hit Result) 打包成 GAS 需要的 FGameplayEventData 。
打击感的构成与实现
动作游戏打击感的构成(个人习惯按感官维度分类):
视觉维度:角色动画(攻击前摇蓄力 - 剪影、攻击轨迹 - 刀光、攻击后摇 - 惯性)、命中反馈(顿帧/卡肉 - 表现阻力,根据武器重量、锋利程度及攻击力度决定)、受击位移、受击硬直(受击动画)、视觉特效(接触点特效 - 溅血火花烟尘等,特效方向通常与受击方向相反)、环境互动(地面/草)、屏幕特效(全屏暗角、后处理效果等)、相机(震屏 - 震动方向与攻击方向一致,震动幅度与伤害量相关、镜头推拉/变焦/特殊镜头);
听觉维度:音效分层(挥动层、撞击层 - 材质区分/重击强低频、残响层 - 空间中的回声)、动态反馈(音调变化、暴击特殊处理);
触觉与操控维度:手柄震动(震动层级区分 - 强度、方向性、阻尼)、输入与时机(输入缓冲 - 当前动作结束前缓存玩家输入,并根据优先级规则无缝衔接下一个动作)、受击时停(顿帧 - 改变玩家的按键输入节奏)。
角色动画系统(Lyra 动画蓝图 + Distance Matching)
Motion Warping
敌人 AI 与 EQS
关卡设计
GAS 相关插件:
Gameplay Ability System (Base)
Combo Graph
GAS Companion
角色基础属性定义 - GAS Attributes:
闪避需要消耗耐力(角色基础属性)。Attributes Set 以 数据表 (DataTable) 的形式存储玩家和敌人的基本信息,如生命值、耐力值、法力值。这样做非常便于维护与扩展,比如我后面想再加入削韧值,便可在这里直接统一配置。
4. 通过 Gameplay Cue 播放闪避时的粒子特效表现:
Gameplay Cue 负责所有表现相关逻辑,由 GameplayCue Tag 触发(下方图)。这样做最大的好处是节省带宽,服务器只需要发一个极其轻量的 Tag
GameplayCue.Dodge,客户端自己就知道该怎么、在哪播;Gameplay Cue 分为两种,分别是 GameplayCueNotify_Static 和 GameplayCueNotify_Actor,前者主要用于播放一次性的效果(枪口火焰),而后者用于播放持续存在、需要 Tick 的特效(如拖尾);
下图为闪避粒子特效的 GameplayCue 逻辑,在执行时会获得 GameplayCue 的位置信息与执行者(玩家)的位置信息,通过 Spawn System Attached 在玩家位置生成闪避 Niagara VFX,并通过持续获得拥有该 GameplayCue 的 Actor 的位置信息(玩家位置),来实现粒子的拖尾跟随效果。
6. 通过 Gameplay Effect 添加耐力消耗和技能冷却时间
与此同时,在技能激活时,会触发 GE_Cost _Dodge 和 GE_Cooldown _Dodge 两个 Gameplay Effect 。
2. 处理 OnHit 事件:
当 ComboGraphCollision 组件检测到武器判定框与物体发生重叠时,会广播 On Hit Registered 事件,随即执行以下一系列逻辑:
目标筛选:首先进行目标筛选,防止对非敌对目标造成误伤。获取 Hit Result 中的 Hit Actor,检查其是否持有 Attackable 标签,只有持有该标签的 Actor(敌人)才会触发后续逻辑,否则直接终止;
伤害与事件分发:确认为合法目标后,调用 Report Damage Event 函数,传入受击者、施害者及伤害数值,处理基础扣血逻辑。
GAS 事件负载构建:使用 Make GameplayEventData 构建事件数据包,通过 Ability Target Data from Hit Result 将物理碰撞结果转换为 GAS 可识别的数据格式;
发送 Gameplay Event:向玩家自身 (Self) 发送带有 Event.Montage Tag 的事件,通知玩家的 GAS 系统攻击已命中,用于触发实现打击感的表现层反馈。
资源回填(攻击回蓝):基于 GE_ManaRegen 创建一个 Gameplay Effect Spec 实例后,利用 Assign Tag Set by Caller Magnitude 节点,通过标签 SetByCaller.Mana 动态设定恢复数值(可以避免在 GE 中写死数值,允许在蓝图中根据不同攻击连段灵活调整)。
UI 更新:最后调用 Show Mana UI ,通知 UI 读取最新的 Attribute 属性值,刷新 HUD 上的法力条显示。
8. 技能结束:
闪避动画播放完成后即是闪避 GA 的结束。如图,当闪避动画蒙太奇播放完成、混出、被打断或取消时结束技能状态;
结束时会移除所有与该 GA 相关的 Tag,并启动 Cooldowns 中的冷却时间。
为什么要用 Combo Graph ,而不用蓝图手搓连招逻辑?(上一个项目中踩过的坑)
更清晰的可视化节点:ComboGraph 提供了一个可视化的树状结构,每一个节点就是一个动作(Animation),每一条连线就是一个分支条件(Input)。在蓝图中则需要创建一个巨大的 Switch on Int(当前连招段数),然后接一堆 If 判断(是否按下攻击键、耐力够不够?)。一旦连招出现分支设计(如 轻-重-轻),连线就会变得一团糟;
内置高精度输入缓冲 (Input Buffering) 机制:这是动作游戏“手感”的灵魂,也是蓝图很难写好的地方。Combo Graph 自带 Input Buffer,在当前动作还在播放,但玩家输入了下一个动作时,系统会自动记录当前舒服,并等当前动作结束后立刻自动衔接下一个动作。这能带来极度流畅的操作手感;
便于后期迭代:Combo Grpah 由 数据驱动,修改时只需要在编辑器里拖拽节点、修改连线即可,而不用去蓝图里断开一堆连线,重新接线,甚至重写逻辑;
易于扩展复杂的派生机制:动作游戏通常有复杂的派生,如 A →(按住)→ A(蓄力变招),在蓝图中实现停顿检测或长按检测并混在连招逻辑里最后一定会一团糟。而在 Combo Graph 中通常只是连线上的一个不同转换条件而已。
动画蒙太奇的配置 (Anim Notify):
Anim Notify 用于在动画播放到某个时间节点时,发送对应的信号通知或触发对应的逻辑。
目前一段攻击动画中,配置有以下几个 Anim Notify:
Motion Warping:用于处理 Motion Warping 逻辑(下文会提到);
Collision Window:用于处理攻击的碰撞判定(下文会提到);
Niagara VFX / Sound Effects:用于播放武器攻击时的拖尾特效 (Niagara VFX) 和 角色脚步声及武器攻击音效。
Control Rig
1. 攻击启动与目标捕获:
触发阶段: 当玩家输入鼠标左键 (IA_LightAttack) 激活轻攻击 Ability 时,系统启动 LightAttack ComboGraph ,并立即执行 GetLightAttackTarget 函数进行目标检索;
广域筛选:以角色为圆心,执行 Sphere Overlap(半径为 x),获取范围内除自身外的所有 Pawn 类 Actor。若无结果,函数结束;
精细筛选:对重叠检测到的结果进行三步过滤。标签检查,剔除不包含 Attackable 标签的 Actor(过滤友军/非交互物),将所有未被剔除的 Actor 加入 HitActors 的 Array 中;距离校验,计算玩家与目标的绝对距离,剔除距离大于 AttackAttachMinDist(配置的大吸附距离)的目标;最优解择取: 在剩余的候选列表中,选择距离玩家最近的 Actor 作为最终攻击目标 (Target Actor) 。
后续优化方向:加入索敌加权评分系统,分数 =(距离* W)+(与相机画面中心的角度大小 - 使用点乘实现* W)+(玩家当前推杆方向* W)+(敌人是否处于特殊状态* W),最终索敌目标为分数最高者。
3. On Hit 后的数值与标签传递:
在执行上述逻辑时,角色会向自身发送 Event.Montage 标签,并会被 ComboGraph 捕获,执行左上图中的逻辑:首先,激活 GE_Damage_Melee (GameplayEffect) 中的逻辑(右图),分别是激活 GE_HitReaction_Melee ,并修改 Health Attribute, 数值为 Set by Caller ,即 Combo Graph 中的 -5.0 ,随后激活 GE_Cost_LightAttack ,用以处理该段攻击的耐力值消耗;
GE_HitReaction_Melee:在该 GameplayEffect 中,通过给目标 Actor(受击者)增加 Event.Character.BeingHit.Melee Tag(左下图)的方式,激活对方的受击 Gameplay Ability;
这样写的好处是可以使用 SetByCaller 特性,通过单一 GE 实例处理动态伤害数值,方便后期修改并避免资产冗余;同时通过 Tags 触发受击反应,实现了攻击者与受击者的完全解耦(依赖倒置),受击表现由目标自身的 GAS 逻辑决定,可以很好的支持不同怪物类型的多态响应;并且通过将伤害、消耗与状态判定分离,极大降低了技能逻辑的耦合度,便于后续的 Buff 扩展。
5. 敌人侧打击感表现的实现:
目标角色收到 Event.Character.BeingHit.Melee 信号后,会执行 GA_HitReaction_Enemy_Melee(右图,该 GA 由指定 Tag 触发,并执行其中的打击感实现逻辑;
目前 GA 中的打击感构成包含相机震动、音效播放、慢镜头、卡帧和受击动画四种。
技术选型分析:为什么要用 Lyra (Distance Matching & Orientation Warping) ,而不用 ALS 或 Motion Matching?
选择 Lyra (Distance Matching) 的动画方案,是在确保运行性能、考虑开发维护成本与动作类玩法下精确控制三者间取得的最佳平衡点。
为什么不用 ALS:ALS 通过极其复杂的蓝图数学运算来模拟惯性和倾斜达到了很高的拟真度表现,这些逻辑主要运行在游戏主线程 (Game Thread) ,大幅增加 CPU 开销;同时 ALS 采用单体式架构,所有状态紧密耦合在一个巨大的状态机中,扩展难度呈指数级上升,也不适配本项目复杂的 GAS 技能系统;
为什么不用 Motion Matching:Motion Matching 高度依赖高质量的动捕数据,不适合个人项目使用;以及 Motion Matching 本质上是模糊搜索,其动作过渡具有一定的不可预测性。本项目属于强动作类,强调响应速度和精准的攻击判定。Lyra 的状态机方案提供了更强的确定性控制,确保连招和闪避的手感永远是精确且可稳定复现。
Lyra 动画架构的核心技术优势:
线程安全的动画更新:传统动画蓝图通常在 Event Graph 中处理大量逻辑(如计算速度、方向、倾斜角),这会导致逻辑在游戏主线程运行,影响 CPU 性能。 Lyra 架构采用 Thread Safe Update Animation 流程,将数据获取与逻辑运算剥离至工作线程 (Worker Threads) 并行执行,利用 UE5 的属性访问系统,直接从 C++ 层(如 Character Movement Component)高效获取数据,并缓存为原生 Float/Bool ,为复杂的动画解算提供极高效率的数据支撑的同时不再占用主线程资源。
分层动画接口 (Linked Anim Layers):为了解决传统单体动画蓝图高耦合问题,Lyra 引入了 Anim Layer Interface ,实现了逻辑与表现的分离。主动画蓝图仅作为状态机框架,具体的动作逻辑被拆分为独立的 Layer,如 Locomotion Layer(基础移动)和 Item Layer(对应不同的武器/道具)。当需要扩展新武器时,只需动态链接对应的 Item Layer 资产而无需修改基础逻辑。这种模块化设计极大降低了维护成本。
以主角动画蓝图 ABP_CyberSkaterBase 为例,我将整个动画流程拆解为 数据驱动层、图层架构层 与 表现执行层 三个部分:
线程安全的数据驱动 - Thread Safe Update Animation:
传统的动画蓝图在 Event Graph 中每帧 Tick 执行大量逻辑,极易造成主线程性能瓶颈;
在 Lyra 架构中,改用 BlueprintThreadSafeUpdateAnimation 函数接管数据更新(见上图,位于主动画蓝图中)。利用 UE5 的 Property Access(属性访问)系统,直接从工作线程访问 CharacterMovementComponent ,提取旋转、速度 (Velocity)、加速度 (Acceleration) 及角色状态等数据(下文会分类解释图中每个函数的作用),并将它们缓存为原生 Float 或 Bool ;
这样做的好处: 将繁重的动画解算逻辑从游戏主线程剥离,降低 CPU 性能开销,确保极端情况下的帧率稳定性。
1.1 基础物理数据更新:
Update Velocity(更新速度,下图):从 CharacterMovementComponent 获取角色的世界空间速度向量(注意 Property Access 节点),并计算 Ground Speed(地面速度)。这是驱动动画状态机的核心数据。它决定了角色状态 (Idle / Moving) ,同时 Ground Speed 直接驱动了 Distance Matching 的播放速率,确保步伐与移动速度匹配。
Update Acceleration(更新加速度):获取角色的当前输入加速度 (Input Acceleration) ,也就是玩家的操作输入。在角色速度还没提起来的瞬间(起步阶段),动画系统须依靠加速度来预判玩家想往哪走,从而立刻播放正确的起步动画。
Update Rotation(更新旋转):获取角色的世界旋转 (Actor Rotation) ,用于计算角色面朝向与玩家视角的偏差。
1.2 方向与位移解算:
Update Locomotion(更新位移参数):计算并缓存角色在帧与帧之间的实际位移量 (DisplacementSinceLastUpdate) 和基于位移的实际速度 (DisplacementSpeed) ,作为 Distance Matching 的核心输入源。它确保动画的播放进度是根据实际运动距离驱动,而不是简单的时间,从而在底层物理层面彻底消除滑步现象。
Update Cardinal Direction from Velocity(基于速度更新基数方向):将 360 度的连续移动方向,量化为 4 个枚举值:F(前),B(后),L(左),R(右),主要用于急停动画的选择。当玩家瞬间取消方向输入时,系统根据当前的速度方向决定急停动画方向。
Update Cardinal Direction from Acceleration(基于加速度更新基数方向,右上图):将玩家的输入意图量化为 4 个离散方向,主要用于 Start(起步)和 Pivot(折返)动画的选择。如当速度向前但加速度突然向后时,系统会识别出这是折返跑操作,从而触发 Pivot 动画。
Update Is Moving Perpendicular to Initial Pivot(更新垂直切向运动状态,右下图):用于检测玩家是否在进行直角拐弯或者复杂的混合输入(如从按住 W 变成突然按住 D),用来优化 Pivot(折返)的手感,确保在复杂走位下的动作过渡平滑流畅。
1.3 状态与环境感知:
Update Character State Data(更新角色状态数据):从移动组件中读取当前的移动模式,并将其转换为动画系统专用的 Bool 状态。这是主状态机进行 Locomotion / Jump 状态切换的根本依据;
Update Jump Fall Data(更新跳跃与滞空数据):用于计算并设置 JumpApexTime(跳跃顶点时间戳),也就是角色在跳跃过程中,垂直速度(Z轴速度)从正数变为负数(开始下落)的那一瞬间的时间,也就是角色到达最高点 (Apex) 的时间点。通过计算 CurrentTime - JumpApexTime ,得到 TimeFromApex(下落持续时间),来驱动下落时 (Fall Loop) 的动画混合,并辅助落地预判逻辑的计算。
2.2 主蓝图逻辑管线 - ABP_CyberSkaterBase (AnimGraph) :
在 ABP_CyberSkaterBase 的 AnimGraph 中,我搭建了一条标准化的 Pose 处理流水线:
Locomotion State Machine(见下文)- Inertialization(惯性插值,Lyra 架构的关键节点,负责所有状态间的平滑过渡,允许状态机在极短时间内完成姿态突变,保持视觉的连续性)- Slot DefaultSlot(蒙太奇插槽)- Control Rig(程序化 IK ,见下文,在 IsFalling = False 时启用,防止双腿在空中出现拉伸。
2.3 核心实现:移动层状态机 - Locomotion State Machine:
Locomotion State Machine 负责处理各个状态的具体切换逻辑(如下图),它利用 Conduit(别名/选择器)和 State Alias(入口) 构建了一个高响应度的复杂逻辑网。我们可以将整个状态机拆分为 地面、空中 与 落地决策 三个模块。
地面循环(图左侧):这是角色最基础的移动闭环,完全由 Distance Matching 驱动。
Idle(待机):状态机的核心原点;
Start(起步):当检测到有速度和加速度时进入,同时在没有加速度时进入 Stop(停步),在播放完成后进入 Cycle(跑动循环);
Cycle(跑动循环):循环动画,角色的跑动循环,在没有加速度时进入 Stop(停步);
Stop(停步):角色急停动画,使用 Distance Matching 进行停下来位置预测,并播放对应的急停动画(决定左脚停还是右脚停)。在 Idle 状态下只有加速度但没有速度时会进入,同时播放完后会返回 Idle 状态,若有速度和加速度,则会返回 Start 状态;
Pivot(折返):用于处理角色急剧改变移动方向时的物理惯性表现。当速度与加速度的点乘小于0时(即速度向量与加速度向量夹角大于 90 度,呈反向趋势)时,从 Start 或 Cycle 状态 (PivotSources) 进入 Pivot 状态。Pivot 状态可被打断,当没有加速度时会打断并进入 Stop 状态,当动画中配置的 ANS_ToLocomotion (Anim Notify State) 激活时,或通过监测 IsMovingPerpendicularToInitialPivot 为 True 时,即玩家在折返过程中突然输入了侧向指令时(如从前到后过程中突然按了右),会混合回 Cycle 循环。
空中循环(中间部分):这里负责处理角色的跳跃部分。为了实现更细腻且便于后期扩展的跳跃手感与高度,将起跳至落地后的过程拆分为了五个 State 。
JumpSources(别名):统一管理所有进入跳跃状态的条件,包括 Idle 、Start 、Cycle 、Stop 、Pivot 、DodgeFallToIdle ;
JumpSelector(选择器):一个 Conduit 节点(逻辑选择),根据当前的角色状态决定进入哪一个 State,如果 IsJumping = True(角色 Z 轴速度大于 0)时会进入 Jump Start ,如果 IsFalling = True(角色 Z 轴速度小于 0)则会进入 JumpApex;
JumpStart & JumpStartLoop:负责起跳后的上升阶段,分别是蹬地起跳动作和上升阶段的循环;
JumpApex(跳跃顶点):这是一个很短的过渡状态,用于表现重力反转时的失重感,从细节上提升了跳跃的真实感。判断方式是通过 Z 轴速度的负数除以重力,即“如果不受干扰,还要多久垂直速度会变成 0”,得到 JumpApexTime (Float) ,当这个值小于 0.4 秒(提前量设计,为动画混合预留时间)时进入这个 State ;
FallLoop:空中下落动画循环;
FallLand(落地接地):落地接地卸力动画,当角色距离地面的距离 GroundDistance 小于 200 时进入,并在播放结束且角色 IsOnGround = True 时退出,进入 EndInAir (Conduit) 。
落地决策与特殊状态(图右侧):这里负责处理角色落地一瞬间及落地后的状态选择,通过 EndInAir (Conduit) 进行分流决策:
EndInAir(落地选择器):当 IsOnGround = True 时,通过 FallLand 或 EndInAirSources(包含 JumpStart 、JumpStartLoop 、JumpApex 、FallLoop 、FallLand )进入,并根据条件决定接下来进入哪个状态;
CycleAlias:即 Cycle 状态,当 IsOnGround = True 且角色有速度与加速度时进入,返回地面 Cycle 状态;
IdleAlias:即 Idle 状态,当 IsOnGround = True 且角色没有任何速度与加速度时进入,返回地面 Idle 状态;
DodgeFallToIdle(空中闪避后的落地):这是本项目为了处理高动量落地情况而设计的特有状态,旨在通过视觉表现消除空中闪避落地后的物理惯性。当角色落地时,若系统检测到角色仍保留着闪避带来的巨大水平动量(有速度)但玩家并未输入任何移动指令时(无加速度),为了避免直接进入普通 Land 状态导致角色出现溜冰一样的平移滑步,会进入该状态,触发急停滑步动画。通过利用动画本身的位移自然匹配残余动量,从而实现从激烈的动态平滑过渡回静态 Idle 的效果。该状态具备高响应性,若在播放期间检测到新的加速度输入,将立即打断并进入 Cycle 状态,否则会在动画结束后自动返回 Idle 状态。
2. 分层动画接口 - Linked Anim Layers:
为了解决传统动画蓝图的高耦合问题,Lyra 引入了 Linked Anim Layers ,实现了逻辑容器与具体表现的分离,这样做主要有模块解耦和高扩展性两个好处;
ABP_CyberSkaterBase 的 AnimGraph 本质上是一个分层状态机,它并不直接包含动作序列,而是通过 Linked Anim Layer 节点调用子图层。
2.4 扩展实现与 Distance Matching:逻辑驱动层 - ABP_ItemLayerBase:
ABP_ItemLayerBase 是动画系统中的核心逻辑基类,它利用面向对象的继承特性,封装了所有与 Distance Matching 相关的底层算法。
如图所示,该类包含了一系列 Setup 和 Update 函数,这些函数直接绑定在状态机内部的 Sequence Evaluator 节点上,实现了对动画播放进度的精确物理控制。
Setup 函数:绑定于状态机的 Entry 节点,它根据当前的角色状态(如速度方向)选择正确的动画资产 (Sequence) ,并将 Sequence Evaluator 的显式时间重置为初始值(一般情况下为 0.0),为后续的计算做好准备。
Update 函数:绑定于状态机的 Update 节点,负责实时驱动。它在每一帧计算角色实际的物理位移或预测目标点,通过数学算法算出当前动画应该播放到哪一帧 (Explicit Time) ,从而强制动画进度与世界的物理移动距离同步。
通过 Setup 的零点重置和 Update 的实时预测,ABP_ItemLayerBase 实现了“物理决定位移,位移驱动动画”的闭环。无论地面摩擦力如何变化,这套逻辑都能保证角色的脚底不会出现滑动,实现了高精度的运动表现。
2.4.1 案例解析:急停 (Stop) 逻辑的具体实现:
以 急停 (Stop) 状态为例,这是 Distance Matching 最典型的应用场景。为解决玩家松开键盘后,角色向前滑行距离与动画停步距离不匹配的滑步问题,我实现了基于物理预测的距离匹配。
状态初始化 Setup Stop Anim 函数(上方图):当状态机检测到角色速度降为0或停止输入时,系统自动调用 Setup Stop Anim 函数,该函数执行了两个关键步骤,首先先将当前的动画节点强制转换为 Sequence Evaluator,即取消自动播放动画,由代码接管动画播放的进度;接下来重置时间 (Set Explicit Time) ,将动画播放进度强制重置为 0.0 秒,确保每一次急停动作都从动画的第一帧开始播放,为后续的距离计算提供一个确定的 T=0, Distance = 0 的起始参照点。
实时帧驱动 Update 函数:初始化完成后,系统在每一帧调用 Update Stop Anim 函数,利用 Distance Matching 实时修正动画进度。逻辑流程如下:
1. 物理预测 (Predict Ground Movement Stop Location) :如下图左上部分所示,核心节点是 Predict Ground Movement Stop Location 。该节点通过读取 CharacterMovementComponent 中的物理参数(如当前速度、摩擦力 Friction 、刹车减速度 BrakingDeceleration )等,利用物理公式算出按照当前的地面摩擦力,角色自然滑行停止的最终位置。
2. 距离计算 (Stop Location) :计算当前角色位置与预测停止位置间的距离,得出 Stop Location(距离目标的剩余距离),并每帧更新。
3. 距离匹配 (Distance Matching) :如图右侧所示,数据最终会传入 Distance Match to Target 节点,该节点会查询急停动画中的根节点位移的距离曲线 (Distance Curve) ,并将传入的距离与距离曲线中对应距离的动画帧匹配,以确定从哪帧开始播放动画。(例:如果物理计算显示角色还需要滑行 1.5 米才能停下,该节点就会强制将动画跳转到距离动画结束还有 1.5 米的那一帧。)
4. 边界处理 (Branch):图中的两个 Branch 节点提供了保护机制,如果不需要距离匹配,或物理预测失败(如停步距离不存在或极短),则会自动回退到 Advance Time(普通时间推进节点),防止动画卡死,确保持续的视觉反馈。
2.5 案例解析:急停 (Stop) 逻辑的具体实现:
持续更新中:2025-12-21
2.1 动画图层接口定义 - Anim Layer Interface:
为实现逻辑与表现的解耦,首先要定义 Anim Layer Interface(动画图层接口)。在项目中,ALI 不是简单的上下半身分层,而是采用了更符合动作游戏需求的 “基于状态的细粒度分层 (State-Based Layering)” 模式。
模板规范:在 ALI_AnimLayerInterface 中定义了动画系统的标准插槽,这些插槽本质上是一种“模板”和“规范”,它们不包含具体动画资源,而是定义各个子蓝图里有哪些 状态插槽 (State Slots) ,并通过主蓝图对这些状态的逻辑声明实现动态调用;
具体插槽定义:接口涵盖了完整的运动周期,确保了对动作混合的精确控制。包括地面运动: Idle(待机)、Start(起步)、Cycle(跑动循环)、Stop(急停)、Pivot(折返),空中运动:Jump Start(起跳爆发)、Jump Start Loop(起跳上升循环)、Jump Apex(跳跃顶点重力反转瞬间)、Fall Loop(下落循环)、Fall Land(下落接地)、Dodge Fall to Idle(空中闪避后的落地缓冲/恢复);
动态调用机制: 主动画蓝图 (ABP_CyberSkaterBase) 负责维护核心状态机的跳转逻辑(判断应该在满足什么条件时切换到什么状态),而具体动画数据则通过接口委托给子蓝图 (Linked Anim Layers) 中执行(播对应子蓝图中的对应动作),从而实现逻辑判断与动作资源的解耦。
以主角 AnimBP 为例,说明 Lyra 架构与 Distance Matching 在项目中的实现
基于动画通知状态的射线检测 (ANS-based Trace):
静态网格体设置:实现 ComboGraphCollision 的碰撞检测,首先需要在武器的模型 (Static Mesh) 上添加数个 Socket(插槽),这一步用以定义每个检测点的位置(如上图);
动画配置:在攻击动画序列 (Animation Sequence) 或蒙太奇 (Montage) 的关键帧轨道上,添加一个 AnimNotifyState (ANS),即上文中的 Collision Window。起始点 (Notify Begin) 为攻击动作开始造成威胁的那帧,结束点 (Notify End) 为挥剑动作结束的那一帧。这一步定义了攻击的判定窗口;
运行时逻辑:当攻击执行,动画播到 Collision Window ANS 的起始点时,组件开始工作。它会基于上一帧与当前帧的 Socket 位置差进行形状扫掠(支持 Line, Sphere, Box 或 Capsule),这能填补帧与帧间的空隙,形成一个连续、严密的扇面判定轨迹,从而完美解决低帧率下的穿模问题;
判定可视化:如左图所示,紫色线框展示了攻击挥舞轨迹形成的连续扇面判定区域,当该区域与敌方碰撞体重叠时,绿色标记即为计算出的实际命中点 (Impact Point) 。
4. 玩家侧打击感表现的实现:
在玩家角色收到 Event.Montage 信号后,会同时执行 Gameplay Cues,用以执行玩家侧打击感表现;
在项目中分别为受击溅血特效、火花特效、击中点反馈特效和角色侧的打中音效;
这样写的好处是可以在 Combo Graph 中直观的根据每段不同的攻击动画,配置不同的玩家侧表现。
以近战攻击为例,说明索敌、受击判定与表现在项目中的实现
5. 在 GA_Dodge 执行时的 Tag 管理:
在 Gameplay Ability 中可以进行 Tag 的配置;
GA_Dodge 能力激活后,玩家会被添加上 Ability.Character.Dodge 的 Tag,在 GA 逻辑结束后会自动消失;
在 Block Abilities with Tag 中可以配置这个 GA 激活期间禁止哪些 Tag (GA) 的执行。图中为:在角色闪避期间,角色不可以执行所有的 Ability(近战、远程攻击,闪避),同时也不可以执行 Event.Character.Jump(跳跃)。