icon
5장 : 충돌, 물리, 인터랙션

트리거 박스와 오버랩 이벤트


이전 절에서는 언리얼 엔진의 충돌 시스템 기초인 충돌 채널과 프로파일 설정에 대해 알아보았습니다. 이제 이러한 충돌 시스템을 활용하여 게임플레이에서 빈번하게 사용되는 상호작용 방식인 트리거(Trigger)오버랩 이벤트(Overlap Event) 에 대해 자세히 살펴보겠습니다. 특히, 특정 영역에 진입했을 때 이벤트를 발생시키는 데 유용한 트리거 박스(Trigger Box) 의 활용법에 초점을 맞출 것입니다.


트리거(Trigger)란?

트리거는 게임 월드 내의 특정 영역(Volume) 으로, 플레이어나 다른 액터가 이 영역에 진입하거나 이탈했을 때 미리 정의된 게임플레이 이벤트를 발생시키는 역할을 합니다. 트리거는 물리적인 블록킹(Blocking)을 일으키지 않고, 단지 감지(Query)만을 수행한다는 점에서 일반적인 충돌 오브젝트와 차이가 있습니다.

트리거는 다음과 같은 다양한 상황에서 활용됩니다.

  • 아이템 획득: 플레이어가 아이템 위에 서면 아이템을 획득하고 사라지게 합니다.
  • 문 열림: 플레이어가 문 앞의 특정 영역에 진입하면 문이 자동으로 열리게 합니다.
  • 지역 감지: 플레이어가 새로운 지역에 진입하면 해당 지역의 이름이 화면에 표시되거나, 배경 음악이 바뀌게 합니다.
  • 컷씬 발동: 특정 지점에 도달하면 시네마틱 컷씬이 시작되게 합니다.
  • 체크포인트: 플레이어가 지나가면 체크포인트가 활성화되게 합니다.

트리거 박스 (Trigger Box) 컴포넌트

언리얼 엔진에서 가장 흔하게 사용되는 트리거 형태는 UBoxComponent 를 기반으로 한 트리거 볼륨(Trigger Volume) 입니다. UBoxComponent는 직육면체 형태의 충돌 감지 영역을 제공하며, 이를 트리거 용도로 설정할 수 있습니다.

트리거 박스의 특징

  • 시각적 표현 없음: 기본적으로 렌더링되지 않으므로, 게임 내에서는 보이지 않습니다. 에디터에서만 와이어프레임 형태로 표시되어 배치와 크기 조절을 돕습니다.
  • 물리적 상호작용 없음: QueryOnly 또는 OverlapAll과 같은 충돌 프리셋을 사용하여 물리적인 Block 반응을 일으키지 않습니다.
  • 오버랩 이벤트 발생: 다른 충돌 컴포넌트와 겹쳤을 때 OnComponentBeginOverlapOnComponentEndOverlap 이벤트를 발생시킵니다.

C++에서 트리거 박스 설정하기

이전 절에서 다룬 AMyTriggerVolume 액터 클래스를 다시 살펴보겠습니다. 이 액터는 UBoxComponent를 사용하여 트리거 박스를 구현하는 전형적인 예시입니다.

AMyTriggerVolume.h
#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "MyTriggerVolume.generated.h"

class UBoxComponent; // UBoxComponent를 미리 선언

UCLASS()
class MYPROJECT_API AMyTriggerVolume : public AActor
{
    GENERATED_BODY()
    
public:    
    AMyTriggerVolume(); // 생성자

protected:
    virtual void BeginPlay() override; // 액터 초기화 시 호출

    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Collision")
    UBoxComponent* TriggerBox; // 트리거 박스 컴포넌트 포인터

    // 오버랩 시작 이벤트에 바인딩될 함수
    // UFUNCTION 매크로 필수: 델리게이트에 바인딩되기 위해 리플렉션 시스템에 노출되어야 함
    UFUNCTION()
    void OnOverlapBegin(UPrimitiveComponent* OverlappedComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);

