본문 바로가기

환경 쿼리와 트레이싱 기본 활용

라인·박스·구·캡슐 트레이스로 시야·명중·상호작용 대상을 탐색하고 쿼리 결과를 디버깅합니다.

On Hit와 On Overlap은 무언가에 실제로 닿은 다음에야 알려 줍니다. 그런데 게임에서는 닿기 전에 알아야 하는 일이 많습니다. 총을 쏘는 순간 총알이 어디에 맞을지, 플레이어가 바라보는 곳에 문이 있는지, AI와 플레이어 사이에 벽이 있는지, 새 건물을 놓으려는 자리가 비어 있는지 같은 것입니다. 이런 정보는 기다려서 얻는 것이 아니라 엔진에 직접 물어서 얻어야 합니다.

앞 글에서 콜리전 이벤트를 다뤘다면, 이 글에서는 충돌이 일어나기 전에 주변을 탐색하는 쿼리(query)와 트레이스(trace)를 다룹니다. 라인 트레이스와 도형 트레이스의 입력과 결과를 읽는 법을 익히고, 좌표 하나로 라인과 구의 결과가 어떻게 다른지 직접 계산해 봅니다.


환경 쿼리와 트레이싱

환경 쿼리(environment query)는 특정 조건에 맞는 환경 정보를 묻는 일을 넓게 가리키는 말입니다. 8장에서 다룬 EQS(Environment Query System)는 후보 위치를 만들고 걸러 내고 점수를 매기는 시스템이었는데, 이 글에서는 그와 구분해 가장 기본적인 충돌 쿼리인 트레이스를 다룹니다.

트레이싱(tracing)은 가상의 선이나 도형을 월드에 쏘아, 그 경로가 어떤 콜리전 오브젝트와 교차하는지 확인하는 방법입니다. 이렇게 한 번 쏘아 보는 검사 하나는 트레이스(trace)라고 부릅니다. 물리 시뮬레이션의 결과와 상관없이 콜리전 형상을 기준으로 바로 판정하므로, 지금 이 순간의 환경 정보를 빠르게 얻을 수 있습니다.

트레이스가 쓰이는 곳은 다양합니다. AI가 플레이어를 볼 수 있는지, 플레이어가 엄폐물 뒤에 있는지 같은 시야 검사에 씁니다. 선분이나 움직이는 도형을 가로막는 충돌이 있는지 보는 장애물 검사에도 쓰지만, 내비메시 위로 걸어서 갈 수 있는지는 경로 찾기 검사로 따로 판단합니다. 총알이나 스킬이 적에게 맞았는지, 범위 안에 적이 있는지 같은 공격 판정, 플레이어 시선 앞의 문이나 스위치, 아이템을 찾는 상호작용 감지, 물체를 놓으려는 자리가 다른 물체와 겹치지 않는지 보는 배치 검사에도 씁니다.

AI가 위치를 고를 때는 EQS와 트레이스를 함께 쓰면 좋습니다. EQS에서 후보 위치의 시야나 충돌 조건을 검사할 때 트레이스를 활용할 수 있기 때문입니다. 다만 후보까지 걸어서 갈 수 있는지는 경로 찾기 테스트 같은 다른 검사로 구분해 확인합니다.


라인 트레이스: 직선 하나로 묻기

라인 트레이스(line trace)는 가장 기본적인 트레이스로, 한 지점에서 다른 지점까지 직선을 쏩니다. 노드는 두 가지입니다. Line Trace By Channel은 지정한 트레이스 채널로 검사하고, Line Trace For Objects는 지정한 충돌 Object Type만 대상으로 합니다. 공식 문서의 설명대로 채널은 가시성이나 카메라처럼 주로 트레이스에 쓰이고, Object Type은 액터의 물리 종류입니다. 특정 블루프린트 클래스만 찾고 싶다면 이 노드들에 클래스 필터가 없으므로, 돌려받은 액터를 따로 검사합니다.

입력 핀은 다음과 같습니다. Start와 End는 트레이스가 시작하고 끝나는 월드 위치(Vector)이고, Trace Channel이나 Object Types는 어떤 채널이나 Object Type을 검사할지 정합니다. Actors to Ignore에는 트레이스를 쏘는 액터 자신처럼 무시할 액터의 배열을 넣고, Draw Debug Type은 디버그 선을 그릴지와 얼마나 남길지를 None, For One Frame, For Duration, Persistent 가운데에서 고릅니다.

