📌 AI Perception Component
언리얼 엔진에서 AI가 감지할 수 있게 해주는 센서 시스템이다.
시각, 청각, 데미지 등 다양한 자극(Stimulus)을 감지하고, 그 정보를 AIController에 전달해서 AI의 의사결정에 활용된다.
간단하게 AI에게 시각을 부여해서 특정 거리안에 있으면 AI가 시각적으로 플레이어를 인식하고, 범위를 벗어나면 시각적으로 인지하지 못해 더 이상 추격이 불가능한 상태를 구현해보려한다.
✅ AIController 구현
*AIController.h
더보기
#pragma once
#include "CoreMinimal.h"
#include "AIController.h"
#include "Perception/AIPerceptionComponent.h"
#include "Perception/AISenseConfig_Sight.h"
#include "Chaser_AIController.generated.h"
UENUM(BlueprintType)
enum class EAIState : uint8
{
Idle,
Suspicious,
chasing
};
UCLASS()
class AI_TEST_API AChaser_AIController : public AAIController
{
GENERATED_BODY()
public:
AChaser_AIController();
UFUNCTION(BlueprintCallable, Category = "AI")
void StartChasing(AActor* Target);
UFUNCTION(BlueprintCallable, Category = "AI")
void StopChasing();
UFUNCTION(BlueprintCallable, Category = "AI")
EAIState GetCurrentState() const { return CurrentState; }
protected:
virtual void Tick(float DeltaTime) override;
UFUNCTION()
void OnperceptionUpdated(AActor* Actor, FAIStimulus Stimulus);
UFUNCTION(BlueprintCallable, Category = "AI")
void UpdateAIState();
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AI")
float DetectionRadius;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AI")
float LoseInterestRadius;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AI")
float chaseRadius;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "AI")
UAIPerceptionComponent* AIPerceptionComponent;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "AI")
UAISenseConfig_Sight* SightConfig;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "AI")
AActor* TargetActor;
private:
bool bIsChasing;
EAIState CurrentState;
FVector LastKnownLocation;
};
*AIController.cpp
더보기
#include "Chaser_AIController.h"
#include "Kismet/GameplayStatics.h"
#include "GameFramework/Character.h"
#include "DrawDebugHelpers.h"
AChaser_AIController::AChaser_AIController()
{
PrimaryActorTick.bCanEverTick = true;
DetectionRadius = 1000.0f;
LoseInterestRadius = 1500.0f;
chaseRadius = 800.0f;
LastKnownLocation = FVector::ZeroVector;
bIsChasing = false;
CurrentState = EAIState::Idle;
TargetActor = nullptr;
SightConfig = CreateDefaultSubobject<UAISenseConfig_Sight>(TEXT("SightConfig"));
SightConfig->SightRadius = DetectionRadius;
SightConfig->LoseSightRadius = LoseInterestRadius;
SightConfig->PeripheralVisionAngleDegrees = 90.0f;
SightConfig->DetectionByAffiliation.bDetectEnemies = true;
SightConfig->DetectionByAffiliation.bDetectNeutrals = true;
SightConfig->DetectionByAffiliation.bDetectFriendlies = true;
PerceptionComponent = CreateDefaultSubobject<UAIPerceptionComponent>(TEXT("PerceptionComponent"));
SetPerceptionComponent(*PerceptionComponent);
PerceptionComponent->ConfigureSense(*SightConfig);
PerceptionComponent->SetDominantSense(*SightConfig->GetSenseImplementation());
PerceptionComponent->RequestStimuliListenerUpdate();
PerceptionComponent->OnTargetPerceptionUpdated.AddDynamic(this, &AChaser_AIController::OnperceptionUpdated);
}
void AChaser_AIController::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
UpdateAIState();
if (bIsChasing && TargetActor)
{
APawn* ControlledPawn = GetPawn();
if (ControlledPawn)
{
float Distance = FVector::Dist(ControlledPawn->GetActorLocation(), TargetActor->GetActorLocation());
if (Distance <= chaseRadius)
{
MoveToActor(TargetActor, 100.0f);
LastKnownLocation = TargetActor->GetActorLocation();
#if WITH_EDITOR
DrawDebugLine(
GetWorld(),
ControlledPawn->GetActorLocation(),
TargetActor->GetActorLocation(),
FColor::Red,
false,
-1.0f,
0,
2.0f
);
#endif
}
else if (Distance > LoseInterestRadius)
{
StopChasing();
}
}
}
}
void AChaser_AIController::StartChasing(AActor* Target)
{
TargetActor = Target;
bIsChasing = true;
if (Target)
{
LastKnownLocation = Target->GetActorLocation();
}
CurrentState = EAIState::chasing;
}
void AChaser_AIController::StopChasing()
{
bIsChasing = false;
TargetActor = nullptr;
StopMovement();
CurrentState = EAIState::Idle;
}
void AChaser_AIController::OnperceptionUpdated(AActor* Actor, FAIStimulus Stimulus)
{
ACharacter* PlayerCharacter = UGameplayStatics::GetPlayerCharacter(GetWorld(), 0);
UE_LOG(LogTemp, Warning, TEXT("Perception on"));
if (Actor == PlayerCharacter)
{
if (Stimulus.Type == UAISense::GetSenseID<UAISense_Sight>())
{
TargetActor = Actor;
APawn* ControlledPawn = GetPawn();
if (ControlledPawn)
{
float Distance = FVector::Dist(ControlledPawn->GetActorLocation(), Actor->GetActorLocation());
if (Distance <= chaseRadius)
{
StartChasing(Actor);
}
else if (Distance <= DetectionRadius)
{
CurrentState = EAIState::Suspicious;
}
}
}
else
{
if (CurrentState == EAIState::chasing)
{
MoveToLocation(LastKnownLocation, 50.0f);
CurrentState = EAIState::Suspicious;
}
}
}
}
void AChaser_AIController::UpdateAIState()
{
if (!TargetActor) return;
APawn* ControlledPawn = GetPawn();
if (!ControlledPawn) return;
float DistanceToTarget = FVector::Dist(ControlledPawn->GetActorLocation(), TargetActor->GetActorLocation());
switch(CurrentState)
{
case EAIState::Idle:
if (DistanceToTarget <= DetectionRadius)
{
CurrentState = EAIState::Suspicious;
}
break;
case EAIState::Suspicious:
if (DistanceToTarget <= chaseRadius)
{
StartChasing(TargetActor);
}
else if (DistanceToTarget > DetectionRadius)
{
CurrentState = EAIState::Idle;
}
break;
case EAIState::chasing:
if (DistanceToTarget > LoseInterestRadius)
{
StopChasing();
}
break;
}
}
🔔 테스트 결과
시각적으로 AI가 인지해서 쫓아오다가 일정 거리 멀어지게되면 플레이어의 정보를 잃고 제자리에 멈춘 것을 확인할 수 있었다.
'내배캠 > Unreal Engine' 카테고리의 다른 글
Debug Helper 만들기 (0) | 2025.05.01 |
---|---|
AI_NavLink (0) | 2025.04.23 |
AI_RVO(회피 이동) (0) | 2025.04.23 |
AI_NavMesh (0) | 2025.04.22 |
미로 게임 구현(Dedicated Server) (0) | 2025.03.25 |