三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

UE5智能掩体系统:基于EQS实现AI动态战术寻路

UE5智能掩体系统:基于EQS实现AI动态战术寻路

1. 项目概述:为什么我们需要一个“聪明”的掩体系统?

在UE5里做AI战斗,最让人头疼的往往不是枪法有多准,而是AI的“走位”有多蠢。你肯定见过这样的场景:敌人像无头苍蝇一样乱跑,要么躲在根本挡不住子弹的薄木板后面,要么在空旷地带和掩体之间反复横跳,把后背完全暴露给你。这种“人工智障”的体验,瞬间就能让精心打磨的关卡设计和战斗节奏毁于一旦。传统的导航网格(NavMesh)加几个预设掩体点的方式,在静态环境下还能应付,一旦战场动态变化——比如掩体被炸毁、玩家位置快速移动、或者需要多个AI协同寻找交叉火力点——这套系统就立刻捉襟见肘了。

这正是“智能掩体系统”要解决的核心痛点。它不是一个简单的“找掩体”功能,而是一个让AI能像真人玩家一样,基于瞬息万变的战场环境,实时评估、决策并移动到最佳掩护位置的“大脑”。而UE5内置的环境查询系统,就是我们实现这个“大脑”最强大、最优雅的工具。EQS本质上是一个用于对环境进行采样、测试和评分的框架,它允许你定义一系列复杂的空间查询逻辑,比如“找到所有能遮挡来自玩家视线的位置”,然后根据距离、角度、安全性等多个维度进行加权打分,最终选出那个“最佳答案”。

这个项目,就是要带你从零开始,用EQS搭建一套真正能在动态战斗中派上用场的智能掩体系统。它不只是让AI“有地方躲”,更是让它们懂得“怎么躲更好”。接下来,我会拆解整个构建过程,从核心思路到每一个蓝图节点的细节,并分享那些在官方文档里找不到的实战踩坑经验。

2. 核心思路拆解:EQS如何为AI赋予“战术眼光”

在动手写第一行蓝图之前,我们必须先想清楚:一个“最佳掩护点”到底应该满足哪些条件?这直接决定了我们EQS查询的设计。盲目开始只会做出一团乱麻的逻辑。

2.1 定义“最佳掩护点”的评分维度

我们可以把AI寻找掩体想象成一个多目标优化问题。一个完美的掩体点需要在多个常常互相冲突的目标之间取得平衡:

  1. 安全性(遮挡性):这是掩体的第一要义。该点必须能有效阻挡来自主要威胁(通常是玩家)的射线攻击。这不仅仅是“在某个物体后面”,还要考虑掩体本身的高度、厚度以及AI模型的大小。
  2. 可达性:这个点必须位于导航网格上,并且AI能够以合理的路径抵达。一个再安全的点,如果被障碍物完全包围无法进入,也是无用的。
  3. 战术价值
    • 射击视野:躲起来是为了更好地反击。掩体点最好能提供一个对威胁方向良好的射击窗口(Peek Hole),而不是完全封死。
    • 距离威胁适中:离玩家太近容易被手雷或近战攻击,太远则失去攻击效力。需要有一个理想距离范围。
    • 侧翼安全:除了正面的玩家,还需要考虑是否暴露给其他可能存在的敌人。
    • 撤退路线:是否有安全的退路,以防掩体失效或被包抄。

EQS的强大之处在于,它允许我们通过组合多个测试来量化这些维度。每个测试会对查询到的每一个候选点给出一个标准化分数(通常是0到1),然后通过一个加权公式计算出总分。

2.2 EQS查询的宏观工作流程