    // 오버랩 종료 이벤트에 바인딩될 함수
    UFUNCTION()
    void OnOverlapEnd(UPrimitiveComponent* OverlappedComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex);
};
AMyTriggerVolume.cpp
#include "MyTriggerVolume.h"
#include "Components/BoxComponent.h" // UBoxComponent 헤더 포함
#include "Kismet/GameplayStatics.h"   // Debug 메시지 등을 위해 (선택 사항)

AMyTriggerVolume::AMyTriggerVolume()
{
    // 액터가 매 프레임 업데이트될 필요가 없다면 틱 비활성화
    PrimaryActorTick.bCanEverTick = false; 

    // UBoxComponent 생성
    TriggerBox = CreateDefaultSubobject<UBoxComponent>(TEXT("TriggerBox"));
    RootComponent = TriggerBox; // 트리거 박스를 액터의 루트 컴포넌트로 설정

    // 충돌 설정: 오버랩 전용으로 설정
    // Project Settings의 Collision Preset에 "TriggerOnly"와 같은 프로파일이 없다면
    // 기본 OverlapAllDynamic을 사용하거나 직접 커스텀 설정을 할 수 있습니다.
    TriggerBox->SetCollisionProfileName(TEXT("OverlapAllDynamic")); 
    // TriggerBox->SetCollisionEnabled(ECollisionEnabled::QueryOnly); // 쿼리만 가능, 물리 없음
    // TriggerBox->SetCollisionObjectType(ECollisionChannel::ECC_WorldDynamic); // 이 트리거의 오브젝트 타입

    // 중요: 오버랩 이벤트를 발생시키도록 설정
    TriggerBox->SetGenerateOverlapEvents(true);

    // (선택 사항) 트리거 박스의 크기 설정
    TriggerBox->SetBoxExtent(FVector(100.0f, 100.0f, 100.0f)); // 100x100x100 cm 크기의 박스
}

void AMyTriggerVolume::BeginPlay()
{
    Super::BeginPlay();
    
    // 오버랩 이벤트를 C++ 함수에 바인딩
    // OnComponentBeginOverlap 델리게이트에 OnOverlapBegin 함수를 연결합니다.
    TriggerBox->OnComponentBeginOverlap.AddDynamic(this, &AMyTriggerVolume::OnOverlapBegin);
    TriggerBox->OnComponentEndOverlap.AddDynamic(this, &AMyTriggerVolume::OnOverlapEnd);
}

void AMyTriggerVolume::OnOverlapBegin(UPrimitiveComponent* OverlappedComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
    // 오버랩된 액터가 유효하고, 자기 자신이 아니며, 플레이어 폰인지 확인하는 것이 일반적입니다.
    // 플레이어 폰인지 확인하는 예시:
    if (OtherActor && OtherActor != this && OtherActor->IsA(APawn::StaticClass()))
    {
        APawn* PlayerPawn = Cast<APawn>(OtherActor); // 플레이어 폰으로 캐스팅
        if (PlayerPawn && PlayerPawn->IsLocallyControlled()) // 로컬 플레이어인지 확인
        {
            UE_LOG(LogTemp, Warning, TEXT("Local Player entered: %s"), *PlayerPawn->GetName());
            // 여기에 플레이어가 트리거에 진입했을 때 실행할 게임플레이 로직을 구현합니다.
            // 예: "아이템 획득!" 메시지 표시, 문 열기 함수 호출 등.
        }
    }
}

void AMyTriggerVolume::OnOverlapEnd(UPrimitiveComponent* OverlappedComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
    if (OtherActor && OtherActor != this && OtherActor->IsA(APawn::StaticClass()))
    {
        APawn* PlayerPawn = Cast<APawn>(OtherActor);
        if (PlayerPawn && PlayerPawn->IsLocallyControlled())
        {
            UE_LOG(LogTemp, Warning, TEXT("Local Player exited: %s"), *PlayerPawn->GetName());
            // 여기에 플레이어가 트리거에서 이탈했을 때 실행할 로직을 구현합니다.
            // 예: "아이템 획득 가능" 메시지 숨기기 등.
        }
    }
}

