UE5.8 AI Perception 感知系统:潜行关卡中的视线、听觉与阵营
故事:仓库区的那个巡逻兵
小林负责的关卡是一个夜间潜入任务。玩家需要穿过一座废弃仓库,避开巡逻的守卫,拿到二楼服务器室的硬盘。策划对 AI 提了四个要求:
- 守卫在灯光下能看清玩家,背对时不行。
- 玩家跑步或打碎玻璃会发出声音,吸引附近守卫。
- 玩家被击中后,中弹的守卫和周围队友要立刻进入战斗。
- 守卫需要分清敌我,不能在自己人交火时把同伴当目标。
最开始,小林在 AIController 里写了一大堆射线检测和距离判断。每个守卫自己算角度、算距离、算遮挡。时间一长,代码里到处是 bCanSeePlayer、LastKnownLocation 这种临时变量,网络同步也很乱。后来主程让他改成 AI Perception 系统,他才意识到虚幻已经把这套东西做成了标准组件。
AI Perception 是 UE 内置的感知框架。它把视觉、听觉、伤害、阵营感知统一到一个组件里,自动处理更新频率、遗忘、遮挡和事件分发。这篇文章以仓库区守卫为例,把这套系统拆开讲清楚。
AI Perception Component 与 AIController
每个需要感知环境的 AI 都在 AIController 上挂一个 UAIPerceptionComponent。这个组件负责收集刺激、维护已知目标列表、向 AIController 广播事件。和手动写射线检测相比,它的优势在于把感知逻辑从业务代码里抽离,并提供一致的接口给 Behavior Tree 使用。
感知组件本身不直接移动角色或开枪,它只是告诉 AI:「我注意到一个目标」。真正的决策由 Behavior Tree 或 State Tree 完成。下面是一张典型架构图:
flowchart TD
subgraph world ["游戏世界"]
player["玩家角色"]
glass["可破坏玻璃"]
weapon["武器命中事件"]
end
subgraph ai ["AI 层"]
controller["AIController"]
perception["AIPerceptionComponent"]
senses["Sight/Hearing/Damage/Team"]
brain["Behavior Tree"]
end
subgraph output ["行为输出"]
move["移动指令"]
anim["动画状态"]
fire["开火逻辑"]
end
player --> perception
glass --> perception
weapon --> perception
controller --> perception
perception --> senses
perception --> brain
brain --> move
brain --> anim
brain --> fire在 C++ 里,AIController 通常这样声明:
UCLASS()
class MYGAME_API AGuardAIController : public AAIController
{
GENERATED_BODY()
public:
AGuardAIController();
UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
TObjectPtr<UAIPerceptionComponent> PerceptionComponent;
};构造函数里调用 SetPerceptionComponent 即可绑定:
AGuardAIController::AGuardAIController()
{
PerceptionComponent = CreateDefaultSubobject<UAIPerceptionComponent>(TEXT("PerceptionComp"));
SetPerceptionComponent(*PerceptionComponent);
}蓝图里更直接,在 AIController 蓝图里添加 AIPerception 组件,Details 面板里勾选需要的 Sense 类型。
Sight 视线感知
视线是潜入游戏最核心的感知类型。守卫能不能看见玩家,取决于距离、水平视角、垂直视角、遮挡和光照。AI Perception 的 Sight Config 把这些参数都暴露成可配置项。
关键参数包括:
- Sight Radius:最大视线距离,单位厘米。
- Lose Sight Radius:目标离开这个距离后才算丢失,通常比 Sight Radius 大一些,避免目标在边界反复进出。
- Peripheral Vision Angle:水平半角。设成 45 度表示守卫左右各 45 度,总共 90 度视野。
- Point of View:感知中心,通常是 AI 眼睛插槽。
- Detection by Affiliation:检测哪些阵营的目标。
守卫的视野可以用一个锥形近似。玩家位置 ,守卫眼睛位置 ,守卫正前方向量 ,则玩家是否在水平视野内取决于夹角 :
当 时,玩家位于水平视野锥内。接下来 AI Perception 会执行一次射线检测,确认中间没有墙体遮挡。如果射线命中了玩家,就会产生一个 Sight Stimulus。
视线更新不是每帧都做。AI Perception 有内部 Tick 间隔,默认大约 0.2 到 0.5 秒一次。这个间隔可以在项目设置里调整,也可以在运行时根据警戒等级动态修改。后面性能优化部分会再提。
小林给仓库守卫配了这样的参数:Sight Radius 2500,Lose Sight Radius 3000,Peripheral Vision Angle 60,Vertical Vision Angle 45。这意味着守卫能看到前方扇形区域,稍微抬头或低头也能覆盖到玩家蹲伏和站立的状态。
Hearing 听觉感知
仓库里有很多金属罐和玻璃窗。玩家奔跑、开枪、打碎玻璃都会发出噪音。AI Perception 的 Hearing Config 让 AI 能对声音产生反应,而不需要每个声源单独写触发器。
声音刺激通过 UAISense_Hearing 注册。任何 Actor 只要调用 UAISense_Hearing::ReportNoiseEvent,范围内的 AI 就会收到一次 Hearing Stimulus。典型调用如下:
UAISense_Hearing::ReportNoiseEvent(GetWorld(), GetActorLocation(), 1.0f, this, 0.0f, FName("Footstep"));参数依次是世界、事件位置、响度、Instigator、最大范围覆盖标签、Noise Tag。响度用于 Hearing Config 的阈值判断。如果事件响度低于 AI 的 Hearing Range 阈值,AI 不会收到刺激。
Hearing Config 里有两个重要参数:
- Hearing Range:AI 能听到声音的最大距离。
- LoS Hearing Range:能穿透遮挡听到声音的距离,通常比 Hearing Range 小。
实际游戏中,声音传播比视线更复杂。同样的脚步声,在空旷仓库里传得远,在密闭房间隔着墙会衰减。虚幻没有内置完整的声学传播,但可以通过响度和 LoS Hearing Range 做简化模拟。例如给玻璃破碎事件一个高响度 2.0,给蹲伏移动一个低响度 0.2,给正常跑步一个中等响度 1.0。
策划给小林提了一个细节:守卫听到声音后,不应该直接冲向玩家位置,而是先转向声源,进入警觉状态,再慢慢走过去。这个行为在 Behavior Tree 里实现,AI Perception 只负责把声音位置交给 Behavior Tree。
Damage 伤害感知
玩家开枪击中守卫后,被击中的守卫自己当然知道受击,但 AI Perception 的 Damage Sense 还能让 AI 把这次伤害当作一个刺激源。也就是说,即使 AI 没看见开枪的人,只要它受到伤害,就会获得一个指向伤害来源的刺激。
Damage Stimulus 通常由 Gameplay Ability System 或普通的伤害计算逻辑报告。调用方式如下:
UAISense_Damage::ReportDamageEvent(GetWorld(), DamagedActor, Instigator, DamageAmount, EventLocation, HitLocation);被击中的守卫收到 Damage Stimulus 后,Behavior Tree 可以立刻把状态从 Patrol 切换到 Combat,并把伤害来源位置设为新的调查点或攻击目标。
这个机制对多人合作或队友 AI 也很重要。如果一名守卫被打中,其他守卫可以通过 Team Sense 间接知道队伍进入了战斗状态,而不需要每个人都亲眼看到玩家。
Team Sense 与阵营感知
Team Sense 不是传统意义上的「看」或「听」,而是让同一阵营的 AI 共享感知状态。当守卫 A 看到玩家后,守卫 B 即使背对玩家,也可能因为队友的汇报而进入警觉。
实现 Team Sense 需要两个条件:
- AIController 实现
IGenericTeamAgentInterface接口。 - 同一队伍的 AI 返回相同的 Team ID。
阵营判断代码通常这样写:
FGenericTeamId AGuardAIController::GetGenericTeamId() const
{
return FGenericTeamId(1);
}Team ID 用整数表示。玩家团队可以设成 2,中立 NPC 设成 0。AI Perception 的 Detection by Affiliation 里会区分 Friendly、Neutral、Hostile。守卫通常只把 Hostile 阵营当作有效目标。
Team Sense 的工作方式可以用一张关系图表示:
graph LR
P["玩家 Team 2"] -->|Hostile| A["守卫 A Team 1"]
P -->|Hostile| B["守卫 B Team 1"]
A -->|Friendly| B
B -->|Friendly| A
A -.->|"Team Sense 共享"| B
C["平民 Team 0"] -->|Neutral| A
C -->|Neutral| B注意 Team Sense 不等于实时位置广播。UE5.8 默认的 Team Sense 需要 Behavior Tree 或黑板配合,由感知到玩家的 AI 把目标写入黑板,其他 AI 读取黑板后做出反应。如果你希望更直接的共享,可以在 Game Mode 或 AI Director 里维护一个全局威胁表。
刺激源 Stimuli Source
不是所有 Actor 都能被感知。玩家角色需要注册为 Stimuli Source,AI Perception 才会把它纳入检测。注册方式是在 Actor 上添加 UAIPerceptionStimuliSourceComponent,并绑定对应的 Sense。
UAIPerceptionStimuliSourceComponent* StimuliSource = CreateDefaultSubobject<UAIPerceptionStimuliSourceComponent>(TEXT("StimuliSource"));
StimuliSource->RegisterForSense(UAISense_Sight::StaticClass());
StimuliSource->RegisterForSense(UAISense_Hearing::StaticClass());
StimuliSource->RegisterWithPerceptionSystem();没有注册刺激源的玩家,即使站在 AI 面前也不会被看到。这是新手常踩的坑:AI Perception 配好了,AI 却像瞎子一样,多半是因为玩家角色忘了挂 Stimuli Source Component。
刺激源还支持动态注册和注销。例如玩家使用隐身技能后,可以临时注销 Sight 刺激源,让 AI 无法通过视觉发现;隐身结束后重新注册。这种动态控制比直接改 AI 参数更自然。
感知更新与遗忘机制
AI Perception 维护一个刺激列表,每个刺激都有 Age、Strength 和是否 Successfully Sensed。更新频率由 Sense 自己决定,Sight 通常是间隔扫描,Hearing 和 Damage 是事件驱动。
当 AI 连续看到玩家时,刺激 Age 会不断刷新,Strength 也会根据距离和角度变化。一旦玩家跑出视野或被遮挡,Age 开始增长。当 Age 超过遗忘时间,刺激会被标记为遗忘。
遗忘时间可以在 Sense Config 里配置。Sight 的 Auto Success Range 和 Max Age 决定了一个刺激能保留多久。小林给守卫设置的 Max Age 是 5 秒,这意味着玩家脱离视野后,守卫还能记住玩家最后位置 5 秒,5 秒后才会彻底放弃追击。
感知更新流程大致如下:
flowchart TD
A["玩家进入检测范围"] --> B{是否在视野锥内}
B -->|"否"| C["不产生刺激"]
B -->|"是"| D["射线检测是否可见"]
D -->|"被遮挡"| E["可能产生被遮挡的刺激"]
D -->|"可见"| F["生成 Sight Stimulus"]
F --> G["更新黑板目标与年龄"]
G --> H{玩家是否仍在视野}
H -->|"是"| G
H -->|"否"| I["Age 递增"]
I --> J{Age > Max Age}
J -->|"否"| I
J -->|"是"| K["遗忘目标"]遗忘不是简单的删除目标,而是把 Successfully Sensed 标记为 false。Behavior Tree 里的 Decorator 可以监听这个标记,决定 AI 是继续搜索最后已知位置,还是返回巡逻路线。
Alert Level 可以用一个简单公式描述。设当前刺激强度为 ,最大刺激强度为 ,遗忘速率为 ,则 t 时刻的警觉值 可近似为:
当 高于阈值 时,AI 进入警戒;高于 时进入战斗。这个公式不是 AI Perception 内部实现,但很多项目会在 Behavior Tree 或自定义组件里用类似思路做警觉度模拟。
感知调试
调试 AI 感知最常用的是控制台命令 ai.debug.perception。开启后,场景中会画出每个 AI 的感知范围、视野锥、最后已知位置和刺激年龄。
常用命令包括:
ai.debug.perception:开关感知调试绘制。showdebug perception:在屏幕右侧显示感知详情。ai.debug.senses:只显示特定 Sense 的调试信息。
小林在仓库关卡里打开 ai.debug.perception 后,立刻发现一个守卫的视野锥朝下偏了 30 度。原因是眼睛插槽绑到了脖子骨骼而不是头部骨骼。修复插槽后,守卫的视线范围就和策划要求一致了。
调试时还要注意 Stimulus 是否被成功注册。在 showdebug perception 面板里,你能看到每个感知到的 Actor、刺激类型、Age、Strength 和 Successfully Sensed 状态。如果看到了 Actor 但 Behavior Tree 没有反应,问题通常出在黑板键名或 Decorator 条件上,而不是感知系统本身。
与 Behavior Tree 的联动
感知系统本身不做决策,它通过黑板把信息传给 Behavior Tree。典型的黑板键包括:
- TargetActor:当前目标 Actor。
- LastKnownLocation:最后已知位置。
- LastHeardLocation:最后听到声音的位置。
- AlertLevel:警戒等级。
- bHasLineOfSight:是否正在直视目标。
Behavior Tree 的结构通常分成三层:巡逻、调查、战斗。
sequenceDiagram
participant P as "玩家"
participant S as "Stimuli Source"
participant Per as AIPerception
participant BT as "Behavior Tree"
participant BB as Blackboard
P->>S: 移动进入视野
S->>Per: 注册为刺激源
Per->>Per: 扫描视野锥与射线
Per->>BB: 写入 TargetActor 和 LastKnownLocation
BT->>BB: 读取目标信息
BT->>BT: 根据 AlertLevel 选择分支
BT->>Per: 持续查询刺激年龄
P->>Per: 脱离视野
Per->>BB: SuccessfullySensed = false
BT->>BT: 切换到调查分支
BT->>BB: 移动到 LastKnownLocation在 Behavior Tree 里,AI Perception 的更新事件可以通过 AI Perception Listener 接口接收。C++ 里实现 IAIPerceptionListener,在 OnPerceptionUpdated 回调里读取最新刺激:
void AGuardAIController::OnPerceptionUpdated(const TArray<AActor*>& UpdatedActors)
{
for (AActor* Actor : UpdatedActors)
{
FActorPerceptionBlueprintInfo Info;
PerceptionComponent->GetActorsPerception(Actor, Info);
for (const FAIStimulus& Stimulus : Info.LastSensedStimuli)
{
if (Stimulus.Type == UAISense_Sight::GetSenseID())
{
Blackboard->SetValueAsObject(TargetActorKey, Stimulus.WasSuccessfullySensed() ? Actor : nullptr);
Blackboard->SetValueAsVector(LastKnownLocationKey, Stimulus.StimulusLocation);
}
}
}
}这段代码只有二十多行,但已经把 Sight 刺激的接收、判断、写入黑板做完了。Hearing 和 Damage 可以类似处理,只是写入的键不同。
性能优化
AI Perception 虽然方便,但用不好会变成性能黑洞。仓库关卡里如果有二十个守卫,每个守卫都每帧扫描视野,开销会很大。
第一个优化点是控制更新频率。Sight Config 的 Implementation 里有扫描间隔设置,默认是 0.5 秒。对于远距离巡逻守卫,可以放宽到 1 秒甚至 2 秒;对于已经进入战斗的守卫,可以缩短到 0.1 秒。Behavior Tree 里可以动态修改这个间隔。
第二个优化点是缩小检测范围。Lose Sight Radius 没必要设得比 Sight Radius 大太多,通常大 10% 到 20% 就够了。过大的范围会增加 Sweep 查询的 Actor 数量。
第三个优化点是使用标签或通道过滤。AI Perception 默认会检测所有 Stimuli Source,但可以通过 Detection by Affiliation 只检测敌对目标,减少无效比较。如果场景里有大量中立动物或环境物体注册了刺激源,建议给它们单独配置 Sense,或降低它们的刺激源优先级。
第四个优化点是 LOD。远处 AI 不需要和近处 AI 一样的感知精度。UE5.8 的 AI LOD 系统可以降低远处 AI 的感知更新频率,甚至完全关闭感知,等玩家靠近再恢复。
第五个优化点是避免 Behavior Tree 里高频查询感知结果。不要把 GetActorsPerception 放在 Tick 里调用,而是靠事件驱动。AI Perception 的更新回调已经足够了, Behavior Tree 的 Decorator 也只在黑板值变化时重新评估。
性能开销可以用一个简化模型估算。设场景中有 个 AI,每个 AI 每次扫描需要比较 个刺激源,单次射线检测开销为 ,扫描间隔为 ,则每帧平均感知开销约为:
从这个式子能看出,降低 通过 LOD,降低 通过过滤,提高 通过降低更新频率,都能有效控制感知开销。
团队感知与阵营的进阶用法
前面提到的 Team Sense 只是基础。在更大的关卡或更复杂的阵营关系中,可能需要自定义团队感知规则。
一种常见模式是 AI Director。它维护一个全局威胁列表,任何 AI 感知到玩家后,把信息上报给 Director,Director 再广播给同一区域的其他 AI。这样可以实现更真实的通讯:守卫通过对讲机报告玩家位置,其他守卫收到后前往支援。
另一种模式是记忆共享。普通 Team Sense 不共享具体位置,只共享状态。如果你希望队友完全共享视野,可以在 Behavior Tree 里设计一个「同步黑板」节点,让一个 AI 把自己黑板上的 TargetActor 和 LastKnownLocation 复制给附近的队友。
阵营关系也可以动态变化。例如玩家完成某个任务后,原本敌对的强盗阵营变成中立。此时只需要把对应 AIController 的 Team ID 改成和玩家一致,AI Perception 的 Detection by Affiliation 会立即生效,守卫不再把玩家当 Hostile。
小林在仓库关卡里用了更简单的方案:所有守卫 Team ID 为 1,玩家 Team ID 为 2,平民 Team ID 为 0。守卫只对 Hostile 阵营产生 Sight 和 Damage 刺激,对 Friendly 和 Neutral 忽略。这样守卫不会互相攻击,也不会因为平民经过而误报警。
常见踩坑
1. 玩家没注册 Stimuli Source
这是最常见的问题。AI Perception 只检测注册了刺激源的 Actor。玩家 Character 必须挂 UAIPerceptionStimuliSourceComponent 并调用 RegisterWithPerceptionSystem。
2. 视野锥中心绑定错误
如果 Point of View 插槽不在头部,守卫可能会看天或看地。建议用 GetPawnViewLocation 和 GetActorForwardVector 作为默认参考,再根据需要微调。
3. 混淆 Sight Radius 和 Lose Sight Radius
Lose Sight Radius 必须大于等于 Sight Radius,否则目标在边界会频繁触发获得和丢失。一般 Lose Sight Radius 比 Sight Radius 大 10% 到 30%。
4. 声音事件没有正确报告
ReportNoiseEvent 需要在服务器和客户端都调用,或者只在服务器调用后通过复制让 AI 感知。如果只客户端调用,服务器上的 AI 不会响应。
5. Damage Stimulus 方向不对
Damage Stimulus 的位置信息来自事件参数。如果 EventLocation 或 HitLocation 传的是玩家位置而不是弹着点,AI 可能会朝错误方向反击。
6. 遗忘时间太短或太长
Max Age 太短会导致 AI 立刻忘记玩家,玩家可以轻松脱离战斗。太长会让 AI 永久记得一个已经远离的目标。建议根据关卡节奏调整,潜入关卡可以稍长,快节奏射击可以稍短。
结语
小林把仓库关卡的 AI 全部改成 AI Perception 后,策划调试守卫行为的时间明显缩短。以前改一个视野距离要翻三份代码,现在只需要调 Sense Config 里的数值。Behavior Tree 也变得更干净,只关心「有没有目标」和「目标在哪里」,不再需要自己算角度和射线。
AI Perception 不是万能的。复杂的声音传播、动态光照影响、群体战术还需要额外实现。但对于大部分潜行、射击、动作游戏的 AI 感知需求,它已经提供了足够完整的基础设施。把 Sight、Hearing、Damage、Team Sense 配置好,处理好刺激源注册和遗忘机制,再接入 Behavior Tree,就能做出反应自然、性能可控的 AI。
仓库区的守卫现在会正常巡逻,听到玻璃破碎会转头,看到玩家会追击,被击中会反击,队友发现玩家后会共享警戒状态。小林周末加班检查时,故意打碎了一块玻璃,三个守卫同时转向声源。他截了一张图发给策划,策划回了一个大拇指。