결과는 Out Hit 구조체로 나오며, Break Hit Result로 풀어 읽습니다.

출력뜻
Blocking Hit트레이스가 무언가에 막혔는지(Boolean)입니다.
Hit Actor부딪힌 액터입니다.
Hit Component부딪힌 프리미티브 컴포넌트입니다.
Location충돌한 순간 트레이스 형상의 위치입니다. 도형 트레이스에서는 형상의 중심 위치와 관계됩니다.
Normal스윕한 형상 쪽에서 본 충돌 법선입니다.
Distance시작점부터 Location까지의 거리입니다.
Impact Point, Impact Normal실제 표면의 접촉점과 법선입니다. 라인에서는 Location, Normal과 같지만 도형 트레이스에서는 다를 수 있습니다.

총알의 명중 판정

라인 트레이스로 총알의 명중을 판정해 보겠습니다. Start는 총구 위치, End는 총구 위치에 총구 방향과 최대 사거리를 곱한 벡터를 더한 위치입니다. Trace Channel은 Visibility나 직접 만든 Projectile 채널로 정합니다. 맞은 Hit Actor가 플레이어나 적이라면 피해를 주고, 맞은 표면에 총알 자국 이펙트를 만듭니다. 아래는 이 노드 연결을 글로 적은 의사 코드입니다.

(Shoot Event)
-> Line Trace By Channel (Start: MuzzleLocation, End: MuzzleLocation + ForwardVector * 5000, Trace Channel: Visibility)
-> Branch (Condition: Return Value)
    -> True:
        -> Break Hit Result (Hit Actor, Impact Point, Impact Normal)
        -> Spawn System at Location (Location: Impact Point, Rotation: Make Rot from Z(Impact Normal))
        -> Apply Damage (Damaged Actor: Hit Actor, Base Damage: 20)

Line Trace By Channel의 Return Value는 Block Hit를 찾았을 때 참이므로, 먼저 이 값으로 분기합니다. 참인 경로에서만 Break Hit Result로 맞은 액터와 접촉점, 법선을 꺼내고, 접촉점에 법선 방향으로 이펙트를 만든 뒤 Apply Damage로 맞은 액터에 20의 피해를 줍니다. 이펙트 노드는 UE5의 나이아가라(Niagara) 노드인 Spawn System at Location이고, Apply Damage에 피해 대상과 피해량을 넘기는 입력 핀의 이름은 Damaged Actor와 Base Damage입니다. 멀티플레이어라면 최종 피해는 서버가 검증한 경로에서 적용합니다.

AI의 시야 검사

AI의 시야도 라인 트레이스로 검사합니다. Start는 AI의 눈 위치, End는 플레이어 캐릭터의 위치로 두고, Trace Channel은 Visibility로 정하며, Actors to Ignore에 AI 자신을 넣습니다. 결과의 Blocking Hit가 참인데 Hit Actor가 플레이어가 아니라면, 둘 사이에 다른 장애물이 있다는 뜻이므로 AI는 플레이어를 볼 수 없습니다.


도형 트레이스: 굵기가 있는 탐색

박스, 구, 캡슐 트레이스는 선 대신 도형을 월드에 쓸고 지나가며 콜리전을 찾습니다. 공식 문서는 도형 트레이스가 라인 트레이스처럼 동작하되 부피를 쓸고 지나간다고 설명합니다. 선 하나로는 빠져나갈 틈이 생기는 근접 공격 범위나, 넓은 영역의 충돌을 검사할 때 알맞습니다.

노드는 Box Trace By Channel, Sphere Trace By Channel, Capsule Trace By Channel이며, For Objects 버전도 있습니다. 입력은 라인 트레이스의 Start, End에 도형의 크기가 더해집니다. 박스는 Half Size, 구는 Radius, 캡슐은 Radius와 Half Height를 받습니다. 박스에는 형상의 회전인 Orientation 입력도 있지만, 기본 블루프린트 캡슐 트레이스에는 회전 입력이 없습니다. 나머지 핀은 라인 트레이스와 비슷합니다.

결과는 단일 버전이면 Out Hit 하나, Multi 버전이면 Out Hits 배열입니다. 두 Multi 버전은 돌려주는 범위가 다릅니다. Multi ... By Channel은 첫 Block Hit까지의 Overlap과 그 Block Hit까지만 돌려주고, Multi ... For Objects는 지정한 Object Type에 맞고 트레이스 쿼리에 응답하는 결과를 모두 돌려줍니다.

근접 공격과 배치 검사

