환경에 대한 데이터를 수집하고, 그 데이터에 대해 질문을 던져서 가장 적절한 결과를 찾는 시스템.
이때 결과는 보통 위치이거나 Actor

  • 플레이어를 볼 수 있는 위치가 어디인가
  • 가장 가까운 엄폐물이 어디인가
  • 가장 위협적인 적은 누구인가
  • 회복 아이템 중 지금 가장 가까운 것은 무엇인가

후보를 만든 뒤 그 후보를 평가해서 가장 높은 점수를 고르는 시스템

기본 구조

  • Generator
  • Context
  • Test

Generator가 후보를 만들고, Context가 기준점을 제공하고, Test가 그 후보를 필터링하거나 점수화한다.

  1. 먼저 후보를 만든다.
  2. 그 후보를 어떤 기준점과 비교한다.
  3. 비교 결과로 걸러내거나 점수를 준다.
  4. 가장 좋은 결과를 반환한다.

Generator

Generator는 EQS의 시작점.
테스트하고 가중치를 줄 대상인 Item을 만들어내는 단계

Item

  • 위치
  • Actor

예를 들어:

  • Points: Grid는 어떤 기준 위치 주변에 여러 점을 만든다
  • Actors Of Class는 특정 클래스의 Actor들을 후보로 가져온다
  • Current Location은 현재 위치를 후보로 쓴다

Generator가 어떤 후보를 만들어내느냐에 따라, 뒤에 붙는 Test의 의미도 완전히 달라진다.
엄폐 지점을 찾고 싶은데 후보가 아이템 액터들이면 당연히 결과가 어긋난다.
그래서 EQS를 짤 때는 Test보다 먼저 Generator가 지금 문제에 맞는 후보를 만들고 있는지부터 보는 편이 맞다.

Root에 여러 Generator를 연결할 수 있어도 실제 Query에서는 가장 왼쪽 Generator만 사용된다.

Context

Context는 EQS가 비교할 때 쓰는 기준점이다.
Tests나 Generators가 참조하는 frame of reference

가장 기본적인 Context

  • Querier
  • Item

Querier는 이 Query를 실행한 쪽
보통 AI Controller가 소유한 Pawn이 기준.

Item은 Generator가 만든 후보
각 후보 지점이나 각 후보 Actor

예를 들어 거리 Test를 붙였을 때,

  • Context를 Querier로 두면 “AI 자신과의 거리”를 계산하고
  • Context를 플레이어를 반환하는 custom Context로 두면 “플레이어와의 거리”를 계산하게 된다

같은 Test여도 Context가 바뀌면 완전히 다른 질문이 된다.
그래서 EQS를 읽을 때는 Test 이름만 보면 안 되고, 그 Test가 어떤 Context를 기준으로 계산되는지 같이 봐야 한다.

custom Context를 EnvQueryContext_BlueprintBase나 C++로 만들 수 있다.
프로젝트가 커지면 기본 Querier만으로는 부족해서, 플레이어, 마지막 탐지 위치, 팀 리더, 위협 대상 같은 기준점을 별도 Context로 분리한다.

Test

Test는 Generator가 만든 후보들 중 무엇이 더 좋은지를 결정하는 단계.
Test가 Item을 필터링하거나 점수화해서 최종적으로 가장 좋은 결과를 고른다고 설명한다.

Test는 두 단계가 있음

필터링은 말 그대로 후보를 제외하는 단계다.
예를 들어 시야가 막힌 위치는 탈락, 너무 먼 위치는 탈락 같은 방식이다.

스코어링은 탈락시키지 않고 점수를 매기는 단계다.
예를 들어:

  • 가까울수록 높은 점수
  • 플레이어가 잘 보일수록 높은 점수
  • 엄폐물 뒤에 있을수록 높은 점수

공식 문서에는 filter test가 scoring보다 먼저 적용된다고 설명한다.
이 순서가 중요한 이유는, 먼저 쓸모없는 후보를 줄여야 이후 점수 계산 비용도 줄어들기 때문이다.

