왜 넣었나?
단순 시야 추적만으로는 연출 다양성이 부족함 : 시야 밖에서도 "소리 듣고 갸우뚱 -> 확인 하러감 ->발견 시 전투 전환" 같은 연출이 필요
전투 리액션 강화 : 피격(촉각/데미지)시 즉시 타겟 전환, 어그로 재설정 등,
유지보수/확장성 : 시각/청각/피해를 표준 이벤트(Simuls)로 통합 관리 -> BT/BB와 둥글게 연결
구성(설계 요점) 리스트
1. AIController에 PerceptionComponent 추가
2. SenseConfig 3 종 생성/등록
3. StimuliSource 부착&등록
4. 블랙보드/BT 키,상태 정의
5. 소리/피해 이벤트 발생 경로 연결
6. 테스트
구현부 주요 내용
몹 AIController에 정의
PerceptionComponent->SetDominantSense();
=> 동시에 들어왔을때 우선순위,
PerceptionComponent->OnPerceptionUpdated.AddDynamic(this, &ThisClass::OnPerceptionUpdate);
=> 인지가 될때 업데이트 되는 함수(델리게이트로 바인딩)
SenseConfig
아래는 내 코드에서 SenseConfig를 이렇게 정의했다.
void APC_AIController::SetupSenseConfig()
{
const FPC_EnemyTableRow* EnemyTableRow = GetEnemyData();
ensure(EnemyTableRow);
SightSense->SightRadius = EnemyTableRow->SightRadius;
SightSense->LoseSightRadius = EnemyTableRow->LoseSightRadius;
SightSense->PeripheralVisionAngleDegrees = EnemyTableRow->SightAngle;
//얼마나 기억할건지
SightSense->SetMaxAge(5.f);
SightSense->DetectionByAffiliation.bDetectEnemies = true;
HearingSense->HearingRange = 1500.f;
HearingSense->DetectionByAffiliation.bDetectEnemies = true;
HearingSense->SetMaxAge(5.f);
DamageSense->SetMaxAge(2.f);
//
PerceptionComponent->ConfigureSense(*SightSense);
PerceptionComponent->ConfigureSense(*HearingSense);
PerceptionComponent->ConfigureSense(*DamageSense);
}
Sight (시각)
- SightRadius / LoseSightRadius
- SightRadius: 최초 발견 거리
- LoseSightRadius: 시야를 잃는 거리
- PeripheralVisionAngleDegrees
- MaxAge(=SetMaxAge)
- 마지막으로 본 자극을 얼마나 기억할지(초).
- DetectionByAffiliation
- bDetectEnemies = true만 두면, 팀 시스템(IGenericTeamAgentInterface) 기준 적군만 감지.
Hearing (청각)
- HearingRange
- 소리를 들을 수 있는 기본 범위. (예: 1500.f)
- 실제 감지는 발생한 노이즈의 Loudness/MaxRange에도 영향.
- MaxAge
- “조금 전 들은 소리”를 얼마나 의심 상태로 유지할지.
- 3~6초가 무난. 그 사이에 도착해서 확인 → 못 찾으면 Patrol 복귀.
- 의사결정
- 발견 아님 → Investigating 상태 전환 + InvestigatingPos에 위치 저장.
- 갸우뚱/정찰 애님, 느린 Move, 도착 후 재탐색(EQS/시야 재확인) 추천.
- MakeNoise
void APC_PlayableCharaceter::Jump(const FInputActionValue& Value)
{
Super::Jump();
MakeNoise(1, this, GetActorLocation());
UGameplayStatics::SpawnSoundAtLocation(GetWorld(), PlayerData->JumpSound, GetActorLocation());
}
Damage (피해)
- MaxAge
- 피격 후 전투 모드 유지 시간 쪽 의미로 받아들여도 됨.
- 너무 길면 “맞고 오래 집착”, 너무 짧으면 “금방 잊음”.
- 정책
- 보통 가장 강한 우선순위.
- 즉시 Battle 전환 + 타겟 교체/어그로 갱신.
- ReportDamgeEvent
float APC_NonPlayableCharacter::TakeDamage(float DamageAmount, FDamageEvent const& DamageEvent,
AController* EventInstigator, AActor* DamageCauser)
{
UAISense_Damage::ReportDamageEvent(
GetWorld(),
this,
DamageCauser,
DamageAmount,
DamageCauser->GetActorLocation(),
GetActorLocation());
return Super::TakeDamage(DamageAmount, DamageEvent, EventInstigator, DamageCauser);
}
타겟 UAIPerceptionStimuliSourceComponent 부착
StimulusSource = CreateDefaultSubobject<UAIPerceptionStimuliSourceComponent>(TEXT("Stimulus"));
StimulusSource->RegisterForSense(TSubclassOf<UAISense>(UAISense_Sight::StaticClass()));
StimulusSource->RegisterWithPerceptionSystem();
동작 방식
1. Perception 업데이트 발생 → UpdatedActors 전달
2. 죽은 대상/군중제어 상태 등 즉시 제외됨
3. 한 액터에 대해 Sight → Hearing → Damage 순으로 체크
4. 시야,데미지는 바로 전투 상태 전환(블랙보드 타겟 액터 저장), 청각은 갸우뚱 이동/애님(블랙보드에 위치저장)
추가로 감각을 스코어링해서 우선순위를 처리하도록 해야 할듯함
구현 결과
마무리
UAIPerceptionComponent는 처음 보면 복잡해 보이지만, 한 번 구조를 잡아두면 표준화된 방식으로 처리 할 수 있다. 이번 포스팅에서 설명한 3요소만 적용해도 꽤 감각있는 몹AI처리가 된다.
'Unreal > API' 카테고리의 다른 글
[Unreal5] TFieldIterator, FNumericProperty와 Reflection으로 반복되는 UI코드 줄이는 방법 (0) | 2025.09.24 |
---|---|
[Unreal5] UStruct 이해하기 – 구조체로 데이터 다루기 (0) | 2025.09.23 |
unreal) EQS (0) | 2025.09.15 |
[Unreal5] SplineComponent : 몬스터 순찰 만들기 (0) | 2025.04.19 |
[Unreal5] GEngine 이란? (0) | 2025.04.08 |