“平A”这个看似简单的动作,在 UE5 蓝图里落地时会牵扯出一连串问题:怎么触发攻击?怎么限制连击节奏?动画播到哪一帧开始判定伤害?如果只在角色蓝图里随便连一个 Play Montage 节点,后续加连击、加特写、加命中特效时,蓝图逻辑会迅速变成一团乱麻。
本文将围绕 BP_SystemAttack 攻击蓝图,从 UE5 新手的视角,系统拆解近战平A攻击逻辑的完整编写过程。这是“上篇”,重点解决攻击入口、状态控制、蒙太奇播放、动画通知触发,以及基础的伤害判定思路。学完你能理解 UE5 动作攻击蓝图的骨架,也能跟着步骤在第三人称模板上做出一个可玩的近战平A Demo。
1. 为什么要单独做一个攻击蓝图
很多初学者做攻击功能时,习惯直接把“键盘按下”和“播放动画”连在一起,跑起来好像没问题,但一旦加入连击、后摇、攻击位移、命中检测,就会遇到几个躲不开的问题:
- 攻击状态与其他行为(跑、跳、受到伤害)互相打断。
- 动画还没播完,玩家再次按攻击键,动画被强行重置。
- 判定伤害的时机很难和动画帧对齐。
- 后续加特效、音效、受击反馈时,逻辑散落在各个蓝图里。
BP_SystemAttack 的价值,就是把这些零散节点组织成一套“可复用的攻击状态机”。它不只是一个蓝图,而是一种处理攻击流程的思路:输入处理、状态判断、动画播放、通知事件、伤害结算各司其职。
本文实现的框架如下:
玩家输入(左键) -> 攻击状态锁判断 -> 连击计数更新 -> 播放攻击蒙太奇 -> 动画通知(Notify)触发判定事件 -> 碰撞查询,找到目标 -> ApplyDamage 造成伤害这个链路里,“上篇”会写到动画通知触发为止,同时把伤害判定的入口和关键节点讲清楚,让下篇能够直接在此基础上扩展。
2. 环境准备与项目设定
2.1 引擎版本说明
本教程基于 UE5 蓝图系统编写。不同 UE5 小版本之间,部分节点名称、菜单位置会有差异,例如 UE5.1 之后第三人称模板默认使用增强输入(Enhanced Input),而旧项目可能还在使用传统输入轴映射。
本文示例以 UE5 通用方案为准,不绑定某个具体小版本。如果你打开项目设置时看到的界面和本文描述不一致,优先检查项目是否开启了 Enhanced Input 插件,或者按你自己的工程版本调整输入处理方式。
2.2 推荐项目模板与资产
建议从第三人称模板创建工程,这样可以直接使用默认的 ThirdPersonCharacter 和 Mannequin 骨骼网格体,省去自己搭建角色的时间。如果你有自己的角色资产,也可以替换。
攻击动画方面,示例中会用到一段近战挥砍动画和配套的蒙太奇资产。你可以使用商店资源、自己制作的动画,也可以用引擎自带动画重定向后凑合。本文重点是蓝图逻辑,动画资源不要求很高品质,但建议至少保证:
- 有一段完整的单手挥砍动画,时长在 0.5 到 1 秒左右。
- 动画有明显的“攻击帧”,也就是武器挥到目标位置的那一帧,方便后面放置 Notify。
2.3 创建测试角色与基础场景
在内容浏览器中创建基于 Character 的蓝图类,命名BP_TestCharacter。如果你的工程没有自定义角色,也可以直接在第三人称模板的 BP_ThirdPersonCharacter 上加逻辑。
为了后续测试方便,场景里放一个用于攻击的目标。可以用引擎自带的 Cube 搭建一个假人,给它添加 StaticMesh 和碰撞体,后面通过蓝图打印伤害数值来验证攻击是否生效。
3. 近战平A攻击的核心概念
动手之前,先理解四个关键概念。很多新手写攻击蓝图失败,不是因为节点复杂,而是没搞清动画系统、碰撞系统和伤害系统分别负责什么。
3.1 攻击流程中的“三阶段”
一次完整近战攻击可以拆成三个阶段:
- 前摇阶段:角色从静止进入攻击动画,这个阶段没有伤害判定。
- 伤害判定阶段:动画播放到特定帧,武器挥出,此时执行碰撞检测。
- 后摇阶段:伤害判定结束,角色恢复待机。
前摇和后摇决定了攻击手感,伤害判定决定了攻击是否命中。蓝图里最难做的不是伤害数值,而是“如何让伤害判定严格对齐动画帧”。
3.2 动画蒙太奇与动画通知
动画蒙太奇(Anim Montage)是 UE5 中管理动画片段播放的容器。你可以把它理解为一段“动画脚本”,它能控制动画播放、暂停、跳转,还可以在动画时间线上的特定位置插入通知。
动画通知(AnimNotify)和通知状态(AnimNotifyState)是挂在动画序列或蒙太奇上的标记:
- AnimNotify:触发一次,类似事件瞬间。
- AnimNotifyState:触发开始和结束两个事件,适合“从武器抬起开始检测,到武器收回结束检测”这种区间逻辑。
近战攻击中,常用 AnimNotifyState 控制伤害检测的有效时间区间。更简单粗暴的做法是:在动画的“攻击帧”放一个 Notify,在动画结束前放一个关闭标记。本文上篇先用 Notify 的方式打开判定通道,方便理解事件流。
3.3 碰撞检测与伤害结算
碰撞检测负责回答“这次攻击打中了谁”,伤害结算负责回答“被击中者扣多少血”。两者在 UE5 蓝图中通常分开处理:
- 碰撞检测:使用 Sphere Overlap Actor 或 Sweep Single by Channel 等节点,从角色/武器位置向外检测。
- 伤害结算:使用 GameplayStatics 库的 ApplyDamage 节点,或直接调用目标身上的自定义伤害函数。
后面会专门演示一个最小可用的判定入口。
4. 输入与攻击状态锁
4.1 输入映射
在 UE5 中,攻击输入可以用传统 InputAction 或增强输入系统处理。以增强输入为例,你需要一个 IA_Attack 输入动作,并把它绑定到鼠标左键。
输入动作配置参考:
{ "InputActionName": "IA_Attack", "Trigger": "Pressed", "Bindings": [ { "Key": "LeftMouseButton", "Modifiers": [] } ] }具体操作路径:内容浏览器右键 -> 输入 -> 输入动作,创建IA_Attack,然后在项目设置 -> 输入 -> 默认玩家输入的映射中添加绑定。
如果你是旧版输入系统,可以在角色蓝图的 EventGraph 中直接使用InputAction Attack事件节点。两种方式最终的逻辑入口是一样的。
4.2 攻击状态锁
攻击状态锁是防止玩家在攻击动画播放期间重复起手的关键。通常使用一个布尔变量bIsAttacking表示角色是否正在攻击。
逻辑图如下:
输入事件触发 -> Branch -> 条件:bIsAttacking == false -> 执行攻击 -> bIsAttacking = true -> 如果为 true,直接忽略本次输入需要明确的是:bIsAttacking = true不能一直保持,否则角色会永远无法再次攻击。通常选择在动画蒙太奇结束时,通过OnCompleted事件把状态复位,或者用一个定时器延迟复位。
4.3 连击窗口与连击计数
平A不只打一下,往往需要支持“三连击”或“四连击”。这要求玩家在攻击后摇阶段再次按键时,能够进入下一段攻击,而不是被状态锁直接忽略。
常用方案是引入连击计数AttackCount和连击窗口:
- 玩家第一次攻击:AttackCount 从 0 变为 1,播放第一段动画。
- 动画播到后摇阶段时打开“连击窗口”
bCanCombo = true。 - 玩家在窗口内再次按攻击键:AttackCount + 1,播放第二段动画。
- 若超过窗口时间未按键,AttackCount 重置为 0,角色回到待机。
蓝图里可以使用定时器或 Delay 节点实现窗口。这里有一个重要技巧:Delay 节点在被再次触发时会取消之前的等待。简单说,玩家每次按下攻击键,都会把上一次的“重置计时器”冲掉并重新计时。这样玩家只要连续按键,计数就不会被中途重置。
4.4 攻击输入入口伪代码
为了更好理解 EventGraph 节点排列,下面用伪代码形式描述攻击入口逻辑:
Event Attack Input -> If (bIsAttacking == true) return -> If (bCanCombo == true) -> AttackCount += 1 -> else -> AttackCount = 1 -> 播放对应攻击蒙太奇 -> bIsAttacking = true -> 启动连击窗口计时器 (2秒后重置 AttackCount)这里的bCanCombo会在前一段攻击动画播放到某个时间点后由动画通知打开。下面来看动画通知如何接入蓝图。
5. 播放攻击蒙太奇与动画通知
5.1 创建攻击蒙太奇
在内容浏览器中选中你的挥砍动画,右键 -> 创建 -> 创建 AnimMontage(动画蒙太奇)。命名建议AM_Attack_01。
打开蒙太奇编辑器后,要处理两件事:
- 确认动画时长和循环设置正确。
- 在攻击帧位置添加 AnimNotify。
先不急着加太多通知,建议从最小闭环开始:加一个名为MeleeHit的通知,作为“这一帧开始判定伤害”的信号。
5.2 在蓝图里播放蒙太奇
角色蓝图中播放蒙太奇有两种常见方式:
- 从角色组件里找到网格体,调用
Play Anim Montage节点。 - 获取动画实例,在动画蓝图里调用播放入口。
角色蓝图中更简洁的是直接调用Play Anim Montage(英文界面节点名可能是Play Anim Montage或Play Montage,不同版本有差异)。节点输入参数包括:
- Montage to Play:要播放的蒙太奇资产。
- Play Rate:播放速度,默认 1.0。
- Start Section Name:起始段名,可以留空从默认段播放。
- Start Montage At(时间偏移):默认从零开始。
播放结束后,蒙太奇节点会有OnCompleted、OnBlendOut、OnInterrupted等输出事件,建议绑定一个自定义事件,在动画播完或被打断时统一处理状态复位。
5.3 处理动画通知事件
动画通知到蓝图之间的传递有两种常见做法。
第一种,直接在动画蓝图中创建事件。在动画蓝图事件图表中,添加对应通知名称的自定义事件,例如AnimNotify_MeleeHit。但这个事件发生在动画蓝图内部,要把信号传给角色蓝图,需要额外调用。
第二种,把通知事件广播给角色。在动画蓝图中收到AnimNotify_MeleeHit后,通过Try Get Pawn Owner获取角色引用,调用角色上的自定义事件MeleeAttackHit。这样角色蓝图内就可以统一处理伤害逻辑了。
这里有一个工程经验:尽量不要在动画蓝图中写大量伤害逻辑。动画蓝图专注于动画播放,攻击判定和伤害计算应该回到角色蓝图。动画蓝图里只做“转发信号”这一件事。
5.4 蒙太奇分组的说明
如果角色同时有上身攻击和下身走路动画,蒙太奇分组 Slot 的配置就很重要。默认情况下蒙太奇播放时会覆盖整个身体动画。
在更复杂的动作游戏中,通常会让攻击动画只播放上半身,下半身保留移动或蹲伏动画。此时需要在动画蓝图中添加 Slot 节点,把DefaultSlot和UpperBody之类的插槽区分开。
对于本文的简单平A来说,可以先不处理分组,整段播放。等后面做“移动攻击”时再引入分组这个概念。
5.5 完整事件流示例
下面是一个精简的动画通知转发参考写法:
动画蓝图 EventGraph: AnimNotify_MeleeHit -> Owner = Try Get Pawn Owner -> Cast To BP_TestCharacter -> 调用 BP_TestCharacter.MeleeAttackHit角色蓝图 EventGraph: MeleeAttackHit 自定义事件 -> 执行伤害检测与结算这样,角色蓝图拿到“攻击帧到了”的信号后,就可以开始做碰撞检测了。
6. 基础近战伤害判定与结算
6.1 选择碰撞检测方式
近战伤害判定常见有三种方案:
- 球形重叠检测
Sphere Overlap Actors:在角色前方生成一个球体,检测该球体半径内是否有可被攻击的对象。优点是实现快,适合演示。 - 射线检测
Line Trace Single:从武器起始点到指定方向打一条射线,适合刀剑挥砍的瞬时检测。 - 基于武器碰撞体的 Overlap 检测:在武器网格体上放置碰撞体,攻击时激活碰撞,并监听 Overlap。
新手阶段推荐先用Sphere Overlap Actors,因为节点参数直观,不容易踩碰撞预设的坑。下篇再讲更精细的武器碰撞方案。
6.2 设置忽略自身
用Sphere Overlap Actors时,有一个必须处理的点:自己也是一个 Actor,如果不忽略自己,武器一挥,角色自身的骨骼网格体也会被检测到。
节点Sphere Overlap Actors的Actors to Ignore参数需要把自己放进去。直接传入self即可。
如果需要忽略队友,可以按 Actor Tag、碰撞通道或蓝图接口进行过滤。过滤逻辑越早做,后面扩展越安全。
6.3 伤害结算接口
UE5 本身自带一套通用伤害接口,核心是ApplyDamage。它的参数包括:
- Damaged Actor:被伤害的目标。
- Base Damage:伤害数值。
- Event Instigator:伤害来源的 Controller。
- Damage Causer:伤害来源 Actor(通常是角色或武器)。
- Damage Type Class:伤害类型类,可留空。
在蓝图中,可以从 GameplayStatics 库拖出ApplyDamage节点。然后在目标的蓝图或通用 Actor 类中,覆写ReceiveAnyDamage事件,当该 Actor 被 ApplyDamage 击中时,ReceiveAnyDamage会自动触发。
这里给一个对应的 C++ 对比接口,帮助理解蓝图背后的概念:
// DamageIntensity.h UINTERFACE(MinimalAPI) class UDamageIntensity : public UInterface { GENERATED_BODY() }; class IDamageIntensity { GENERATED_BODY() public: UFUNCTION(BlueprintNativeEvent, Category = "Damage") void ReceiveMeleeDamage(float BaseDamage, AActor* DamageCauser); };蓝图版本则可以直接用ReceiveAnyDamage事件,不需要额外建接口。如果你希望自定义受击反馈,也可以在角色蓝图上写ReceiveMeleeDamage自定义事件,然后从攻击侧直接调用目标身上的这个自定义事件。
6.4 最小判定流程伪代码
MeleeAttackHit 事件 -> 获取角色前方向量 -> 设置检测球体中心 = ActorLocation + ForwardVector * 150 -> Sphere Overlap Actors -> Sphere Radius = 100 -> Actors to Ignore = [self] -> 遍历所有 Overlap Actors -> 如果是目标(可攻击角色 / 假人) -> ApplyDamage -> 打印命中对象名称这段逻辑完成之后,攻击时只要动画播放到MeleeHit通知帧,角色前方区域内的敌人就会被扣血。上篇的核心链路到这里已经完整了。
7. 常见问题排查与工程建议
7.1 常见问题表格
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
| 按攻击键没反应 | 输入映射未配置,或增强输入资产未添加到映射上下文 | 检查项目设置输入绑定;确认攻击按键的 Trigger 类型是 Pressed |
| 动画播放后角色卡死,无法再次攻击 | bIsAttacking 没有在动画结束时复位 | 绑定 Play Anim Montage 的 OnCompleted 事件,在事件里复位状态变量 |
| 攻击判定只在第一段动画生效 | 后续连击蒙太奇没有添加 MeleeHit 通知 | 为每一段攻击动画都添加对应通知,并统一命名为 MeleeHit 或按段区分 |
| 角色攻击时把自己打到了 | Sphere Overlap Actors 没有忽略自身 | 把 self 加入 Actors to Ignore 列表 |
| 动画通知事件没有触发 | 通知名称与蓝图事件名称不一致,或通知放在了错误的轨道 | 打开蒙太奇/动画序列,确认通知位置;核对动画通知事件命名 |
| 目标受到伤害,但不会死亡 | 目标蓝图没有血量变量或死亡逻辑 | 在目标 Actor 上定义血量,监听伤害事件并执行销毁/死亡动画 |
| 判定范围过大或过小 | SphereOverlap 半径参数和攻击动画不匹配 | 先打开可视化辅助调试,反复调整球体半径与中心偏移 |
7.2 蓝图布局建议
事件图表中节点一多,新手很容易迷失。建议把 EventGraph 分成几个区块,用注释框(Comment Box)圈起来:
- 输入处理区:所有输入事件入口。
- 状态控制区:攻击状态锁、连击计数、定时器。
- 动画播放区:播放蒙太奇和状态复位。
- 伤害判定区:MeleeAttackHit 之后的碰撞与 ApplyDamage。
模块之间的调用建议走自定义事件或蓝图接口,不要用一堆Cast To互相拖线。特别是角色蓝图和动画蓝图之间,保持单向调用关系:动画蓝图只发通知,角色蓝图统一处理结果。
7.3 调试技巧
在蓝图节点上点击右键可以添加断点。运行游戏后,攻击时观察断点是否命中,可以快速判断输入、动画通知、碰撞检测哪一段链路断了。
碰撞检测阶段建议先用Draw Debug Sphere可视化检测范围。这样你一眼就能看出球体的位置和半径到底对不对。Debug 节点在验证完后记得删除,否则打包后可能影响性能。
7.4 性能与安全注意
- Overlap 检测不要每帧执行,只在动画通知触发时才执行。
Actors to Ignore不要只填一个角色,要尽可能填上所有不参与判定的 Actor,减少遍历开销。- 伤害处理严格区分客户端与服务器。本文是单机演示,逻辑相对简单;如果是联机项目,伤害结算必须由服务器权威执行,客户端只负责表现。
- 修改血量、销毁 Actor 这类操作要谨慎。尤其涉及网络属性复制时,建议先在本地单机测试,再考虑多人同步。
7.5 命名与目录建议
一个攻击系统会涉及蒙太奇、动画通知、蓝图类、特效资产,项目资源目录尽量按模块划分,避免所有资产堆在根目录:
Content/ Characters/ Mannequin/ Animations/ Montages/ Notifies/ Blueprints/ Character/ System/ FX/ Niagara/蓝图命名也保持统一:
- 角色蓝图:BP_TestCharacter
- 攻击系统蓝图:BP_SystemAttack
- 攻击蒙太奇:AM_Attack_01、AM_Attack_02
- 通知命名:MeleeHit、MeleeComboWindowOpen、MeleeEnd
8. 后续扩展:Niagara 特效与完整战斗反馈
经过上面这些步骤,你的角色已经可以完成一次“平A -> 命中 -> 扣血”的最小闭环。这是战斗系统最核心的地基。
接下来可以继续扩展的方向有很多:
- 第二个、第三个连击段,以及不同段位的伤害倍率。
- 命中停顿(Hit Stop),在命中瞬间让动画短暂暂停,提升打击感。
- 伤害数字、飘血、命中闪白。
- 受击动画和死亡动画。
- Niagara 命中特效:在命中点生成刀光、血液飞溅、攻击拖尾。
其中 Niagara 特效与攻击判定的衔接方式通常是:命中检测拿到目标 Hit Result 或 Overlap 位置之后,在该位置Spawn System at Location播放 Niagara 特效。例如在MeleeAttackHit事件中,检测到目标后生成攻击特效系统。
与动画的配合上,武器拖尾通常作为 Niagara 组件附加在武器骨骼插槽上,在攻击前摇阶段隐藏,在攻击判定阶段显示,后摇或动画结束时隐藏。这部分的“显示/隐藏”时机,依然可以通过 AnimNotify 来控制。
所以不要急着把特效、音效都堆在上篇里。先把“输入 -> 动画 -> 检测 -> 伤害”这条主线跑通,你会发现后续接 Niagara、接音效都只是在这条链路上增加几个分支而已。
从工程经验来说,战斗系统最忌讳一开始就铺太开。建议一步一步来:今天先做出可玩的单段平A,明天再加连击,后天再加特效。Battle 系统的手感是“调”出来的,不是一次写出来的。
如果你在跟着本文操作时遇到了报错,先别急着改大逻辑,按第 7 节的排查表格逐项检查输入映射、通知名称、碰撞过滤三件事,大部分问题都出在这三个地方。把这套最小闭环跑顺了,再往后走的信心会完全不一样。