환경에 대한 데이터를 수집하고, 그 데이터에 대해 질문을 던져서 가장 적절한 결과를 찾는 시스템.
이때 결과는 보통 위치이거나 Actor
- 플레이어를 볼 수 있는 위치가 어디인가
- 가장 가까운 엄폐물이 어디인가
- 가장 위협적인 적은 누구인가
- 회복 아이템 중 지금 가장 가까운 것은 무엇인가
후보를 만든 뒤 그 후보를 평가해서 가장 높은 점수를 고르는 시스템
기본 구조
GeneratorContextTest
Generator가 후보를 만들고, Context가 기준점을 제공하고, Test가 그 후보를 필터링하거나 점수화한다.
- 먼저 후보를 만든다.
- 그 후보를 어떤 기준점과 비교한다.
- 비교 결과로 걸러내거나 점수를 준다.
- 가장 좋은 결과를 반환한다.
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
QuerierItem
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 실행 흐름
- Query가 실행된다.
- Generator가 후보
Item을 만든다. - 각 Test가 Context를 기준으로 후보를 검사한다.
- 일부 후보는 탈락하고, 일부 후보는 점수를 받는다.
- 최종적으로 가장 높은 결과가 반환된다.
이 반환값은 위치일 수도 있고 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 |