引言:一个“瞎子”AI引发的血案
那是去年夏天,我们工作室接到一个开放世界潜行游戏的订单,核心玩法是玩家潜入一座军事基地,躲避AI巡逻兵的视线和听觉。项目上线Demo测试时,玩家反馈最多的一句话是:“这AI是瞎子吗?我都贴脸了它还没反应!”——那一刻,我知道,感知系统不是加个组件那么简单,它需要一次从架构到调优的彻底重写。
今天,我就以这个项目为线索,完整复盘我们如何用UE5的AIPerception系统,从“看不见”到“听得见”,最终交付一个让玩家直呼“狡猾”的AI。全程第一人称,有血有肉,有坑有解。

需求分析:从“看见”到“听见”,不是加个组件那么简单
项目初期,我们天真地以为只要给AI挂上AIPerception组件,配置好视觉和听觉刺激,就万事大吉。结果测试版一出,AI要么发呆,要么对背后开枪的玩家毫无反应。后来我们才明白,感知系统不是孤立的,它和AI的决策树、行为树、移动组件紧密耦合。
我们的需求拆解如下:
- 视觉感知:AI应能检测玩家进入视野范围,并考虑遮挡、距离、视角锥体,以及光照影响(夜间视野降低)。
- 听觉感知:玩家奔跑、开枪、或投掷物落地时,AI应能听到声音并前往声源位置调查。
- 感知融合:当视觉和听觉同时触发时,AI应能综合判断,优先处理威胁更大的刺激。
- 性能开销:场景中同时存在20+AI,每帧更新感知,必须控制在1ms以内。
这个需求列表,直接决定了我们后续的技术选型和踩坑方向。
技术选型:原生AIPerception?还是自研方案?
UE5自带的AIPerception组件已经很强大,它基于感知刺激(Stimulus)机制,支持视觉、听觉、伤害、预测等多种感知类型。但原生方案有一个致命伤:视觉感知的遮挡测试使用的是LineTrace,性能开销大,且对半透明物体(如草丛)无法正确遮挡。我们的游戏有大量草丛和灌木,玩家喜欢蹲在里面当“老六”,如果AI在草丛中还能看到玩家,那潜行玩法就崩了。
因此,我们决定:基于AIPerception扩展,重写视觉感知的遮挡逻辑,并优化听觉感知的距离衰减。同时,为了兼容行为树,我们保留了AIPerception的刺激接口,方便决策树订阅。
最终架构:
| 模块 | 原生方案 | 我们的方案 |
|---|---|---|
| 视觉感知 | LineTrace遮挡 | SphereTrace + 环境查询系统(EQS) |
| 听觉感知 | 固定半径 | 距离衰减 + 障碍物阻挡 |
| 刺激融合 | 单一刺激 | 刺激权重 + 优先级 |
| 性能 | 每帧全量检测 | 分帧检测 + 空间哈希 |
技巧:不要盲目推翻引擎自带功能,而是站在它的肩膀上扩展。AIPerception的刺激监听和感知ID机制非常优秀,我们把精力花在瓶颈上。
踩坑实录:那些年我们掉进去的坑
坑1:视觉遮挡的“透视”问题
原生视觉感知用LineTrace从AI眼睛到目标点,如果目标点落在角色胶囊体中心,而玩家蹲下时,视线可能被草丛遮挡,但LineTrace是直线,无法模拟草丛的“半遮挡”效果。结果就是:玩家蹲在草丛里,AI照样看见他。
解决思路:我们改用了SphereTrace,用球形检测来模拟视线的“体积”,同时配合EQS(环境查询系统)在目标周围生成多个采样点,判断是否有任何一个点可见。这样,草丛作为覆盖物,会阻挡部分采样点,AI只有看到足够多的点才会确认目标。
// 伪代码:EQS生成采样点后,逐个检测可见性
for (FVector SamplePoint : SamplePoints) {
if (IsVisible(AIEyeLocation, SamplePoint)) {
VisibleCount++;
}
}
if (VisibleCount >= MinVisiblePoints) {
// 确认感知到目标
}
坑2:听觉感知的“穿墙”问题
默认的听觉感知只是半径检测,声音会穿墙。玩家躲在墙后,AI依然能听到脚步声,导致玩家无法通过墙体阻挡声音来躲避。这完全不符合物理。
解决思路:在听觉检测时,我们引入了声源到AI之间的导航网格(NavMesh)路径,如果路径长度大于直线距离的1.5倍,说明有障碍物,降低听觉权重。同时,我们实现了基于距离的衰减曲线,并让玩家奔跑时的噪声半径大于走路,开枪时更大。
float PathLen = GetPathLength(SourceLocation, AILocation);
float DirectLen = FVector::Dist(SourceLocation, AILocation);
float ObstructionFactor = PathLen / DirectLen;
if (ObstructionFactor > 1.5f) {
HeardStrength *= 0.2f; // 障碍物阻挡
}
坑3:多AI感知冲突
当多个AI同时感知到玩家,它们会各自为战,导致玩家被围殴。我们需要AI之间共享感知信息,协同行动。
解决思路:在GameMode中维护一个共享感知管理器,AI检测到目标后,将目标信息广播给一定范围内的友军,形成“信息共享”。这样,一个AI发现了玩家,其他AI也会警觉,并向目标区域包围。
注意:共享感知要控制范围,避免全图AI都来追玩家,导致性能爆炸和玩法失衡。我们使用了“感知链”机制,只传播给视线内或距离近的友军。
核心技术实操:打造“听得见”的AI
下面我们重点看看听觉感知的实现细节,因为视觉感知大家讲得多,听觉才是我们这次项目的制胜法宝。
基于自定义类扩展听觉感知
我们继承了UAIPerceptionComponent,重写了听觉刺激的处理逻辑。核心思路是:将声音事件包装成刺激源,附带位置、强度、音频类型(脚步声、枪声、爆炸声)。
UCLASS()
class UMyAIPerceptionComponent : public UAIPerceptionComponent {
GENERATED_BODY()
public:
virtual void OnNewStimulus(const FVector& Location, float Strength, EAISenseId SenseId) override;
private:
float CalculateAudibility(float Distance, float Strength, float Obstruction);
};
在OnNewStimulus中,我们根据距离和障碍物计算可听度,并决定是否产生感知刺激。
声音事件驱动的感知融合
为了让AI“听声辨位”,我们实现了音源类型与AI状态机联动:
- 如果AI处于巡逻状态,听到噪音会进入“调查”状态,前往声源位置。
- 如果AI处于战斗状态,听到枪声会立即转向声源方向,并进入警戒。
- 如果AI同时看到玩家和听到声音,会选择视觉目标优先,但听觉会作为辅助线索。
我们用一个权重公式来融合:
FinalStimulus = VisualStimulus * VisualWeight + HearingStimulus * HearingWeight;
视觉权重默认更高,但如果玩家在黑暗中,视觉权重降低,听觉权重上升,这就鼓励玩家利用环境声音来引开AI。
性能优化:分帧检测和空间分区
场景中20个AI,每个每帧做视觉和听觉检测,CPU压力很大。我们采用了分帧检测:每帧只处理一部分AI,比如每帧处理5个,分4帧完成全部更新。同时,用空间哈希将AI和声源分区,只检测附近的对象。
// 分帧检测伪代码
int32 AIIndex = GetCurrentFrameIndex() % TotalAI;
for (int32 i = 0; i < NumPerFrame; ++i) {
int32 AIIdx = (AIIndex + i) % TotalAI;
UpdatePerceptionForAI(AIIdx);
}
实测性能从每帧4.5ms降到1.2ms,效果显著。
成果展示:从“瞎子”到“战狼”
经过重构,我们的AI在测试中表现出色:
- 玩家在草丛中蹲行,AI在2米外发现不了,但一旦靠近或发出声音,AI会立刻警觉。
- 玩家在墙体后面奔跑,AI能听到声音,但不会知道具体位置,而是到声源附近搜索。
- 玩家故意投掷石头引开AI,AI会前往声源点,但不会完全放弃巡逻路线,而是调查后返回。
玩家反馈:“AI终于像人了,会听声辨位,还会包抄,太刺激了!”

这个项目的成功,让我深刻体会到:UE5的AIPerception系统是一套优秀的框架,但真正让AI“活”起来的,是开发者对游戏机制的理解和调优。希望这篇复盘能帮你在AI感知的深水区少踩坑。
最后一条建议:不要拘泥于参数,先想清楚你的游戏需要什么样的感知行为,再去调整引擎。感知系统的本质是“信息管理”,而不是一堆数值。








评论(0)