UE5 EQS (Environmental Query System)
EQS란
섹션 제목: “EQS란”EQS(Environmental Query System)는 AI가 주변 환경을 쿼리해 가장 적합한 위치나 액터를 선택하는 시스템입니다.
예시 사용 사례:
- 플레이어에서 숨을 수 있는 엄폐물 찾기
- 적을 공격할 수 있는 최적 사격 위치 선택
- 아군 근처의 안전한 이동 목표 지정
EQS 활성화
섹션 제목: “EQS 활성화”Editor Preferences → Experimental → AI → Environment Query System 활성화
기본 EQS Query 구성
섹션 제목: “기본 EQS Query 구성”에디터에서 EQS Query 에셋 생성:
Generator: Points around Context (Querier 주변 360도 샘플링) └─ Radius: 500, Points per circle: 16, Num circles: 3
Tests: 1. Trace (Line of Sight to Player) Weight: 1.0 Filter: Must Pass 2. Distance to Player Weight: -1.0 (가까울수록 낮은 점수 = 멀리서 사격) 3. Distance to Querier Weight: 0.5 (너무 멀리 이동하지 않도록)C++ 커스텀 Generator
섹션 제목: “C++ 커스텀 Generator”UCLASS()class UCoverPointGenerator : public UEnvQueryGenerator_ProjectedPoints { GENERATED_BODY()
UPROPERTY(EditDefaultsOnly) float SearchRadius = 600.f; UPROPERTY(EditDefaultsOnly) int32 NumPoints = 24;
public: virtual void GenerateItems(FEnvQueryInstance& QueryInstance) const override { UObject* Querier = QueryInstance.Owner.Get(); if (!Querier) return;
FVector Origin = Cast<AActor>(Querier)->GetActorLocation();
TArray<FNavLocation> NavPoints; for (int32 i = 0; i < NumPoints; i++) { float Angle = (360.f / NumPoints) * i; float Rad = FMath::DegreesToRadians(Angle); FVector Offset(FMath::Cos(Rad), FMath::Sin(Rad), 0.f); FVector Point = Origin + Offset * SearchRadius;
// NavMesh 위 실제 위치로 투영 FNavLocation NavLoc; if (GetWorld()->GetNavigationSystem()->ProjectPointToNavigation( Point, NavLoc, FVector(50, 50, 50))) NavPoints.Add(NavLoc); }
AddItemData<UEnvQueryItemType_Point>(QueryInstance, NavPoints); }};C++ 커스텀 Test
섹션 제목: “C++ 커스텀 Test”UCLASS()class UCoverTest : public UEnvQueryTest { GENERATED_BODY()
public: UCoverTest() { Cost = EEnvTestCost::High; ValidItemType = UEnvQueryItemType_Point::StaticClass(); SetWorkOnFloatValues(true); }
virtual void RunTest(FEnvQueryInstance& QueryInstance, const FEnvQueryContextData& ContextData) const override { UObject* Querier = QueryInstance.Owner.Get(); ACharacter* Char = Cast<ACharacter>(Querier); if (!Char) return;
APlayerController* PC = UGameplayStatics::GetPlayerController(GetWorld(), 0); if (!PC || !PC->GetPawn()) return;
FVector PlayerLoc = PC->GetPawn()->GetActorLocation();
for (FEnvQueryInstance::ItemIterator It(this, QueryInstance); It; ++It) { FVector TestPoint = GetItemLocation(QueryInstance, It.GetIndex());
// 플레이어에서 테스트 위치로 시야 차단 여부 FHitResult Hit; bool bBlocked = GetWorld()->LineTraceSingleByChannel( Hit, PlayerLoc, TestPoint, ECC_Visibility);
It.SetScore(TestPurpose, FilterType, bBlocked ? 1.f : 0.f, // 시야 차단 = 좋은 엄폐물 0.f, 1.f); } }};Behavior Tree 연동
섹션 제목: “Behavior Tree 연동”Behavior Tree: Sequence └─ EQS Query Task ├─ Query Asset: EQS_FindCoverPoint ├─ Query Context: EQSContext_Player (타겟 기준) └─ Blackboard Key: BB_MoveTarget └─ Move To (BB_MoveTarget)C++에서 런타임 쿼리 실행
섹션 제목: “C++에서 런타임 쿼리 실행”void AMyAIController::FindBestCoverPoint() { FEnvQueryRequest QueryRequest(CoverQueryAsset, this); QueryRequest.Execute(EEnvQueryRunMode::SingleResult, this, &AMyAIController::OnCoverQueryFinished);}
void AMyAIController::OnCoverQueryFinished(TSharedPtr<FEnvQueryResult> Result) { if (Result->IsSuccessful()) { FVector BestLoc = Result->GetItemAsLocation(0); GetBlackboardComponent()->SetValueAsVector("MoveTarget", BestLoc); MoveToLocation(BestLoc); }}- EQS는 Generator(위치 생성) + Test(점수 평가) 파이프라인
- C++ 커스텀 Generator로 게임 특화 샘플링 전략 구현
- C++ 커스텀 Test로 Line Trace, 거리, 시야 등 점수 계산
- Behavior Tree의
RunEQSQuery태스크로 결과를 블랙보드에 저장 SingleResult모드로 최고 점수 위치만 빠르게 반환