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);

这里有个有意思的点:LoSHearingRangeHearingRange的区别。

说白了,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「记住」看到的东西,以及如何用刺激源实现更复杂的感知逻辑。到时候见。