AI Perception 시스템 아키텍처
UAIPerceptionSystem의 전체 구조, Listener/Source 모델, Stimulus 처리 파이프라인을 분석합니다
UAIPerceptionSystem 아키텍처
월드 레벨의 Perception 관리 시스템
UAIPerceptionSystem은 월드 서브시스템으로, 모든 AI 감각 데이터의 중앙 허브 역할을 합니다. Sense 클래스를 관리하고, Listener(감지자)와 Source(자극원) 간의 매칭을 처리합니다.
// UAIPerceptionSystem - 월드 서브시스템
class AIMODULE_API UAIPerceptionSystem : public UAISubsystem
{
// 등록된 Sense 인스턴스 관리
TArray<UAISense*> Senses;
// Listener 등록/해제
void RegisterSource(FAISenseID SenseID, AActor& Source);
void UnregisterSource(AActor& Source);
// Perception 업데이트 (매 프레임)
void Tick(float DeltaTime);
// 특정 Sense의 글로벌 설정
static void SetMaxStimulusAge(FAISenseID SenseID, float MaxAge);
};
// 접근 방법
UAIPerceptionSystem* PerceptionSystem =
UAIPerceptionSystem::GetCurrent(GetWorld());
// 또는 AIController에서
UAIPerceptionComponent* Perception =
AIController->GetPerceptionComponent();UAIPerceptionSystem은 월드당 하나만 존재합니다. 모든 AI의 Perception 요청이 이 시스템을 통해 처리되므로, Sense 설정의 최적화가 전체 AI 성능에 직접적으로 영향을 미칩니다.
Listener와 Stimulus Source
감지자와 자극원의 등록 및 매칭
Listener는 UAIPerceptionComponent를 가진 AI로, 감각 입력을 받습니다. Stimulus Source는 AIPerceptionStimuliSourceComponent를 가진 액터로, 감각 자극을 발생시킵니다.
// Listener 설정 (AIController)
AMyAIController::AMyAIController()
{
// Perception Component 생성
PerceptionComponent = CreateDefaultSubobject
<UAIPerceptionComponent>(TEXT("PerceptionComp"));
// Sight Config 추가
UAISenseConfig_Sight* SightConfig =
NewObject<UAISenseConfig_Sight>();
SightConfig->SightRadius = 3000.f;
SightConfig->LoseSightRadius = 3500.f;
SightConfig->PeripheralVisionAngleDegrees = 60.f;
SightConfig->SetMaxAge(5.f);
SightConfig->DetectionByAffiliation.bDetectEnemies = true;
SightConfig->DetectionByAffiliation.bDetectNeutrals = true;
SightConfig->DetectionByAffiliation.bDetectFriendlies = false;
PerceptionComponent->ConfigureSense(*SightConfig);
PerceptionComponent->SetDominantSense(
SightConfig->GetSenseImplementation());
}
// Stimulus Source 설정 (감지 대상 액터)
// 블루프린트: AIPerceptionStimuliSourceComponent 추가
// 또는 C++:
UAIPerceptionStimuliSourceComponent* SourceComp =
CreateDefaultSubobject<UAIPerceptionStimuliSourceComponent>(
TEXT("StimuliSource"));
SourceComp->RegisterForSense(
TSubclassOf<UAISense>(UAISense_Sight::StaticClass()));
SourceComp->bAutoRegisterAsSource = true;LoseSightRadius를 SightRadius보다 크게 설정하면 히스테리시스가 생겨, 시야 경계에서 감지/비감지가 반복되는 현상을 방지합니다.
Stimulus 처리 파이프라인
자극 생성부터 콜백까지의 전체 흐름
Stimulus는 Source에서 발생하여 Sense에 의해 처리되고, FAIStimulus 구조체로 Listener에 전달됩니다. 이 과정에서 MaxAge, Strength 등의 필터링이 적용됩니다.
// FAIStimulus 구조체 - 감각 정보의 단위
struct FAIStimulus
{
float Age; // 자극 나이 (초)
float ExpirationAge; // 만료 시간
float Strength; // 자극 강도
FVector StimulusLocation; // 자극 발생 위치
FVector ReceiverLocation; // 수신자 위치
FName Tag; // 자극 태그
bool bSuccessfullySensed; // 감지 성공 여부
// ...
};
// Perception 업데이트 콜백
PerceptionComponent->OnPerceptionUpdated.AddDynamic(
this, &AMyAIController::OnPerceptionUpdated);
PerceptionComponent->OnTargetPerceptionUpdated.AddDynamic(
this, &AMyAIController::OnTargetPerceptionUpdated);
// 개별 타겟 감지/상실
void AMyAIController::OnTargetPerceptionUpdated(
AActor* Actor, FAIStimulus Stimulus)
{
if (Stimulus.WasSuccessfullySensed())
{
// 감지됨 → Blackboard에 타겟 등록
GetBlackboardComponent()->SetValueAsObject(
TEXT("TargetActor"), Actor);
}
else
{
// 감지 상실 → 마지막 위치 기록
GetBlackboardComponent()->SetValueAsVector(
TEXT("LastKnownLocation"),
Stimulus.StimulusLocation);
GetBlackboardComponent()->ClearValue(
TEXT("TargetActor"));
}
}OnPerceptionUpdated는 모든 감지 결과를 한 번에 전달하고, OnTargetPerceptionUpdated는 개별 액터별로 호출됩니다. 대부분의 경우 개별 콜백이 더 효율적입니다.
Perception 쿼리와 필터링
감지된 액터를 조건별로 필터링
UAIPerceptionComponent는 다양한 쿼리 메서드를 제공하여 감지된 액터를 Sense 종류, 현재 감지 상태 등으로 필터링할 수 있습니다.
// 현재 감지 중인 모든 액터
TArray<AActor*> PerceivedActors;
PerceptionComponent->GetCurrentlyPerceivedActors(
nullptr, PerceivedActors); // nullptr = 모든 Sense
// 특정 Sense로 감지된 액터만
TArray<AActor*> SeenActors;
PerceptionComponent->GetCurrentlyPerceivedActors(
UAISense_Sight::StaticClass(), SeenActors);
// 특정 액터에 대한 상세 감지 정보
FActorPerceptionBlueprintInfo Info;
PerceptionComponent->GetActorsPerception(TargetActor, Info);
for (const FAIStimulus& Stimulus : Info.LastSensedStimuli)
{
if (Stimulus.Type == UAISense::GetSenseID<UAISense_Sight>())
{
UE_LOG(LogAI, Log, TEXT("Sight: Age=%.1f, Strength=%.2f"),
Stimulus.Age, Stimulus.Strength);
}
}
// 감지 이력이 있는 모든 액터 (만료 포함)
TArray<AActor*> KnownActors;
PerceptionComponent->GetKnownPerceivedActors(
UAISense_Sight::StaticClass(), KnownActors);GetKnownPerceivedActors()는 MaxAge 이내의 모든 액터를 반환합니다. 현재 시야에 없지만 최근에 본 적을 "기억"하는 AI를 구현할 때 유용합니다.
핵심 요약
- UAIPerceptionSystem은 월드 서브시스템으로, 모든 AI 감각의 중앙 허브 역할을 한다
- Listener(UAIPerceptionComponent)와 Source(StimuliSourceComponent)의 분리 구조로 감지 시스템이 동작한다
- FAIStimulus가 자극 정보의 단위이며, Age/Strength로 자극의 유효성을 관리한다
- OnTargetPerceptionUpdated 콜백으로 개별 액터의 감지/상실을 처리하는 것이 효율적이다
도전 과제
배운 내용을 직접 실습해보세요
UAIPerceptionSystem::GetCurrent()로 월드의 Perception 시스템에 접근하세요. 등록된 Listener 수와 Stimulus Source 수를 로그로 출력하고, 시스템의 업데이트 주기를 확인합니다.
UAIPerceptionStimuliSourceComponent를 Actor에 부착하여 자동으로 감각 소스로 등록되도록 하세요. RegisterForSense
UAISense를 상속하여 '지진 감지(Seismic Sense)' 커스텀 감각을 구현하세요. 폭발이나 대형 몬스터 이동 시 지면 진동을 발생시키고, 범위 내 AI가 이를 감지하도록 합니다.