一个典型的智能掩体EQS查询会遵循以下流程,这个过程会在AI的“行为树”中周期性触发(例如每秒一次或当受到威胁时):

  1. 生成候选点:首先,需要在AI周围一定半径内,生成一系列可能成为掩体的位置点。这可以通过Grid(网格)生成器在导航网格上撒点,或者用Context(上下文)生成器,比如所有已标记为“掩体”的Actor的位置。
  2. 执行一系列测试:对每一个候选点,执行我们定义好的测试电池。
    • Trace测试:从该点向威胁源(玩家)发射射线,检测中间是否有遮挡物。这是安全性的核心。
    • 距离测试:计算该点到威胁源的距离,并给出分数(例如,距离在10米到20米之间分数最高)。
    • Dot测试:计算“掩体点->AI”向量与“AI->威胁源”向量的点积。用于偏好位于AI侧方或后方的掩体(便于迂回),而非正对威胁方向的掩体。
    • 路径查找测试:计算从AI当前位置到该候选点的路径长度和复杂度,分数与路径效率成反比。
  3. 加权与评分:每个测试会输出一个分数。我们需要为“安全性”、“距离”、“路径成本”等分配不同的权重。例如,安全性权重最高(0.5),距离适中次之(0.3),路径成本较低(0.2)。然后将加权后的分数相加,得到每个点的最终得分。
  4. 选择最佳点:EQS查询会输出得分最高的那个点(或前几个点)的Location信息。AI的行为树将接收这个位置,并将其作为“移动至”指令的目标。

注意:权重配置没有黄金标准,它高度依赖于你的游戏类型。在一个快节奏的射击游戏中,安全性权重可能压倒一切;而在一个战术潜行游戏中,射击视野和撤退路线的权重可能会大大提高。这需要你在实际游戏中反复调试。

3. 构建环境查询:从蓝图到实战配置

理解了思路,我们进入UE5编辑器,开始实际构建这个EQS查询。我假设你已经创建了一个基本的AI角色和对应的行为树。

3.1 创建EQS查询资产

在内容浏览器中右键,选择“人工智能” -> “环境查询”。我将其命名为EQS_FindBestCover。双击打开后,你会看到EQS编辑界面。

3.2 配置生成器:我们去哪里找点?

首先需要确定候选点的来源。对于掩体系统,我强烈推荐使用“上下文(Context)” + “网格(Grid)”的组合方式,这比单纯用网格更高效、更精准。

  1. 添加生成器:在“生成器”面板,添加一个Points: Grid
  2. 设置网格参数
    • Grid Half Size: 这决定了搜索范围。例如,设置为(1000, 1000, 0),意味着在以AI为中心,X和Y轴各1000单位(厘米)的矩形区域内搜索。
    • Space Between: 网格点间距。例如200。间距越小,搜索越精细,但计算量也越大。对于掩体搜索,200-300是一个不错的起步值。
    • Offset: 网格中心的偏移。通常保持为(0,0,0),即以AI自身位置为中心。
  3. 关键一步:绑定导航网格限制:在Grid生成器的细节面板,找到Navigation Filter选项。这里必须设置一个导航过滤器,并勾选Project to Navigation。这个操作至关重要,它确保生成的网格点会自动投影到最近的导航网格上,筛除掉那些在桌子、屋顶等不可行走区域的无效点。忽略这一步,你的AI可能会试图飞檐走壁。

3.3 设计测试电池:给每个点打分

这是EQS查询的核心。我们依次添加并配置测试项。

3.3.1 测试一:射线遮挡测试(核心安全性)

这是最重要的测试,用于判断候选点是否真的能挡住子弹。

  1. 添加一个Trace测试。
  2. 配置细节:
    • Trace From:设置为Querier(查询者),即AI自己。这意味着测试会模拟“从AI的位置看向候选点”。
    • Trace To:设置为Context->Item。即向每个候选点发射射线。
    • Navigation Filter:这里不要使用导航过滤器!我们要检测的是几何体遮挡,而不是寻路。保持为None
    • Trace Channel:设置为Visibility或你自定义的用于检测遮挡的碰撞通道。确保你的掩体Actor(如墙壁、箱子)的碰撞体阻挡此通道。
    • Test Purpose:设置为Filter Only。这意味着,如果射线被阻挡(即从AI看不到候选点),该点通过测试;如果射线无阻挡(AI能直接看到候选点),该点被过滤掉。这是实现“躲藏”的关键逻辑。
    • Context:这里需要指定“威胁源”。点击Context旁边的加号,添加一个Querier上下文,但通常我们需要的是“玩家”的位置。更常见的做法是,在行为树中运行EQS查询时,通过Blackboard Key(黑板键)将“玩家Actor”或“玩家位置”作为Context传入。在测试配置中,Context就选择这个传入的黑板键(例如EnemyActor)。