근접 공격의 범위를 판정할 때는 캐릭터 앞쪽으로 Sphere Trace나 Box Trace를 짧게 쏩니다. 폰들을 대상으로 하려면 Sphere Trace For Objects 같은 노드의 Object Types에 Pawn을 넣습니다. Pawn은 기본 Object Type일 뿐 "적"만 뜻하는 트레이스 채널이 아니므로, Multi 결과에서 유효한 적인지 따로 판별합니다. 또 한 액터가 캡슐과 메시처럼 여러 컴포넌트로 검출될 수 있으므로, 같은 액터를 중복에서 제거한 뒤 액터마다 한 번씩 피해를 줍니다.

배치 검사에서는 새 물체를 놓으려는 위치에 Box Trace를 쏘아 기존 물체와 겹치는지 확인합니다. 겹친다면 미리보기를 빨간색으로 표시하거나 배치할 수 없다고 처리합니다.


라인과 구의 결과를 좌표로 계산하기

TRACE QUERY · LOCATION · IMPACT POINT

구 스윕의 중심 위치와 표면 접촉점은 다를 수 있습니다

정면의 평평한 벽에 초기 겹침 없이 도달하는 단순 개념도입니다. Trace는 충돌 결과를 조회하며, 표시된 구를 물리적으로 밀거나 튕기지 않습니다.

Line Trace · 선분

선 트레이스의 벽 접촉점선분이 벽 표면에서 막히면 Location과 Impact Point가 같은 접촉 위치다.벽Start접촉 위치
선 트레이스의 표면 접촉 위치초기 겹침 없는 정면의 평평한 벽이다. 선에서는 Location과ImpactPoint가 같은 표면이고 구에서는 반지름30cm만큼 중심이 앞선다. 조회는 물리 반응이 아니다.벽Start같은 접촉점

Location = Impact Point: 벽 표면의 접촉 위치입니다. 두 법선도 라인에서는 일치합니다.

Sphere Trace · 구 형상의 스윕

구 스윕의 중심과 표면 접촉점구 중심이 벽 앞 30cm에서 멈췄을 때 Location은 중심, Impact Point는 벽 표면이다. 초기 겹침 없는 정면 충돌의 단순 개념도다.벽Start중심접촉
구 스윕의 중심과 접촉 위치초기 겹침 없는 정면의 평평한 벽이다. 선에서는 Location과ImpactPoint가 같은 표면이고 구에서는 반지름30cm만큼 중심이 앞선다. 조회는 물리 반응이 아니다.벽Start중심접촉

Location: 충돌 시점의 구 중심 위치입니다.

Impact Point: 벽과 닿은 표면 접촉점입니다. 충돌 크기와 형상에 따라 중심과 다릅니다.

형상 위치 · 접촉점 · 경로 도달 가능성을 구분합니다

Blocking Hit 결과를 분기하고 Hit Actor/Component를 검증합니다. Shape Trace의 Normal과 Impact Normal도 역할이 다를 수 있습니다. 여기의 두 점 설명을 초기 겹침 등 모든 충돌 사례에 기계적으로 적용하지 않습니다.

Trace로 선분/형상의 막힘을 검사하는 것과 NavMesh로 걸어서 도달할 수 있는지 검사하는 것은 별도입니다. For One Frame은 디버그 표시 수명이며 매 프레임 조회를 자동 실행하지 않습니다.

위 그림은 정면의 평평한 벽에 처음부터 겹쳐 있지 않은 상태로 도달하는 단순한 경우를 두 트레이스로 비교합니다. 위쪽의 라인 트레이스에서는 선분이 벽 표면에서 막히므로 Location과 Impact Point가 같은 접촉 위치이고, 두 법선도 일치합니다. 아래쪽의 구 스윕에서는 구의 중심이 벽 앞 30 cm에서 멈추므로, Location은 그때의 구 중심이고 Impact Point는 벽과 닿은 표면의 점입니다. 공식 문서도 Location은 트레이스의 형상에 따라 보정된 위치이고, Impact Point는 형상을 뺀 실제 충돌 지점이라고 설명합니다.