핵심 설정

  • SetCollisionProfileName(TEXT("OverlapAllDynamic")): 이 컴포넌트가 모든 동적 오브젝트와 오버랩을 감지하도록 설정합니다. 프로젝트 설정에서 더 세분화된 커스텀 프로파일을 만들었다면 해당 이름을 사용하세요.
  • SetGenerateOverlapEvents(true): 이 함수를 true로 설정해야 오버랩 이벤트가 발생합니다.
  • OnComponentBeginOverlap.AddDynamic(this, &AMyTriggerVolume::OnOverlapBegin): AddDynamic 함수는 언리얼 엔진의 델리게이트(Delegate) 시스템에서 이벤트에 함수를 바인딩하는 데 사용됩니다. OnComponentBeginOverlapUPrimitiveComponent가 제공하는 멀티캐스트 델리게이트로, 오버랩이 시작될 때 호출됩니다.
  • 첫 번째 인자 this는 이벤트를 받을 오브젝트(액터)의 포인터입니다.
  • 두 번째 인자 &AMyTriggerVolume::OnOverlapBegin는 바인딩할 멤버 함수의 주소입니다. 이 함수는 반드시 UFUNCTION()으로 선언되어야 하며, 델리게이트의 시그니처와 일치하는 파라미터를 가져야 합니다.

오버랩 이벤트의 파라미터 이해

OnOverlapBegin (그리고 OnOverlapEnd) 함수가 받는 파라미터들은 오버랩에 대한 중요한 정보를 제공합니다.

  • UPrimitiveComponent* OverlappedComp: 오버랩을 발생시킨 이 컴포넌트(즉, TriggerBox 자체).
  • AActor* OtherActor: 이 트리거 박스와 겹친 다른 액터의 포인터.
  • UPrimitiveComponent* OtherComp: OtherActor 내에서 OverlappedComp와 실제로 겹친 다른 컴포넌트의 포인터. (예: 플레이어 캐릭터의 CapsuleComponent)
  • int32 OtherBodyIndex: 스켈레탈 메시의 경우, 겹친 본(Bone)의 인덱스.
  • bool bFromSweep: 스윕(Sweep) 검사로 오버랩이 발생했는지 여부. (예: 빠르게 이동하는 오브젝트의 충돌 감지)
  • const FHitResult& SweepResult: bFromSweeptrue일 경우, 스윕 검사의 자세한 결과.

이 파라미터들을 통해 우리는 어떤 액터의 어떤 컴포넌트가 우리 트리거 박스와 상호작용했는지 정확히 파악하고, 그에 따른 맞춤형 로직을 구현할 수 있습니다.


블루프린트에서 트리거 박스 및 이벤트 활용

C++로 AMyTriggerVolume을 만들었다면, 언리얼 에디터에서 이를 기반으로 한 블루프린트 클래스(BP_MyTriggerVolume)를 생성하여 레벨에 배치할 수 있습니다.

배치 및 크기 조절: BP_MyTriggerVolume 인스턴스를 레벨에 드래그하여 배치하고, 디테일 패널에서 TriggerBox 컴포넌트의 Box Extent를 조절하여 원하는 트리거 영역 크기로 만듭니다.

이벤트 그래프에서 활용: BP_MyTriggerVolume 블루프린트 에디터를 열고, Event Graph에서 TriggerBox 컴포넌트를 선택한 후, 디테일 패널 하단에 있는 이벤트 목록에서 OnComponentBeginOverlapOnComponentEndOverlap 이벤트를 추가할 수 있습니다.

이 이벤트 노드들에서 Other Actor 핀을 뽑아 Cast To ThirdPersonCharacter (혹은 여러분의 캐릭터 클래스)와 같은 캐스팅 노드를 사용하여 플레이어가 겹쳤는지 확인하고, 원하는 블루프린트 로직을 연결할 수 있습니다.


마치며

트리거 박스와 오버랩 이벤트는 언리얼 엔진에서 비물리적인 게임플레이 상호작용을 구현하는 데 있어 가장 기본적인 도구입니다. 이들을 통해 아이템 획득, 구역 진입/이탈 감지, 이벤트 발동 등 다양한 기능을 효율적으로 구현할 수 있습니다. 충돌 채널과 프로파일 설정을 통해 원하는 액터들 사이에서만 오버랩이 발생하도록 정교하게 제어할 수 있다는 점을 기억하는 것이 중요합니다.