4、AI感知系统(一):AIPerception组件详解、视觉感知( Sight )配置与调试、听觉感知( Hearing )与刺激源
好,咱们今天正式进入AI感知系统。说实话,这是整个行为树实战里我最喜欢讲的一块。为什么?因为感知系统做得好不好,直接决定了你的AI是「像个真人」还是「像个木桩」。
我见过太多项目,AI逻辑写得天花乱坠,结果敌人站在背后三米都看不见玩家。嗯,这种坑我踩过不止一次。所以这一章,咱们把AIPerception组件彻底掰开揉碎。
4.1 AIPerception组件:AI的「眼睛和耳朵」
先问个问题:你的AI怎么知道玩家在哪?
最简单粗暴的方式——每帧用LineTrace扫一圈。但你想过没有,场景里如果有20个AI,每个AI每帧扫360度……帧率直接崩给你看。
Unreal Engine的AIPerception组件就是来解决这个问题的。它不是让你自己去写射线检测,而是提供了一套完整的感知框架。说白了,你只需要告诉它「我要看多远」「我要听多响」,剩下的引擎帮你搞定。
我个人习惯,只要项目里AI数量超过3个,就一定会用AIPerception。原因很简单:性能好,扩展性强。
- AIPerceptionComponent:挂载在AI Controller上的感知大脑
- 感知配置( Sense Config ):定义每种感知方式的具体参数
- 刺激源( Stimuli ):产生感知事件的对象(比如玩家角色、爆炸物)
4.2 视觉感知( Sight ):让AI真正「看见」
视觉感知是最常用的,也是最容易出问题的。我记得第一次做FPS项目时,AI的视觉范围设成了180度,结果敌人从侧面走过来,AI完全没反应。后来才发现——我忘了设置「视觉锥体」的朝向。
4.2.1 配置视觉感知
在AI Controller的BeginPlay里,我们这样配置:
// C++ 示例
UAIPerceptionComponent* PerceptionComp = FindComponentByClass<UAIPerceptionComponent>();
if (PerceptionComp)
{
// 创建视觉感知配置
UAISenseConfig_Sight* SightConfig = NewObject<UAISenseConfig_Sight>(this);
// 设置视觉范围
SightConfig->SightRadius = 1500.0f; // 能看见的最远距离
SightConfig->LoseSightRadius = 2000.0f; // 丢失目标的最远距离
SightConfig->PeripheralVisionAngleDegrees = 90.0f; // 视野角度(左右各45度)
SightConfig->DetectionByAffiliation.bDetectEnemies = true;
SightConfig->DetectionByAffiliation.bDetectNeutrals = true;
SightConfig->DetectionByAffiliation.bDetectFriendlies = false;
// 注册配置
PerceptionComp->ConfigureSense(*SightConfig);
PerceptionComp->SetDominantSense(SightConfig->GetSenseImplementation());
}
这里有个细节要注意:LoseSightRadius一定要比SightRadius大。为什么?
你想想看,如果两个值一样大,AI在边界上就会反复「看见-丢失-看见-丢失」,像个神经病一样。我曾经调试一个项目,AI在走廊里疯狂抽搐,查了半天才发现是这两个值设成一样的了。
SightRadius和LoseSightRadius之间留出20%-30%的缓冲区间。比如SightRadius=1000,LoseSightRadius=1300。这样AI的视觉行为会平滑很多。
4.2.2 调试视觉感知
调试视觉感知,我强烈推荐用Unreal的「AI Debug」工具。在运行游戏时,按反引号键(`)打开控制台,输入:
ai.debug.AIPerception 1
你会看到AI周围出现一个绿色的视觉锥体。这个锥体就是AI实际能看到的范围。如果锥体没对准玩家,说明你的配置有问题。
我一般会检查三件事:
- 锥体朝向:AI的Pawn正面是否朝向目标?
- 遮挡物:中间有没有墙挡住?视觉感知默认会被碰撞阻挡
- 检测频率:默认每0.5秒检测一次,如果觉得不够灵敏可以调小
我曾经遇到一个情况:AI明明站在玩家面前,但就是看不见。调试了半天,发现是AI的Pawn上挂了一个「隐形碰撞体」,把视觉射线挡住了。所以记得检查AI自身的碰撞设置。
4.3 听觉感知( Hearing ):让AI「听见」动静
视觉感知虽然好用,但有个致命弱点——它依赖视线。如果玩家躲在墙后面,AI就彻底瞎了。
这时候就需要听觉感知上场了。听觉感知不关心视线,只关心「声音够不够响」和「距离够不够近」。
4.3.1 配置听觉感知
// 创建听觉感知配置
UAISenseConfig_Hearing* HearingConfig = NewObject<UAISenseConfig_Hearing>(this);
// 设置听觉范围
HearingConfig->HearingRange = 2000.0f; // 能听到的最远距离
HearingConfig->LoSHearingRange = 5000.0f; // 视线通畅时的最远距离
HearingConfig->DetectionByAffiliation.bDetectEnemies = true;
HearingConfig->DetectionByAffiliation.bDetectNeutrals = true;
HearingConfig->DetectionByAffiliation.bDetectFriendlies = true;
// 注册配置
PerceptionComp->ConfigureSense(*HearingConfig);
这里有个有意思的点:LoSHearingRange和HearingRange的区别。
说白了,LoSHearingRange是「如果中间没有墙,我能听多远」。而HearingRange是「不管有没有墙,我都能听到的范围」。举个例子:
- 玩家在墙后面开枪:如果距离在HearingRange内,AI能听到
- 玩家在空旷地带说话:如果距离在LoSHearingRange内,AI能听到
4.3.2 产生听觉刺激源
光配置AI的耳朵还不够,你还得让玩家或者其他物体能「发出声音」。在Unreal里,这通过ReportEvent来实现:
// 在玩家角色或武器上调用
UAIPerceptionSystem::ReportEvent(
GetWorld(),
*NewObject<UAISenseEvent_Hearing>()
->SetLocation(GetActorLocation())
->SetRange(1500.0f) // 声音传播距离
->SetStimulusStrength(1.0f) // 声音强度
);
我个人习惯,把开枪、脚步声、爆炸声都做成独立的刺激源。这样AI可以根据声音类型做出不同反应——听到枪声就警戒,听到脚步声就转身查看。
如果你想让AI对声音更敏感,可以调小HearingRange但增大StimulusStrength。这样AI不会听到全地图的声音,但一旦听到就会很警觉。
4.4 感知事件的处理
配置好了感知,AI怎么知道「我看到玩家了」?
答案是:通过委托(Delegate)。在AI Controller里绑定感知事件:
// 绑定感知更新事件
PerceptionComp->OnPerceptionUpdated.AddDynamic(this, &AAIController::OnPerceptionUpdated);
void AAIController::OnPerceptionUpdated(const TArray<AActor*>& UpdatedActors)
{
for (AActor* Actor : UpdatedActors)
{
FAIStimulus Stimulus;
if (PerceptionComp->GetActorsPerception(Actor, Stimulus))
{
if (Stimulus.WasSuccessfullySensed())
{
// 感知成功!更新行为树的黑板值
GetBrainComponent()->GetBlackboardComponent()->SetValueAsObject("TargetActor", Actor);
GetBrainComponent()->GetBlackboardComponent()->SetValueAsVector("LastKnownLocation", Actor->GetActorLocation());
}
else
{
// 丢失目标
GetBrainComponent()->GetBlackboardComponent()->ClearValue("TargetActor");
}
}
}
}
这里有个坑:WasSuccessfullySensed()返回true只代表「这次检测到了」,不代表「之前没检测到」。所以你需要自己维护一个状态,判断是「新发现」还是「持续发现」。
我一般会在AI Controller里加一个CurrentTarget变量,每次感知更新时对比一下。如果变了,才触发行为树的状态切换。
4.5 性能优化建议
最后聊点实在的。感知系统虽然好用,但用不好也会卡。
| 配置项 | 推荐值 | 说明 |
|---|---|---|
| 检测间隔 | 0.2 - 0.5秒 | 太频繁会消耗CPU,太慢会反应迟钝 |
| 视觉范围 | 不超过2000 | 超过这个值,场景里大量AI会互相检测 |
| 听觉范围 | 不超过3000 | 声音传播距离越大,检测开销越大 |
| 同时感知的AI数量 | 不超过10个 | 超过这个数,建议用LOD系统降级 |
我记得有一次做开放世界项目,场景里放了50个AI,每个AI的视觉范围都是5000。结果帧率直接掉到20。后来我把视觉范围砍到1500,帧率回到60,而且玩家根本感觉不到区别——因为远处的AI本来就不需要看到玩家。
嗯,这就是所谓的「感知的边界」。不是所有AI都需要全知全能的。
视觉感知负责「看到」,听觉感知负责「听到」。两者配合使用,你的AI就能在墙后面也能感知到玩家。但别忘了调试——用ai.debug.AIPerception 1看看你的AI到底「看没看到」。
下一章,咱们聊感知系统的进阶用法——如何让AI「记住」看到的东西,以及如何用刺激源实现更复杂的感知逻辑。到时候见。