그림의 주석은 세 가지를 덧붙입니다. 트레이스는 충돌 결과를 묻는 것일 뿐, 그림의 구를 물리적으로 밀거나 튕기지 않습니다. 결과를 쓰기 전에는 Blocking Hit로 분기하고 Hit Actor와 Hit Component를 검증하며, 도형 트레이스에서는 Normal과 Impact Normal의 역할도 다를 수 있습니다. 그리고 트레이스로 선분이나 형상의 막힘을 검사하는 것과 내비메시로 걸어서 도달할 수 있는지 검사하는 것은 별개이고, For One Frame은 디버그 표시가 남는 시간일 뿐 매 프레임 쿼리를 자동으로 실행하지 않습니다. 이 두 점의 설명을 처음부터 겹쳐 있는 경우 같은 모든 충돌에 기계적으로 적용하지는 않습니다.

이제 그림의 상황을 좌표로 계산해 보겠습니다. 월드의 길이는 cm 단위이고, Start는 (0, 0, 0), 단위 방향은 (1, 0, 0), 거리는 5,000 cm입니다. 따라서 End는 (5000, 0, 0)이고 사거리는 50 m입니다. 앞의 총알 의사 코드에서 5000이 이 거리가 되려면 방향 벡터가 길이 1로 정규화되어 있어야 합니다. 처음 만나는 Block 벽은 x = 1000인 평평한 면이고, 시작 위치가 벽 안에 있지 않으며 다른 충돌은 없다고 가정합니다.

이 조건에서 라인 트레이스의 Location과 Impact Point는 모두 (1000, 0, 0)입니다. 같은 경로로 반지름 30 cm의 구를 스윕하면, 구의 앞쪽 표면이 벽에 닿는 순간 중심은 벽보다 반지름만큼 앞인 (970, 0, 0)에 있습니다. 그래서 Location은 (970, 0, 0), Impact Point는 (1000, 0, 0)입니다. 총알 자국을 벽 표면에 붙이고 싶다면 Impact Point를, 구가 멈춘 자리에 무언가를 놓고 싶다면 Location을 써야 하듯이, 어떤 출력 핀을 쓸지는 무엇을 하려는지에 따라 달라집니다. 어느 쪽이든 먼저 Blocking Hit로 분기하고 유효한 Hit Actor와 Hit Component를 확인한 뒤 결과를 씁니다. 맞지 않은 경로에서 나온 기본값의 Out Hit를 실제 명중으로 읽어 피해나 접촉 효과를 만들지 않습니다.


트레이스 디버깅

트레이스를 디버깅할 때 가장 먼저 할 일은 Draw Debug Type 핀을 For One Frame이나 For Duration으로 바꿔 선이나 도형을 눈으로 보는 것입니다. For One Frame은 호출한 쿼리를 한 프레임 동안만 그립니다. 이 옵션이 쿼리 자체를 매 프레임 실행하는 것은 아니므로, 계속 보이게 하려면 쿼리를 계속 호출해야 합니다. For Duration은 정해진 시간 동안 그려 두므로, 움직이는 동안 트레이스가 어떻게 바뀌는지 따라가 볼 수 있습니다.


트레이스를 쓸 때 따질 것

트레이스의 결과는 대상 액터의 콜리전 설정, 곧 Object Type과 Trace Responses에 따라 달라집니다. 원하는 대상이 맞지 않는다면 트레이스 노드보다 대상의 콜리전 설정부터 다시 확인합니다.

트레이스는 비교적 가벼운 연산이지만, 많은 액터가 매 프레임 긴 거리를 트레이스하면 성능에 영향을 줄 수 있습니다. 필요할 때만 실행하고 거리를 줄이는 것이 좋으며, 실제 비용은 형상, 거리, 횟수, 충돌 형상의 복잡도에 따라 달라지므로 측정해서 판단합니다. 트레이스는 결과를 조회할 뿐 물체를 튕기는 물리 반응을 일으키지 않는다는 점도 기억합니다.

멀티플레이어에서는 중요한 트레이스 로직을 서버에서 실행하는 편이 안전합니다. 클라이언트의 트레이스 결과로 중요한 게임플레이를 처리하면 클라이언트가 결과를 조작할 여지가 생기기 때문입니다. 총알의 명중 판정이 대표적인 예이며, 화면의 이펙트처럼 즉각적인 반응이 필요한 부분만 클라이언트에서 먼저 보여 주고 확정은 서버가 합니다.