实操心得Trace测试非常消耗性能,尤其是在网格点很多的时候。一个优化技巧是分两步走:先用一个简单的Distance测试过滤掉过远或过近的点,减少候选点数量,然后再对剩下的点执行昂贵的Trace测试。你可以在EQS中通过多个生成器阶段来实现。

3.3.2 测试二:到威胁源的距离测试(战术距离)

我们希望AI躲在离玩家不远不近的位置。

  1. 添加一个Distance测试。
  2. 配置细节:
    • Distance To:选择Context,并关联到代表威胁源的黑板键(如EnemyActor)。
    • Scoring Equation:选择Inverse Linear(反比线性)或Linear(线性)。但为了定义“理想区间”,我更喜欢用Custom Curve(自定义曲线)。
    • 使用自定义曲线:在Scoring Curve中,可以创建一条曲线。X轴代表距离,Y轴代表分数(0-1)。你可以将曲线调成一条“山形”:在5米处分数为0(太近),在15米处分数为1(理想距离),在30米处分数又降为0.3(太远)。这样EQS会自动根据距离查表给出分数。
3.3.3 测试三:路径开销测试(可达性)

一个点即使安全,如果路径蜿蜒曲折,AI跑过去要花10秒钟,那在快节奏战斗中也是不可接受的。

  1. 添加一个Pathfinding测试。这个测试会模拟计算从AI当前位置到候选点的路径长度。
  2. 配置细节:
    • Test Purpose:设置为Score Only
    • Scoring Equation:选择Inverse Linear。路径越长,分数越低。你可以通过Clamp Min/MaxNormalization Factor来调整分数的敏感度。例如,将路径长度归一化到0-1000单位之间进行评分。
3.3.4 加权与总分计算

现在我们有三个测试:Trace(过滤)、Distance(评分)、Pathfinding(评分)。在EQS编辑器的“测试”面板,你可以为后两个评分测试设置Weight(权重)。例如:

  • Distance测试权重设为0.6
  • Pathfinding测试权重设为0.4

EQS会先执行Trace测试过滤掉所有不安全的点,然后对剩余的点,分别计算Distance分数和Pathfinding分数,乘以各自权重后相加,得到最终得分。

4. 集成到行为树与AI控制器:让系统跑起来

EQS查询资产本身不会做任何事情,它需要被行为树调用。

4.1 在行为树中调用EQS查询

  1. 打开你的AI行为树。
  2. 在需要寻找掩体的地方(例如一个SelectorSequence节点下),添加一个Run EQS Query任务节点。
  3. 配置该任务节点:
    • Query Template:选择我们刚创建的EQS_FindBestCover
    • Query Config:这里需要配置运行时上下文。点击Run Mode下的Single Result,然后在细节面板中,找到Query Params。你需要在这里添加一个参数,将Blackboard Key(比如EnemyActor)绑定到EQS查询中TraceDistance测试所期望的Context上。这是将“玩家”这个动态目标传递给EQS的关键步骤。
    • Blackboard Key:选择一个黑板键来接收EQS查询找到的最佳位置,例如BestCoverLocation

4.2 在AI控制器中编写决策逻辑

行为树驱动动作,但何时触发“寻找掩体”这个分支,需要更上层的决策逻辑。这通常在AI控制器的Tick函数或事件驱动中完成。

一个简单的状态机逻辑可以是:

  1. 检查状态:AI是否处于“受威胁”状态(例如,看到玩家或生命值低于一定阈值)?
  2. 检查当前掩体:AI是否已经在掩体后?当前掩体是否仍然有效(是否被摧毁?是否还能遮挡玩家)?
  3. 触发查询:如果受威胁且无有效掩体,则通过设置黑板变量等方式,触发行为树中Run EQS Query的分支。
  4. 执行移动:行为树找到BestCoverLocation后,驱动AI通过Move To节点向该位置移动。
  5. 抵达后行为:AI到达掩体点后,应切换为“掩体状态”,可能包括探头射击、躲避、换弹等子行为。
