敌人 AI 与 EQS
本章介绍敌人 AI 的三层解耦架构(感知/逻辑/表现),并以近战敌人为完整案例,演示从感知触发到行为树决策再到攻击执行的全流程。
6.1 敌人 AI 架构体系:控制器与基类的模块化解耦
Section titled “6.1 敌人 AI 架构体系:控制器与基类的模块化解耦”在构建动态且具有扩展性的 AI 系统时,逻辑与表现的分离是工业化开发的核心。项目通过 UE 经典的大脑(Controller)-宿主(Pawn/Character)架构,实现了敌人 AI 逻辑的解耦。

| 层级 | 资产 | 职责 |
|---|---|---|
| 核心基类 | BP_Enemy_Base / AIC_Enemy_Base | 敌人基本框架 |
| 逻辑层 | BT_Enemy_Melee + 3 棵子树(Frozen / Investigating / Passive) | 决策的分层管理 |
| 工具层 | Tasks (BTT) · Decorators (BTD) · EQS | 行为树的”子行为”积木 |
总体架构:感知 / 逻辑 / 表现三层解耦
| 层 | 载体 | 职责 |
|---|---|---|
| 感知层 | AIC_Enemy_Base | 挂载 AI Perception(视觉/听觉/伤害),启动行为树,初始化黑板 |
| 逻辑层 | Behavior Tree & Blackboard | BB 存储记忆(AttackTarget/State),BT 根据状态变量决定行为 |
| 表现层 | BP_Enemy_Base | 存储属性/资源,实现 BPI_EnemyAI 接口,执行动作指令 |
6.2 以近战敌人为例,说明 AI 全流程在项目中的实现
Section titled “6.2 以近战敌人为例,说明 AI 全流程在项目中的实现”敌人 AI 完整数据流:
感知刺激 → AIC_Enemy_Base (Perception) → 更新黑板 (AttackTarget / State) → BT_Enemy_Melee (决策) → BTT/BTD (执行) → BP_Enemy_Melee (表现)
6.2.1 资源及表现层:敌人 Pawn 类与资源属性载体(BP_Enemy_Base / Melee)
Section titled “6.2.1 资源及表现层:敌人 Pawn 类与资源属性载体(BP_Enemy_Base / Melee)”在 AI 系统的解耦架构中,Pawn 类(角色蓝图)是 AI 的身体,它不负责决策,而是作为资源和属性的载体,负责执行逻辑层的指令并存储表现所需的资源和逻辑。
BP_Enemy_Base 确立了所有敌人的底层框架,定义了”什么是敌人”及它们应该具备哪些基础的、共有的能力:

- 核心组件:GAS 核心(确保敌人具备与玩家一致的战斗逻辑底层,共用 Attribute Set 数据表)、UI 与反馈(WB_EnemyHealthBar 实时呈现血量状态)、ComboGraphCollision(处理战斗中的攻击碰撞判定);
- 接口(Interfaces):实现 BPI_EnemyAI 蓝图接口,向 AI Controller 声明自己能够接收指令(如装备武器、设置移动速度、激活 ComboGraph 等),从而实现低耦合的接口通信;
- 变量配置:预留了 PatrolRoute(巡逻路径引用,关联 BP_PatrolRoute)和 BehaviorTree(行为树资产引用)的变量,支持在关卡编辑器中为不同位置的敌人实例单独配置巡逻路线和行为逻辑。
BP_Enemy_Melee 作为 BP_Enemy_Base 的子类,专门负责填充近战敌人特有的资产和视觉表现逻辑:

- 初始化流程:在 BeginPlay 中先后执行武器挂载(BP_EnemyWeapon_DualSword 绑定到手部 Socket)、ComboGraphCollision 碰撞处理、血条 Widget 创建和玩法部分的掉落物概率计算;
- 视觉表现层:加入了死亡动态溶解效果(DynamicEnemyDissolveMat,通过动态材质实例驱动溶解参数)、生成时随机材质选择(SetSwordMaterial,从预设材质数组中随机赋值),低成本增加敌人视觉多样性并丰富表现;

- 战斗状态管理:声明了战斗状态变量,包括标记是否已进入战斗状态(用于触发拔刀等一次性逻辑)和 Boss 的特殊逻辑开关。
6.2.2 感知层:感知驱动与外界信息获取(AIC_Enemy_Base)
Section titled “6.2.2 感知层:感知驱动与外界信息获取(AIC_Enemy_Base)”AIC_Enemy_Base 是所有敌人 AI 的控制器基类,作为 AI 系统的”大脑”,它承担了两个核心职责:环境感知与行为启动。
AI Perception Component 配置:

控制器挂载了 AI Perception Component,配置了三种感知类型以覆盖不同的战斗场景:
- AI Sight(视觉感知):负责检测视野范围内的玩家。配置了感知半径、视野角度和失去视线后的记忆持续时间。这是敌人从 Passive 状态进入 Attacking 状态的主要触发源;
- AI Hearing(听觉感知):负责检测玩家产生的声音刺激(如攻击音效、脚步声)。当敌人尚未看见玩家但听到声响时,会进入 Investigating 状态前往声源位置调查;
- AI Damage(伤害感知):负责检测来自玩家的伤害事件。即使敌人背对玩家,受到攻击后也会立即感知到威胁源并切换至战斗状态,确保”偷袭”后敌人不会无动于衷。
感知事件处理 - On Target Perception Updated:
当任意感知通道检测到刺激源时,系统会触发 On Target Perception Updated 事件回调。在该回调中执行以下逻辑:

-
感知类型判定:通过 E_AISenses 枚举(None、Sight、Damage)识别本次感知的触发来源,以决定后续的状态切换策略;
-
黑板变量更新:将感知到的目标 Actor 写入黑板的
AttackTarget变量中,同时根据感知类型更新PointOfInterest(兴趣点,用于 Investigating 状态的移动目标); -
AI 状态切换:根据感知类型更新黑板中的 E_AIStates 状态变量:
- Sight 感知:直接将状态设为
Attacking,敌人立即进入战斗; - Damage 感知:将状态设为
Attacking,同时将伤害来源设为 AttackTarget; - Hearing 感知:将状态设为
Investigating,敌人前往声源位置调查; - 失去感知(Unregister):当目标离开感知范围后,根据当前上下文决定是进入
Investigating(前往最后已知位置搜索)还是回退到Passive。
- Sight 感知:直接将状态设为
行为树的启动与黑板初始化:
在 AIC_Enemy_Base 的 On Possess 事件中(即控制器接管 Pawn 的瞬间),执行以下初始化流程:
- 从 BP_Enemy_Base 中读取预配置的 BehaviorTree 资产引用,调用 Run Behavior Tree 启动行为树;
- 初始化黑板(BB_Enemy_Base)中的关键变量,包括
State(初始为 Passive)、AttackTarget(初始为空)、DefendRadius(理想攻击距离)、PatrolRoute(从 Pawn 上读取的巡逻路径引用)等。
6.2.3 决策层:决策中枢与记忆存储(BT_Enemy_Melee & BB_Enemy_Base)
Section titled “6.2.3 决策层:决策中枢与记忆存储(BT_Enemy_Melee & BB_Enemy_Base)”黑板 BB_Enemy_Base - 记忆与共享数据:
黑板是行为树的”短期记忆”,存储了 AI 在运行时需要读写的所有关键数据。项目中的核心黑板变量包括:
| 变量名 | 类型 | 作用 |
|---|---|---|
State | E_AIStates (Enum) | 当前 AI 状态,驱动行为树顶层分支选择 |
AttackTarget | Object (Actor) | 当前锁定的攻击目标(玩家) |
PointOfInterest | Vector | 兴趣点坐标(调查目标位置/EQS 查询结果) |
DefendRadius | Float | 理想攻击距离,用于判断是否需要接近目标 |
PatrolRoute | Object | 巡逻路径引用,关联 BP_PatrolRoute |
E_AIStates 状态枚举:定义了敌人的五种行为状态,分别是 Passive(被动巡逻)、Attacking(战斗攻击)、BeingHit(受击僵硬)、Investigating(调查搜索)和 Dead(死亡)。该枚举是整个行为树分支选择的根本依据。

行为树 BT_Enemy_Melee - 决策主干:
BT_Enemy_Melee 采用顶层 Selector 节点的经典优先级架构,从左到右(优先级从高到低)分为四个核心分支。每个分支通过 Blackboard Decorator 检查 State 变量的值来决定是否激活:
Root (Selector)├── [1] BeingHit State(受击僵硬) ← State == BeingHit → SubTree_Frozen├── [2] Combat State(战斗状态) ← State == Attacking → 战斗逻辑序列├── [3] Investigating State(调查) ← State == Investigating → SubTree_Investigating└── [4] Passive State(被动巡逻) ← State == Passive → SubTree_Passive这种优先级设计确保了:受击反馈拥有最高响应优先级(打断一切行为),战斗逻辑次之,调查行为再次,巡逻为最低优先级的默认行为。