정리

  • 트레이스는 선이나 도형을 쏘아 경로의 충돌을 묻는 쿼리이며, EQS처럼 후보를 평가하는 시스템이나 경로 찾기 검사와 구분합니다.
  • By Channel은 트레이스 채널로, For Objects는 Object Type으로 대상을 고르며, 클래스 필터는 결과 액터를 따로 검사합니다.
  • Multi ... By Channel은 첫 Block Hit까지만, Multi ... For Objects는 맞는 결과를 모두 돌려줍니다.
  • 라인에서는 Location과 Impact Point가 같지만, 도형 트레이스에서 Location은 형상의 중심이고 Impact Point는 표면의 접촉점입니다.
  • 결과를 쓰기 전에 Blocking Hit나 Return Value로 분기하고, Draw Debug Type으로 쿼리를 눈으로 확인합니다.

연습 문제

1. 다른 좌표에서 라인과 구 비교하기

시작점을 (100, 0, 0), 끝점을 (2100, 0, 0)으로 옮겼습니다. 처음 만나는 벽은 x = 1500이고, 구의 반지름은 50 cm입니다. 처음부터 겹쳐 있지 않고 정면으로 이동하며, 다른 충돌은 없습니다. 구 스윕의 Location, Impact Point, 그리고 중심이 시작점에서 이동한 거리를 계산하고, 라인 트레이스의 결과와 비교해 보세요.

해설 보기

구 중심은 벽보다 반지름만큼 앞인 (1450, 0, 0)에서 멈추므로 Location은 (1450, 0, 0)이고, 표면 접촉점인 Impact Point는 (1500, 0, 0)입니다. 중심이 이동한 거리는 1450 - 100 = 1350 cm입니다.

라인 트레이스는 (1500, 0, 0)에서 접촉하므로 Location과 Impact Point가 모두 (1500, 0, 0)이고, 이동 거리는 1500 - 100 = 1400 cm입니다. 두 결과의 50 cm 차이는 쿼리 형상의 반지름에서 나온 것이며, 실제 구 액터가 그만큼 이동하거나 튕겼다는 물리 결과가 아닙니다. 비스듬한 표면이나 처음부터 겹쳐 있는 경우는 이 단순한 좌표 계산과 따로 생각해야 합니다.

2. 두 번 들어간 근접 공격 피해

Multi Sphere Trace For Objects의 Object Types에 Pawn을 넣고, Out Hits 배열을 돌며 각 결과의 Hit Actor에 20의 피해를 줬습니다. 그런데 적 한 명의 체력이 40 줄어드는 경우가 있습니다. 왜 그럴 수 있을까요? 어떻게 고쳐야 할까요?

해설 보기

한 액터가 캡슐 컴포넌트와 스켈레탈 메시처럼 여러 컴포넌트로 검출되면, Out Hits에 같은 액터의 결과가 두 번 들어갈 수 있습니다. 결과마다 피해를 주면 같은 적에게 두 번 피해가 들어가 40이 줄어듭니다.

배열을 돌면서 이미 처리한 액터를 별도의 배열에 모으고, 그 배열에 없는 액터에만 피해를 준 뒤 추가하는 식으로 액터 단위의 중복을 제거합니다. 또 Pawn Object Type에는 아군이나 플레이어 자신도 포함될 수 있으므로, 실제로 피해를 줄 적인지도 함께 확인합니다.

3. 늘 가려졌다고 나오는 시야 검사

AI의 눈 위치에서 플레이어 위치까지 Line Trace By Channel(Visibility)을 쏘고, Hit Actor가 플레이어가 아니면 "보이지 않음"으로 처리했습니다. 그런데 플레이어가 탁 트인 곳에 있어도 항상 보이지 않는다고 나옵니다. 무엇을 먼저 의심해야 할까요?

해설 보기

먼저 Actors to Ignore에 AI 자신을 넣었는지 확인합니다. 눈 위치가 AI의 캡슐이나 메시 안에서 시작한다면, 트레이스가 가장 먼저 AI 자신에게 막혀 Hit Actor가 AI가 되기 때문입니다.

Draw Debug Type을 For Duration으로 바꾸면 선이 어디서 막혔는지 눈으로 볼 수 있습니다. AI 자신을 무시하도록 바꿨는데도 막힌다면, 플레이어 쪽 컴포넌트가 Visibility 채널을 Block하는지와, 그 사이에 보이지 않는 콜리전이 있는지를 확인합니다.

채널과 Object Type의 차이, Multi 트레이스의 결과 범위, Location과 Impact Point의 뜻은 언리얼 엔진 공식 문서의 트레이스 개요에서 확인할 수 있습니다.

다음 글 날씨 및 시간 시스템의 기초에서는 태양의 각도로 하루의 시간을 표현하고, 비나 안개 같은 날씨를 바꾸는 기본 구조를 만듭니다.