EQS 실행 흐름

  1. Query가 실행된다.
  2. Generator가 후보 Item을 만든다.
  3. 각 Test가 Context를 기준으로 후보를 검사한다.
  4. 일부 후보는 탈락하고, 일부 후보는 점수를 받는다.
  5. 최종적으로 가장 높은 결과가 반환된다.

이 반환값은 위치일 수도 있고 Actor일 수도 있다.
Query를 어디에 연결하느냐에 따라 바로 이동 대상으로 쓸 수도 있고, 공격 대상으로 쓸 수도 있다.

EQS Query는 Behavior Tree에서 호출해서, 테스트 결과를 기반으로 다음 행동을 결정하는 데 쓴다.

Behavior Tree와의 연결

EQS는 보통 혼자 쓰이기보다 Behavior Tree에서 연결해서 쓴다.
Run EQS Query Task로 호출하는 구조가 기본이다.
Behavior Tree는 행동 흐름을 결정하고,
EQS는 그 흐름 안에서 필요한 위치나 대상을 계산한다

Behavior Tree가 “엄폐하러 가라”를 결정하면,
EQS는 “어디로 가는 게 가장 좋은가”를 계산하는 식이다.

이렇게 나누면 Decision과 Search가 섞이지 않는다.
Behavior Tree 안에서 조건문과 거리 계산만으로 전부 해결하려고 하면 금방 노드가 지저분해진다.
반대로 EQS를 붙이면 선택 기준을 Query 자산으로 분리해서 볼 수 있다.

EQS가 잘 맞는 경우

  • 후보가 여러 개 있다
  • 그 후보 중 가장 좋은 것을 골라야 한다
  • 기준이 하나가 아니라 여러 개다
  • 상황에 따라 점수 기준이 중요하다

예를 들어 가장 가까운 엄폐물 하나만 찾는 게 아니라,

  • 플레이어가 보이지 않아야 하고
  • 너무 멀면 안 되고
  • 현재 AI 위치에서도 지나치게 멀지 않아야 하고
  • 가능하면 측면 각도를 만드는 위치면 더 좋다

이런 식으로 조건이 누적되면 EQS가 훨씬 자연스럽다.

디버깅

EQS Query를 Editor 안에서 preview할 수 있고, 결과는 디버그 구체로 시각화된다.
EQS Testing Pawn을 통해 Query 결과를 확인하는 흐름도 같이 제공한다.

EQS가 결과 숫자만 보고 이해하기 어려운 시스템이기 때문에
왜 저 위치가 선택됐는가를 눈으로 보는 게 훨씬 빠르다.

EQS 디버깅에서는

  • Generator가 후보를 너무 많이 만드는가
  • Context가 의도한 기준점을 보고 있는가
  • Test가 필터로 작동하는지, 점수로 작동하는지
  • 너무 강한 필터 때문에 결과가 비는가
  • 점수 스케일이 한 Test에 지나치게 쏠리는가

를 확인할 수 있다.

커스텀 확장

  • custom Generator는 Blueprint나 C++로 만들 수 있다
  • custom Context도 Blueprint나 C++로 만들 수 있다
  • custom Test는 C++로 만든다

프로토타입 단계에서는 Blueprint Generator나 Context만으로도 충분할 수 있지만,
Test는 결국 평가 규칙의 핵심이라서 C++ 확장이 필요한 순간이 온다.

Generator는 Blueprint보다 C++가 더 빠르다.
그래서 Query가 자주 돌거나 후보 수가 많아지는 경우에는 성능도 같이 봐야 한다.

'TIL' 카테고리의 다른 글

Clean Code  (0) 2026.06.09
Unreal Engine AI Controller  (0) 2026.06.05
ProjectFT 시작, 초기 설정  (0) 2026.06.04
Unreal Component  (0) 2026.06.02
TEXT RPG(DIABL5) 발표하며 회고...  (0) 2026.05.29