// 这是一个概念性的伪代码逻辑,在AI控制器的Tick中 void AMyAIController::Tick(float DeltaTime) { Super::Tick(DeltaTime); AActor* Enemy = GetPerceivedEnemy(); bool bIsInCover = GetBlackboard()->GetValueAsBool(bInCoverKey); FVector CurrentCover = GetBlackboard()->GetValueAsVector(CoverLocationKey); if (Enemy && !bIsInCover) { // 检查是否需要新掩体 if (!IsCoverValid(CurrentCover, Enemy)) { // 触发行为树中的寻掩体任务 GetBlackboard()->SetValueAsBool(bNeedNewCoverKey, true); } } }

4.3 动态上下文与多威胁处理

上面的例子只处理了一个威胁源(玩家)。在复杂的战斗中,AI可能需要考虑多个敌人。EQS同样可以处理。

  • 方法一:迭代查询:对于每个感知到的威胁,依次运行EQS查询,然后综合所有结果(例如,选择能遮挡最多威胁源的点,或对威胁度最高的敌人权重更大)。这比较消耗性能。
  • 方法二:聚合上下文:更高级的做法是,在运行EQS查询前,在AI控制器中计算出一个“平均威胁位置”或“主要威胁方向”,将这个向量或位置作为一个Context传入EQS。这样一次查询就能综合考虑多个威胁。这需要你自定义EQS的上下文对象。

5. 高级优化与实战调试技巧

一套基础系统搭建完成后,真正的挑战在于让它运行得高效、稳定、符合预期。以下是我在多个项目中积累的实战经验。

5.1 性能优化策略

EQS查询,尤其是包含TracePathfinding的复杂查询,是性能大户。在屏幕上同时有几十个AI时,必须进行优化。

  1. 降低查询频率:不要在行为树的Tick里每帧都跑EQS。使用Service节点配合Interval(间隔),比如每0.5秒或1秒查询一次。或者在AI状态改变时(如首次发现敌人、当前掩体失效)才触发查询。
  2. 分层查询
    • 第一阶段(粗筛):使用一个大间距的网格(如500单位)和简单的Distance测试,快速过滤出几个大致的方向区域。
    • 第二阶段(精查):在第一阶段得分最高的区域中心,发起第二次EQS查询,这次使用小间距网格(如100单位)和完整的测试电池。这能大幅减少不必要的Trace计算。
  3. 使用异步查询:UE5的EQS支持异步执行。Run EQS Query任务可以设置为Run Mode中的Single Result并勾选Run in Background。这样查询不会阻塞行为树,AI可以继续执行其他任务(如移动、开火),等查询完成后再处理结果。这对于计算耗时的查询非常有用。
  4. 简化测试:评估每个测试的必要性。Pathfinding测试比Trace更耗资源吗?在某些简单场景下,或许用Distance测试代替Pathfinding也能获得近似效果。

5.2 调试与可视化

UE5提供了强大的EQS调试工具,善用它们可以节省大量时间。

  1. 在编辑器中运行并调试:在Play模式下,打开Window->Developer Tools->Environment Query Editor。选择你的AI角色,然后选择运行的EQS查询。你可以实时看到:
    • 生成的网格点(白色小点)。
    • 每个测试后的点状态(被过滤的点会消失或变色,如Trace测试后,能被直接看到的点会被过滤掉)。
    • 每个点的最终得分,并以颜色深浅(或高度)可视化出来。得分最高的点会非常醒目。
  2. 解读可视化结果:如果发现AI总是选择一些看似愚蠢的点,通过这个调试器,你可以一步步看是哪个测试环节出了问题。是Trace没检测到某个薄掩体?还是Distance曲线设置得不合理,导致理想距离带太窄?
  3. 绘制调试图形:你可以在AI控制器的代码中,使用DrawDebug系列函数(如DrawDebugSphere)临时绘制出EQS返回的最佳点位置,方便在运行时观察AI的决策。