⚡ [1] BeingHit(受击僵硬)
Section titled “⚡ [1] BeingHit(受击僵硬)”当敌人被攻击命中,GAS 通过 GE_HitReaction_Melee 添加 Event.Character.BeingHit.Melee Tag,感知层将 State 切换为 BeingHit,行为树立即跳转至此分支。
SubTree_Frozen 内部流程:
| 步骤 | Task | 作用 |
|---|---|---|
| 1 | BTT_RotateToPlayer | 受击瞬间强制面向玩家,确保受击方向一致 |
| 2 | BTT_ClearFocus | 清除 Focus,防止僵硬期间不自然的头部追踪 |
| 3 | BTT_SetMovementSpeed(Idle) | 移速设为 0,确保受击不滑动 |
| 4 | Wait | 等待硬直恢复(匹配受击动画时长) |
等待结束后 State 重新设回 Attacking,形成 受击 → 硬直 → 恢复战斗 的完整闭环。

⚔️ [2] Combat(战斗状态)
Section titled “⚔️ [2] Combat(战斗状态)”整个行为树中最复杂的分支,拆分为三个子阶段:

阶段 A — 装备检查:BTD_IsEnemyHasSwords 判断是否已装备。若未装备则 BTT_FocusTarget → BTT_EquipWeapon(拔刀),仅首次进入战斗触发。
阶段 B — 攻击执行(Sequence):
| 步骤 | Task | 作用 |
|---|---|---|
| 1 | BTT_SetMovementSpeed(Run) | 奔跑接近目标 |
| 2 | BTT_ClearFocus | 临时清除 Focus,避免奔跑时锁定不自然 |
| 3 | BTT_MoveToIdealRange | 移动至 DefendRadius 定义的理想攻击距离 |
| 4 | BTT_FocusTarget | 重新锁定玩家 |
| 5 | BTT_ActiveComboGraph | 通过 BPI_EnemyAI 接口激活 CG_Enemy_Combo |

阶段 C — 战术走位(Strafe Selector):
| 条件 | 行为 |
|---|---|
| 超出理想距离 | ClearFocus → SetSpeed(Run) → MoveToIdealRange(重新接近) |
| 在理想距离内 | FocusTarget → SetSpeed(Walk) → EQS_Strafe 查询 → Move to PointOfInterest(围绕玩家游走) |
🔍 [3] Investigating(调查)
Section titled “🔍 [3] Investigating(调查)”敌人通过 AI Hearing 感知到声音但未看到玩家时进入。
- Move to PointOfInterest — 移动至声源世界坐标
- Wait — 到达后原地等待,模拟搜索环视
- BTT_SetStateAsPassive — 若无新感知事件,State 设回
Passive,恢复巡逻

🚶 [4] Passive(巡逻)
Section titled “🚶 [4] Passive(巡逻)”敌人的默认空闲行为。
通过 BTD_HasPatrolRoute Decorator 判断 PatrolRoute 是否有效:
| 判断结果 | 行为 |
|---|---|
| 有巡逻路径 | BTT_MoveAlongPatrolRoute — 沿 BP_PatrolRoute 的 Spline 路径循环移动 |
| 无巡逻路径 | 原地 Idle 等待,作为定点守卫 |
BP_PatrolRoute 通过在关卡中放置 Spline 点定义路径,支持不同敌人绑定不同路线。


6.2.4 执行层:任务节点与条件判断(BTT & BTD)
Section titled “6.2.4 执行层:任务节点与条件判断(BTT & BTD)”行为树的叶节点由自定义的 BTTask(任务节点)和 BTDecorator(条件装饰器)构成。它们是整个 AI 系统的”原子操作”——每个节点只做一件事,通过组合实现复杂行为。
自定义任务节点(BTTask)清单:
| 任务节点 | 职责 | 关键实现 |
|---|---|---|
| BTT_FocusTarget | 锁定注视目标 | 调用 AI Controller 的 Set Focus,使敌人持续面朝 AttackTarget |
| BTT_ClearFocus | 清除注视锁定 | 调用 Clear Focus,解除头部/身体的强制追踪 |
| BTT_SetMovementSpeed | 设置移动速度 | 通过 BPI_EnemyAI 接口设置 E_MovementSpeed(Idle / Walk / Run) |
| BTT_MoveToIdealRange | 移动至理想距离 | 使用 Move To 节点接近 AttackTarget,到达 DefendRadius 时完成 |
| BTT_EquipWeapon | 装备武器 | 通过 BPI_EnemyAI 接口触发拔刀蒙太奇,将武器从背部 Socket 切换到手部 Socket |
| BTT_RotateToPlayer | 转向玩家 | 强制旋转 Pawn 朝向 AttackTarget,用于受击瞬间的方向校正 |
| BTT_ActiveComboGraph | 激活攻击连招 | 通过 BPI_EnemyAI 接口激活 CG_Enemy_Combo,执行攻击动画及 GAS 伤害逻辑 |
| BTT_MoveAlongPatrolRoute | 沿路径巡逻 | 读取 PatrolRoute 中的路径点,依次移动并循环 |
| BTT_SetStateAsPassive | 重置为被动状态 | 将黑板 State 设为 Passive,用于调查超时后的状态回退 |
| BTT_DefaultAttack | 默认攻击(备用) | 简化版攻击逻辑,作为无 ComboGraph 时的回退方案 |
自定义条件装饰器(BTDecorator)清单:
| 装饰器节点 | 职责 | 判断逻辑 |
|---|---|---|
| BTD_HasPatrolRoute | 是否有巡逻路径 | 检查黑板 PatrolRoute 变量是否有效(非 null) |
| BTD_IsEnemyHasSwords | 是否已装备武器 | 通过 BPI_EnemyAI 接口查询敌人的武器挂载状态 |
| BTD_IsWithinIdealRange | 是否在理想攻击距离内 | 计算敌人与 AttackTarget 的距离,与黑板 DefendRadius 比较 |


