라인 트레이스 (Trace) 시스템
시작점·끝점·채널을 지정한 Line Trace로 대상을 검출하고 FHitResult에서 충돌 정보를 읽습니다.
이전 절에서 우리는 물리 시뮬레이션과 오브젝트에 힘을 적용하는 방법을 배웠습니다.
이제 직접적인 충돌이나 오버랩 감지 없이도 게임 월드 내의 오브젝트를 탐색하고 정보를 얻는 강력한 방법인 라인 트레이스(Line Trace) 시스템에 대해 알아보겠습니다.
라인 트레이스는 레이캐스트(Raycast)라고도 불리며, 가상의 선을 쏘아 그 선이 어떤 오브젝트와 부딪혔는지 감지하는 데 사용됩니다.
트레이스는 물리 접촉을 기다리지 않고 시작점과 끝점 사이를 검사해 월드의 오브젝트를 능동적으로 탐색하는 충돌 쿼리입니다.
- 시작과 끝 결정
카메라, 총구, 캐릭터 위치에서 Start를 잡고 방향 벡터와 거리로 End를 계산합니다. Start / End
- 트레이스 필터 기준
Collision Channel 또는 Object Type으로 반응할 대상을 좁히고 자기 자신은 Query Params에서 제외합니다. Channel / Params
- HitResult를 해석한다
반환값이 true이면 Actor, Component, Location, Normal을 확인하고 데미지나 이펙트 같은 후속 처리를 연결합니다. FHitResult
- 실전에서 먼저 보는 것
좁은 판정은 Line, 여유 있는 판정은 Sphere나 Capsule을 고릅니다. Visibility, Camera, 커스텀 채널 중 목적에 맞는 응답만 검사합니다. AddIgnoredActor로 발사자나 임시 오브젝트가 결과에 섞이지 않게 합니다. Debug Draw로 방향과 히트 지점을 확인하면 채널 설정 문제를 빠르게 찾을 수 있습니다.
- 정확성
좁은 판정은 Line, 여유 있는 판정은 Sphere나 Capsule을 고릅니다.
- 필터
Visibility, Camera, 커스텀 채널 중 목적에 맞는 응답만 검사합니다.
- 예외
AddIgnoredActor로 발사자나 임시 오브젝트가 결과에 섞이지 않게 합니다.
- 검증
Debug Draw로 방향과 히트 지점을 확인하면 채널 설정 문제를 빠르게 찾을 수 있습니다.
라인 트레이스란 무엇인가요?
이 다이어그램은 라인 트레이스가 시작점, 종료점, 충돌 채널을 기준으로 월드를 검사하고 FHitResult로 결과를 돌려주는 흐름을 정리합니다.
LineTraceSingleByChannel 은 시작점, 방향, 거리, 충돌 채널, 무시 대상, 결과 구조체가 함께 맞아야 의미 있는 판정을 만든다.
- 카메라 또는 총구
Start 시야 판정과 총구 판정이 다르면 보정 기준을 명확히 둔다.
- 충돌 응답을 가진 컴포넌트
Target 대상이 해당 Trace Channel을 Block해야 결과가 채워진다.
- FHitResult를 읽는다
bHit true Actor, Component, Location, Normal, Physical Material을 후속 로직에 연결한다.
- 방향과 채널을 점검한다
Miss 화면 중앙, 총구 위치, 사거리, 대상 프리셋이 어긋났는지 본다.
- 무시 목록 누락
Self Hit AddIgnoredActor(this) 와 소유자 컴포넌트 제외 여부를 확인한다.
- 응답 규칙 불일치
Wrong Hit Block, Overlap, Ignore 설정과 Object Type이 의도와 맞는지 비교한다.
라인 트레이스(Line Trace)는 3D 공간의 시작 지점에서 종료 지점까지 가상의 선(혹은 광선)을 발사하여, 그 선이 월드 내의 어떤 오브젝트와 교차하는지 감지하는 쿼리(Query) 시스템입니다.
충돌 컴포넌트 간의 물리적 접촉이나 겹침을 기다리는 대신, 능동적으로 월드를 "스캔"하여 정보를 얻는 방식입니다.
라인 트레이스는 다음과 같은 다양한 게임플레이 상황에서 활용됩니다.
- 총알/투사체 히트 감지: 총을 발사했을 때 총알이 실제로 어떤 오브젝트에 맞았는지 정확히 판단합니다. (투사체의 빠른 속도로 인해 실제 물리 충돌은 오브젝트를 통과할 수 있으므로 트레이스가 더 정확합니다.)
- 레이저 시야/타겟팅: 플레이어가 보고 있는 정확한 지점이나 오브젝트를 식별합니다.
- AI 시야: AI 캐릭터가 전방에 장애물이 있는지, 플레이어를 볼 수 있는지 등을 판단합니다.
- 상호작용: 플레이어가 특정 오브젝트(버튼, 문 등)를 바라보고 있을 때만 상호작용 가능하도록 감지합니다.
- 지면 감지: 캐릭터가 점프 후 착지할 때 지면의 특성(재질, 높이)을 파악합니다.
- 벽 타고 움직이기: 캐릭터가 벽에 붙어 있을 때 벽면의 노멀(법선) 벡터를 얻어 벽을 따라 움직이게 합니다.
트레이스의 유형
언리얼 엔진은 다양한 형태의 트레이스 함수를 제공하며, 목적에 따라 적절한 함수를 선택해야 합니다.
- Line Trace (라인 트레이스): 가장 기본적인 형태. 점과 점 사이의 직선을 따라 충돌을 감지합니다.
- Sphere Trace (스피어 트레이스): 점과 점 사이의 경로를 따라 구체를 굴리듯이 충돌을 감지합니다. 총알처럼 얇은 오브젝트가 아닌, 더 넓은 범위의 감지가 필요할 때 유용합니다.
- Box Trace (박스 트레이스): 점과 점 사이의 경로를 따라 직육면체를 움직이듯이 충돌을 감지합니다. 넓은 면적의 장애물 감지에 좋습니다.
- Capsule Trace (캡슐 트레이스): 점과 점 사이의 경로를 따라 캡슐 형태를 움직이듯이 충돌을 감지합니다. 캐릭터 이동 시 장애물이나 지면 감지에 사용하기 좋습니다.
모든 트레이스 함수는 크게 두 가지 방식으로 호출할 수 있습니다.
- By Channel (채널별 트레이스): 특정 충돌 채널(Collision Channel)에 대해서만 충돌을 감지합니다. (예:
ECC_Visibility또는 커스텀Bullet채널) - By Object Type (오브젝트 타입별 트레이스): 특정 오브젝트 타입(Object Type)에 대해서만 충돌을 감지합니다. (예:
WorldStatic,Pawn,PhysicsBody등)
또한, 단일 히트만 감지하는 Single 버전과 여러 히트를 감지하는 Multi 버전이 있습니다.
LineTraceSingleByChannel(가장 가까운 하나의 히트만 반환)LineTraceMultiByChannel(경로상의 모든 히트 정보를 배열로 반환)
아래 기준표는 트레이스 함수의 모양, 필터 방식, 반환 개수, 디버그 확인 순서를 고를 때 함께 보면 좋습니다.
함수 이름을 외우기보다 세 질문에 답하면 필요한 쿼리가 정해진다.
- 범위
Line 정확한 선과 가장 낮은 범위 비용 Sphere · Box · Capsule 두께나 캐릭터 부피까지 검사
- 필터
By Channel 대상이 이 Trace Channel에 응답하는가? By Object Type 원하는 오브젝트 분류에 속하는가?
- 결과
Single 첫 Blocking Hit 하나 Multi 경로의 여러 Hit를 배열로
- Start → End
카메라·소켓 기준
- Query Params
Self 제외·복잡 충돌
- bool
Blocking Hit 여부
- FHitResult
Actor·Impact·Normal
C++에서 라인 트레이스 구현하기
라인 트레이스 함수들은 주로 UWorld 클래스와 UKismetSystemLibrary (블루프린트 함수 라이브러리)에 정의되어 있습니다.
C++에서는 UWorld 포인터를 통해 호출하는 것이 일반적입니다.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "MyCharacter.generated.h"
UCLASS()
class MYPROJECT_API AMyCharacter : public ACharacter
{
GENERATED_BODY()
public:
AMyCharacter();
protected:
virtual void BeginPlay() override;
public:
// 마우스 왼쪽 클릭 시 발사되는 함수 (블루프린트에서 호출 가능하도록)
UFUNCTION(BlueprintCallable, Category = "Combat")
void FireWeapon();
// 트레이스 길이를 에디터에서 조절할 수 있도록 노출
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Combat")
float TraceDistance = 10000.0f; // 100미터
};#include "MyCharacter.h"
#include "Components/CapsuleComponent.h"
#include "GameFramework/SpringArmComponent.h" // 카메라 암 컴포넌트
#include "Camera/CameraComponent.h" // 카메라 컴포넌트
#include "Kismet/KismetMathLibrary.h" // Rotator To Vector 등을 위해 (선택 사항)
#include "DrawDebugHelpers.h" // 디버그 드로잉을 위해
AMyCharacter::AMyCharacter()
{
PrimaryActorTick.bCanEverTick = true;
// ... 기존 컴포넌트 설정 (CapsuleComponent, Mesh, SpringArm, Camera 등) ...
}
void AMyCharacter::BeginPlay()
{
Super::BeginPlay();
}
void AMyCharacter::FireWeapon()
{
// 월드 포인터 확인
UWorld* World = GetWorld();
if (!World)
{
UE_LOG(LogTemp, Error, TEXT("World is null. Cannot perform trace."));
return;
}
// 1. 트레이스 시작 지점 결정: 보통 카메라 위치 또는 총구 위치
FVector StartLocation = GetFollowCamera()->GetComponentLocation();
// 또는 ACharacter의 EyeHeight 등을 사용할 수 있습니다.
// FVector StartLocation = GetMesh()->GetSocketLocation(TEXT("MuzzleSocket")); // 총구 소켓이 있다면
// 2. 트레이스 방향 및 종료 지점 결정: 카메라의 정방향으로 트레이스 쏘기
FRotator CameraRotation = GetFollowCamera()->GetComponentRotation();
FVector EndLocation = StartLocation + (CameraRotation.Vector() * TraceDistance);
// 3. 트레이스 파라미터 설정
FCollisionQueryParams TraceParams;
TraceParams.AddIgnoredActor(this); // 트레이스 시 자기 자신(캐릭터)은 무시
TraceParams.bTraceComplex = true; // 복잡한(폴리곤 단위) 충돌 메시를 사용할지 여부 (정확하지만 느림)
TraceParams.bReturnPhysicalMaterial = false; // 물리 재질 정보 반환 여부
// 4. 충돌 결과 정보 구조체
FHitResult HitResult;
// 5. 라인 트레이스 실행!
// LineTraceSingleByChannel(시작점, 끝점, 충돌 채널, 충돌 파라미터, 히트 결과)
// ECC_Visibility는 Project Settings의 Collision -> Trace Channels에서 기본으로 정의된 채널
bool bHit = World->LineTraceSingleByChannel(HitResult, StartLocation, EndLocation, ECollisionChannel::ECC_Visibility, TraceParams);
// 6. 트레이스 결과 처리
if (bHit)
{
// 뭔가에 맞았다!
UE_LOG(LogTemp, Warning, TEXT("Trace Hit: %s at %s"), *HitResult.Actor->GetName(), *HitResult.Location.ToString());
// 히트한 액터, 컴포넌트, 위치, 노멀 벡터 등의 정보에 접근
AActor* HitActor = HitResult.GetActor();
UPrimitiveComponent* HitComponent = HitResult.GetComponent();
FVector HitLocation = HitResult.Location;
FVector HitNormal = HitResult.Normal;
// 여기에 게임플레이 로직 구현 (예: 데미지 적용, 파티클 생성 등)
if (HitActor)
{
// 예시: 맞은 액터에게 데미지 적용 (추후 데미지 시스템 학습)
// UGameplayStatics::ApplyDamage(HitActor, 10.0f, GetController(), this, UDamageType::StaticClass());
}
}
else
{
// 아무것도 맞지 않았다
UE_LOG(LogTemp, Warning, TEXT("Trace Missed."));
}
// 7. 디버그 드로잉 (개발 중에 트레이스 시각화)
DrawDebugLine(World, StartLocation, EndLocation, bHit ? FColor::Red : FColor::Green, false, 5.0f, 0, 3.0f);
if (bHit)
{
DrawDebugSphere(World, HitResult.Location, 25.0f, 12, FColor::Red, false, 5.0f);
}
}FVector StartLocation,FVector EndLocation: 트레이스의 시작점과 끝점을 정의합니다. 카메라의 위치와 방향을 사용하여 플레이어의 시야 방향으로 트레이스를 쏘는 것이 일반적입니다.FCollisionQueryParams TraceParams: 트레이스에 대한 추가 설정을 정의합니다.AddIgnoredActor(this): 트레이스 시 특정 액터(여기서는 자기 자신)를 무시하여, 자기 자신과 충돌하는 것을 방지합니다.bTraceComplex:true로 설정하면 메시의 모든 삼각형과 정확하게 충돌 검사를 수행합니다.false일 경우 최적화된 단순화된 충돌 메시(Convex Hull, Sphere, Box 등)를 사용합니다. 성능상false가 좋지만, 정교한 충돌이 필요할 때는true로 설정합니다.
FHitResult HitResult: 트레이스 결과가 저장되는 구조체입니다. 충돌이 발생하면 히트한 액터, 컴포넌트, 충돌 위치, 충돌 노멀(법선) 벡터 등 다양한 정보가 채워집니다.World->LineTraceSingleByChannel(...): 실제 라인 트레이스를 실행하는 함수입니다.ECollisionChannel::ECC_Visibility:Project Settings>Collision의Trace Channels에서 정의된Visibility채널에 대해서만 트레이스를 수행합니다. 이 채널에Block또는Overlap반응을 보이는 오브젝트만 감지됩니다.
DrawDebugLine/DrawDebugSphere: 개발 및 디버깅 목적으로 뷰포트에 트레이스 선과 히트 지점을 시각적으로 표시해줍니다. 게임 출시 시에는 제거하거나 디버그 빌드에서만 활성화해야 합니다.
충돌 채널과 트레이스
라인 트레이스는 충돌 채널 시스템과 밀접하게 연관되어 있습니다.
Project Settings > Collision 에는 Object Channels와 Trace Channels가 있습니다.
Object Channels: 액터/컴포넌트 자체가 어떤 타입의 오브젝트인지 정의합니다. (예:Pawn,WorldStatic,PhysicsBody)Trace Channels: 라인 트레이스가 어떤 종류의 가상 선인지를 정의합니다. (예:Visibility,Camera)
트레이스를 수행할 때, 트레이스 함수에 지정된 Trace Channel과, 월드 내 각 오브젝트의 Object Channel 또는 Collision Profile 간의 상호작용 규칙에 따라 충돌 여부가 결정됩니다.
즉, 트레이스 채널이 Block 또는 Overlap으로 설정된 오브젝트만 감지됩니다.
트레이스 쿼리 타입 (Trace Query Types)
트레이스 함수를 호출하기 전에, 트레이스가 어떤 종류의 오브젝트를 감지할 것인지 설정하는 것이 중요합니다.
ECollisionChannel::ECC_WorldStatic: 정적 월드 지오메트리만 감지ECollisionChannel::ECC_WorldDynamic: 동적 월드 오브젝트만 감지ECollisionChannel::ECC_Pawn: 폰(캐릭터, AI)만 감지ECollisionChannel::ECC_Visibility:Visibility채널에 대해Block또는Overlap으로 설정된 모든 오브젝트 감지 (가장 일반적인 시야/총알 트레이스)- 커스텀 트레이스 채널:
Project Settings>Collision에서 직접 생성한Trace Channels(예:ECC_GameTraceChannel1,ECC_GameTraceChannel2...)
트레이스는 모양, 채널, 결과 처리 방식을 함께 고르는 기능이므로 아래 기준으로 먼저 선택지를 좁힐 수 있습니다.
감지 폭, 충돌 규칙, 결과 수를 분리하면 LineTrace, ShapeTrace, Single, Multi 선택이 흔들리지 않는다.
- 선 하나면 Line
폭 선 하나면 Line 두께가 필요하면 Sphere, Capsule, Box 같은 Shape를 쓴다.
- 응답 기준이면 Channel
규칙 응답 기준이면 Channel 찾을 대상 종류가 우선이면 Object Type 기준으로 좁힌다.
- 첫 대상이면 Single
개수 첫 대상이면 Single 관통 후보나 거리 정렬이 필요하면 Multi를 선택한다.
| 상황 | 함수 축 | 충돌 축 | 결과 축 |
|---|---|---|---|
| 상호작용 버튼 카메라 앞의 가장 가까운 대상만 확인 | Line Trace | Visibility 채널, 자기 자신 제외 | Single로 즉시 사용 |
| 근접 범위 판정 검이나 몸통처럼 폭이 있는 감지 | Sphere 또는 Capsule Trace | Pawn, WorldStatic 등 대상 타입 | Multi로 후보 정리 |
| 관통 스캔 여러 히트를 거리순으로 처리 | Line 또는 Shape | 프로젝트 규칙에 맞춰 선택 | Multi 뒤 필요한 액터만 유지 |
트레이스 코드는 선을 쏘는 부분보다 결과를 어떤 기준으로 읽고 버릴지 정하는 부분에서 안정성이 갈립니다.
트레이스가 빗나가면 충돌 채널보다 먼저 실제 검사선을 화면에 그려 본다.
| 구간 | 정할 것 | 확인할 값 |
|---|---|---|
| Start | 카메라, 총구, 캐릭터 위치 중 의도와 맞는 좌표 | 플레이어가 보는 기준과 발사 기준 |
| End | Forward Vector와 사거리로 방향과 길이를 계산한다 | 벡터 방향과 월드 좌표 |
| Query | Trace Channel, Ignore Actor, 복잡 충돌 여부를 정한다 | FCollisionQueryParams와 채널 응답 |
| Hit | FHitResult에서 ImpactPoint, Actor, Component를 읽는다 | 상호작용, 디버그 선, 실패 분기 |
라인 트레이스 시스템은 언리얼 엔진에서 정교하고 효율적인 월드 쿼리 기능을 제공합니다.
직접적인 물리적 접촉 없이도 원하는 정보를 얻을 수 있으므로, 총알 히트 감지, AI 시야, 상호작용 등 다양한 게임플레이 메커니즘을 구현하는 데 필수적인 도구입니다.
트레이스의 유형(Line, Sphere, Box, Capsule), 채널/오브젝트 타입별 감지, 그리고 결과(FHitResult) 처리 방법을 숙지하면 강력한 게임플레이 시스템을 구축할 수 있을 것입니다.