5.3 常见问题与排查实录

这里记录几个我踩过的典型坑和解决方法:

问题现象可能原因排查与解决思路
AI找不到任何掩体,EQS始终返回失败。1.生成器问题:网格点没有投影到导航网格上(Project to Navigation未勾选或导航过滤器设置错误)。
2.过滤太严Trace测试将所有点都过滤掉了。可能是威胁源Context没正确传入,或者碰撞通道设置不对。
1. 打开EQS调试器,看第一步生成的点是否都在地面上(导航网格上)。
2. 临时将Trace测试的Test Purpose改为Score Only,看所有点是否都有分数。检查传入的EnemyActor上下文是否有效。
AI选择的掩体点“穿模”或躲在无效物体后面。Trace测试的射线检测过于简单。从AI眼睛位置到掩体点的射线没被挡,但从AI身体中心到掩体点后方一点的射线可能就被挡了。进行更全面的遮挡测试。可以尝试从AI身上的多个关键点(如头部、胸部)向掩体点发射多条射线,只有全部被挡或大部分被挡才认为安全。这需要自定义EQS测试或是在查询后做二次验证。
AI在掩体间频繁切换,行为“抽搐”。1.查询频率过高
2.评分权重设置不当,导致几个候选点分数非常接近,最佳点随玩家微小移动而频繁变动。
1. 降低查询频率,并加入“掩体最小保持时间”的冷却机制。
2. 增加DistancePathfinding测试的权重,让系统更有倾向性。或者在行为树中,只有当前掩体“失效”程度超过某个阈值时才寻找新掩体。
性能开销巨大,游戏帧数下降明显。同时活动的AI过多,每个都在进行高频率的复杂EQS查询。实施分层查询异步查询。为AI设置不同的“更新频率”,离玩家远或不在屏幕内的AI降低EQS查询频率。使用GameplayTag标记AI状态,只有“战斗状态”的AI进行全功能查询。

5.4 超越基础:让掩体行为更真实

当基础系统稳定后,可以考虑加入更多细节,让AI的掩体行为更具沉浸感:

  1. 掩体类型识别:通过Trace测试时返回的命中Actor,可以判断掩体类型(矮墙、高箱、窗户)。AI可以根据类型决定是蹲下、站立射击还是盲射。
  2. 动态掩体失效:监听游戏事件(如爆炸、破坏系统)。当某个掩体Actor被摧毁时,通知所有正在使用或将其作为候选点的AI,更新它们的内部状态或立即触发新的EQS查询。
  3. 团队协作与掩体抢占:为掩体点增加一个“占用”状态。当AI选择一个掩体点后,通过GameplayTag或一个全局管理器将其标记为“已占用”。其他AI的EQS查询中可以加入一个测试,对“已占用”的点进行减分或过滤,避免多个AI挤向同一个掩体。这可以扩展为简单的团队阵型系统。
  4. 结合感知系统:不要只依赖EQS。将EQS与UE5的AIPerception组件深度结合。例如,AI在掩体后时,可以暂时降低其对玩家的“视觉”感知强度,模拟“躲藏”效果;或者当玩家投掷手雷时,AI的EQS查询中临时加入一个“远离危险位置”的测试。

构建一个健壮的智能掩体系统,是一个从核心原理到细节打磨的持续过程。EQS提供了强大的底层工具,但如何组合这些工具,设计出符合你游戏特定需求的评分逻辑和决策流程,才是真正体现设计功力的地方。这套系统一旦搭建完成,不仅能极大提升AI的战术可信度,也为后续实现更复杂的群体AI行为(如包抄、交叉火力、战术撤退)打下了坚实的基础。记住,所有参数都需要放到实际游戏场景中去测试和调整,没有一劳永逸的配置,只有最适合你当前关卡和玩法的调校。

← 返回列表