6.3 空间智能:环境查询系统(EQS)的原理与实践
Section titled “6.3 空间智能:环境查询系统(EQS)的原理与实践”6.3.1 什么是 EQS?
Section titled “6.3.1 什么是 EQS?”环境查询系统(Environment Query System,EQS)是 UE 提供的一套空间推理工具,它允许 AI 在世界中生成一组候选位置点,并通过一系列可配置的测试(Test)对这些点进行评分和排序,最终选出”最优位置”供行为树使用。
在本项目中,EQS 主要用于解决敌人的战术走位问题——攻击完成后,敌人需要选择一个合理的位置进行游走或重新走位,而不是呆板地站在原地等待下一次攻击。
6.3.2 EQS_Strafe 的具体实现
Section titled “6.3.2 EQS_Strafe 的具体实现”查询上下文 - EQS_Context_AttackTarget:
在执行 EQS 查询前,首先需要定义查询的参考点。EQS_Context_AttackTarget 是一个自定义的 EQS Context,它从行为树的黑板中读取 AttackTarget 变量(即玩家),并将其世界坐标作为生成器的中心参考点。
生成器 - Points: Circle(环形点生成):
EQS_Strafe 使用 Points: Circle 生成器,以 EQS_Context_AttackTarget(玩家位置)为圆心,在半径 400 的圆周上均匀生成 5 个候选位置点。这些点构成了敌人可能的走位落点。
选择环形生成器而非网格生成器的原因是:近战敌人的战术走位本质上是围绕玩家做弧形运动,环形点天然契合这种运动模式,且只需 5 个点即可覆盖玩家周围的主要方向,查询开销极低。

测试配置(Tests):
生成器产生候选点后,通过以下两个 Test 进行评分筛选:
-
Distance Test(距离测试):评估每个候选点与 Querier(敌人自身)之间的距离;配置筛选范围为 150 ~ 500,即过近(< 150)或过远(> 500)的点会被直接剔除。这确保敌人不会走位到离玩家太近(容易被连招覆盖)或太远(脱离战斗节奏)的位置。
-
Path Exists Test(路径可达性测试):从 Querier(敌人自身)到候选点执行导航路径检测;剔除所有无法通过 NavMesh 到达的点(如被墙壁、障碍物阻挡的位置)。这是一个硬性筛选条件,确保敌人不会尝试移动到物理上不可达的位置,避免 AI 卡在障碍物上。
查询结果的使用:
EQS 查询返回的最优位置点会被写入黑板的 PointOfInterest 变量。随后,行为树的 Combat State 中的 Strafe 分支会执行 Move to PointOfInterest,驱动敌人移动至该点。整个流程形成了:攻击 → EQS 查询最优走位点 → 移动至走位点 → 重新评估距离 → 再次攻击的战术循环。


6.3.3 EQS 在项目中的设计考量
Section titled “6.3.3 EQS 在项目中的设计考量”为什么不直接随机移动?
简单的随机移动会导致两个问题:敌人可能移动到墙壁后方或不可达区域导致卡死,或者移动到离玩家极远的位置破坏战斗节奏。EQS 通过 Distance Test 和 Path Exists Test 的双重约束,确保走位点始终在合理的战斗范围内且物理可达。
为什么只用 5 个点?
EQS 查询存在性能开销,候选点越多,每帧的测试计算量越大。对于近战敌人的走位需求,5 个等距环形点已足够覆盖玩家周围的前、后、左、右及侧后方,在保证战术多样性的前提下将查询开销降到最低。
后续扩展方向:
- Dot Product Test(方向偏好):加入与敌人面朝方向的点乘测试,使敌人倾向于选择侧面或背面的走位点,模拟”包抄”战术;
- 多敌人协同:当场景中存在多个敌人时,加入 Distance to Other Queriers 测试,使不同敌人之间保持距离,避免扎堆站在同一位置,实现群体 AI 的基础分散站位。