충돌 이벤트란?
액터나 컴포넌트가 다른 객체와 상호작용할 때 발생
- 오버랩 이벤트
- 객체가 겹칠 때 발생하는 이벤트
- 히트 이벤트
- 객체가 닿을 때 발생하는 이벤트
BaseItem에 Colision Component를 붙인다.


위와 같은 방법으로 4개의 아이템을 다 블루프린트로 만들어준다.

블루프린트에 스태틱 메시들을 할당해준다.




Collision Presets 살펴보기

콜리전 컴포넌트의 콜리전 카테코리를 보면 콜리전 프리셋이 있다.
콜리전 프리셋의 종류
이 오브젝트가 무엇과 어떻게 충돌할지”를 미리 정해둔 규칙 묶음.

🔵 NoCollision
👉 아무것과도 충돌하지 않음
- 트레이스도 무시
- 물리 충돌도 없음
- 완전 통과
✔ 용도
- 이펙트, UI, 장식 오브젝트
- 충돌 필요 없는 배경
🔵 BlockAll
👉 모든 것을 막음
- Pawn, WorldStatic, PhysicsBody 등 전부 “Block”
✔ 용도
- 벽, 바닥, 구조물
- “여기 못 지나감” 확실히 만들 때
🔵 BlockAllDynamic
👉 동적 오브젝트만 막는 범용 블록
- Static World는 상황에 따라 다르게 처리
- 기본적으로 “움직이는 것과 충돌”
✔ 용도
- 일반 오브젝트 대부분
- 애매하면 이거 쓰는 경우 많음
🔵 OverlapAll
👉 전부 통과하지만 “겹침 이벤트”는 발생
- 물리 충돌 없음
- 대신 BeginOverlap / EndOverlap 발생
✔ 용도
- 트리거, 감지 구역
- 아이템 습득 범위, 이벤트 존
🔵 OverlapAllDynamic
👉 동적 대상과만 Overlap
- Static World는 무시
- Pawn / PhysicsBody 등에만 반응
✔ 용도
- 플레이어 감지 영역
- AI 시야 범위
🔵 Pawn
👉 캐릭터용 프리셋
- Pawn끼리 충돌하거나 월드와 충돌
- 트레이스 반응 포함
✔ 용도
- 플레이어 캐릭터
- NPC
🔵 Spectator
👉 관전자용
- 대부분 충돌 없음
- 다른 Pawn과도 최소 반응
✔ 용도
- 디버그 카메라
- 관전 모드
🔵 CharacterMesh / Character
👉 캐릭터 전용 세팅
- Capsule 기반 충돌
- Mesh는 보통 NoCollision 또는 QueryOnly
✔ 용도
- 3인칭/1인칭 캐릭터
🔵 PhysicsActor
👉 물리 시뮬레이션용
- 모든 물리 충돌 활성
- 밀리고 튕기고 떨어짐
✔ 용도
- 박스, 통, 떨어지는 오브젝트
- RigidBody 기반 오브젝트
🔵 Vehicle
👉 차량용
- 바퀴/지형 충돌 최적화
- Pawn과 상호작용 고려됨
✔ 용도
- 차량 시스템
이번 실습에서는 모든 아이템에 OverlapAllDynamic을 사용한다.
Collision Enabled
이 오브젝트가 충돌 시스템에 얼마나 참여하는지를 결정

🔵 No Collision
👉 충돌 완전 OFF
- 물리 충돌 ❌
- 트레이스 감지 ❌
- 오버랩 이벤트 ❌
✔ 용도
- UI, 이펙트, 장식용 메쉬
- 성능 최적화용 (완전히 무시 가능)
🔵 Query Only (No Physics Collision)
👉 트레이스 / 오버랩만 가능
- 물리 충돌 ❌ (밀리거나 튕기지 않음)
- 라인트레이스 / 스윕 / 오버랩 ✔
✔ 용도
- 트리거 존
- 감지 영역
- 아이템, 인터랙션 오브젝트
- “닿았는지 체크만 필요할 때”
🔵 Physics Only
👉 물리 충돌만 가능
- 물리 시뮬레이션 ✔ (밀리고 떨어짐)
- 트레이스 / 오버랩 ❌ (감지 안 됨)
✔ 용도
- 순수 물리 오브젝트
- 떨어지는 박스, 쓰러지는 물체
- 충돌은 하지만 “감지 이벤트 필요 없을 때”
🔵 Collision Enabled (Query and Physics)
👉 전부 활성
- 물리 충돌 ✔
- 트레이스 ✔
- 오버랩 ✔
✔ 용도
- 플레이어 캐릭터
- 핵심 오브젝트
- 대부분의 “진짜 게임 오브젝트”
보통 콜리전 오버랩으로 해놓음.
기본 콜리전 설정을 코드로 적용하기
Collision = CreateDefaultSubobject<USphereComponent>(TEXT("Collision"));
Collision->SetCollisionProfileName(TEXT("OverlapAllDynamic")); // 콜리전 프리셋 설정
Collision->SetCollisionProfileName(TEXT("OverlapAllDynamic")); 로 디폴트 설정을 해줄 수 있다.
이벤트 바인딩
매개변수 목록을 함수 시그니처라 부르는데, 이를 맞춰줘야 Overlap 이벤트를 발생시킬 수 있다.
ItemInterface.h에서 Overlap 관련 선언을 이렇게 바꿔준다.
virtual void OnItemOverlap(
UPrimitiveComponent* OverlappedComp,
AActor* OtherActor,
UPrimitiveComponent* OtherComp,
int32 OtherBodyIndex,
bool bFromSweep,
const FHitResult& SweepResult
) = 0; // 캐릭터, 몬스터 등 여러 경우를 생각해서 캐스팅할 수 있도록 AActor 클래스를 받는다.
virtual void OnItemEndOverlap(
UPrimitiveComponent* OverlappedComp,
AActor* OtherActor,
UPrimitiveComponent* OtherComp,
int32 OtherBodyIndex
) = 0;
파라미터 하나씩 의미
1️⃣ UPrimitiveComponent* OverlappedComp
👉 “이쪽(내 오브젝트)의 충돌 컴포넌트”
- 예: 아이템의 Sphere Collision
2️⃣ AActor* OtherActor
👉 “부딪힌 대상 액터”
- 예: 플레이어 캐릭터
3️⃣ UPrimitiveComponent* OtherComp
👉 “상대방의 충돌 컴포넌트”
- 예: 플레이어 캡슐 콜라이더
4️⃣ int32 OtherBodyIndex
👉 “멀티 바디 충돌에서 어느 부분인지”
- 보통은 0이거나 잘 안 씀
- Skeletal Mesh처럼 바디 많을 때 사용
5️⃣ bool bFromSweep
👉 “이게 Sweep(이동 중 충돌)인지 여부”
- true → 이동하면서 감지됨 (예: 캐릭터 이동 중)
- false → 단순 겹침 발생
6️⃣ const FHitResult& SweepResult
👉 “충돌 상세 정보”
- 충돌 위치
- 충돌 법선
- 충돌 지점 등
인터페이스를 상속받은 BaseItem.h, cpp도 수정한다.
virtual void OnItemOverlap(
UPrimitiveComponent* OverlappedComp,
AActor* OtherActor,
UPrimitiveComponent* OtherComp,
int32 OtherBodyIndex,
bool bFromSweep,
const FHitResult& SweepResult
) override; // 캐릭터, 몬스터 등 여러 경우를 생각해서 캐스팅할 수 있도록 AActor 클래스를 받는다.
virtual void OnItemEndOverlap(
UPrimitiveComponent* OverlappedComp,
AActor* OtherActor,
UPrimitiveComponent* OtherComp,
int32 OtherBodyIndex
) override;
void ABaseItem::OnItemOverlap(
UPrimitiveComponent* OverlappedComp,
AActor* OtherActor,
UPrimitiveComponent* OtherComp,
int32 OtherBodyIndex,
bool bFromSweep,
const FHitResult& SweepResult
)
{
}
void ABaseItem::OnItemEndOverlap(
UPrimitiveComponent* OverlappedComp,
AActor* OtherActor,
UPrimitiveComponent* OtherComp,
int32 OtherBodyIndex
)
{
}
이제 BaseItem.cpp의 마지막줄에 콜리전 함수를 연결해준다.
Collision->OnComponentBeginOverlap.AddDynamic(this, &ABaseItem::OnItemOverlap);
Collision->OnComponentEndOverlap.AddDynamic(this, &ABaseItem::OnItemOverlap);
함수 시그니처가 다르기 때문에 반드시 이름을 맞춰서 연결해줘야한다.
이벤트 바인딩을 하는 이유
언리얼 엔진(Unreal Engine)은 기본적으로 이렇게 동작합니다:
- 충돌 발생
- 엔진 내부에서 이벤트 발생
- 하지만 “무슨 함수를 실행할지”는 모름
- 엔진은 “무슨 일이 일어났는지”만 알고
👉 “어떻게 처리할지”는 게임마다 다르기 때문- 아이템 → 줍기
- 문 → 열기
- 트랩 → 데미지
- UI → 표시
- 👉 전부 다 다름
- 그래서 고정 로직이 아니라
“연결 구조(바인딩)”로 설계된 것
그래서 개발자가 직접 알려줘야 합니다:
👉 “이 이벤트가 발생하면 이 함수 실행해!”
이걸 하는 게 **Event Binding (델리게이트 연결)**입니다.
- 엔진은 “신호만 보냄”
- 개발자는 “원하는 행동 연결”
- 재사용성 ↑
- 확장성 ↑
- 컴포넌트화 가능
이벤트 바인딩을 하려면 리플렉션 시스템에 등록해줘야한다.
ItemInterface에 한번만 UFUNCTION을 붙여주면 자식 클래스들도 자동으로 등록된다.
UFUNCTION() // 바인딩하는 함수에는 리플렉션 시스템에 등록하기 위해 UFUNCTION을 붙여준다.
virtual void OnItemOverlap(
UPrimitiveComponent* OverlappedComp,
AActor* OtherActor,
UPrimitiveComponent* OtherComp,
int32 OtherBodyIndex,
bool bFromSweep,
const FHitResult& SweepResult
) = 0; // 캐릭터, 몬스터 등 여러 경우를 생각해서 캐스팅할 수 있도록 AActor 클래스를 받는다.
UFUNCTION()
virtual void OnItemEndOverlap(
UPrimitiveComponent* OverlappedComp,
AActor* OtherActor,
UPrimitiveComponent* OtherComp,
int32 OtherBodyIndex
) = 0;


이후 캐릭터 클래스를 가져와서 Tags에 Player를 추가해준다.
로그로 확인하기
GEngine->AddOnScreenDebugMessage로 로그를 확인해준다.
void ABaseItem::OnItemOverlap(
UPrimitiveComponent* OverlappedComp,
AActor* OtherActor,
UPrimitiveComponent* OtherComp,
int32 OtherBodyIndex,
bool bFromSweep,
const FHitResult& SweepResult
)
{
if (OtherActor && OtherActor->ActorHasTag("Player")) // OtherActor가 null이 아니고 플레이어 태그가 붙어있을 때 실행
{
GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Green, FString::Printf(TEXT("Overlap")));
ActivateItem(OtherActor);
}
}
결과


콜리전 오버랩 이벤트가 일어난다.
아이템에 개별 로직 구현
CoinItem을 초기화하고, ActivateItem 함수를 수정해준다.
// Fill out your copyright notice in the Description page of Project Settings.
#include "CoinItem.h"
ACoinItem::ACoinItem()
{
// 변수 초기화
PointValue = 0;
ItemType = "DefaultCoin";
}
void ACoinItem::ActivateItem(AActor* Activator)
{
// 액터가 존재하며 플레이어인지 확인
if (Activator && Activator->ActorHasTag("Player"))
{
GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Green, FString::Printf(TEXT("Player Gained %d points!"), PointValue));
DestroyItem();
}
}
이후 BigCoin과 SmallCoin의 구현부도 수정해준다.
// Fill out your copyright notice in the Description page of Project Settings.
#include "BigCoinItem.h"
ABigCoinItem::ABigCoinItem()
{
PointValue = 50;
ItemType = "BigCoin";
}
void ABigCoinItem::ActivateItem(AActor* Activator)
{
Super::ActivateItem(Activator);
// CoinItem에서 구현하였으므로 지워준다.
// DestroyItem();
}
HealingItem의 ActivateItem은 CoinItem과 로그만 다르게 만들어준다.
// Fill out your copyright notice in the Description page of Project Settings.
#include "HealingItem.h"
AHealingItem::AHealingItem()
{
HealAmount = 20.0f;
ItemType = "Healing";
}
void AHealingItem::ActivateItem(AActor* Activator)
{
if (Activator && Activator->ActorHasTag("Player"))
{
GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Green, FString::Printf(TEXT("Player Gained %d HP!"), HealAmount));
DestroyItem();
}
}
MineItem은 충돌 후 5초 뒤에 폭발하고, 폭발 반경에 있는 객체들이 데미지를 입는다.
타이머 핸들러
특정 시간 뒤에 함수를 실행하거나, 일정 간격으로 반복 호출할 때 사용하는 기능이다.
구조
GetWorld()->GetTimerManager().SetTimer(
FTimerHandle& InOutHandle, // 1. 관리할 핸들 변수
UserObject* InObj, // 2. 함수를 가진 객체 (보통 this)
FunctionName InTimerMethod, // 3. 실행할 함수 주소 (&클래스::함수)
float InRate, // 4. 주기/지연 시간 (초 단위)
bool InbLoop, // 5. 반복 여부 (true면 반복, false면 1회)
float InFirstDelay // 6. (선택) 첫 실행까지의 추가 지연 시간
);
주의점
1. 타이머를 걸어만 두고 제어하지 못하면 메모리나 로직에 문제가 생길 수 있다. 이때 아까 만든 FTimerHandle 변수를 사용한다.
- 타이머 취소 (중지): GetWorld()->GetTimerManager().ClearTimer(MyTimerHandle);
- 타이머 일시정지 / 재개: GetWorld()->GetTimerManager().PauseTimer(MyTimerHandle); GetWorld()->GetTimerManager().UnPauseTimer(MyTimerHandle);
- 남은 시간 확인: float Remaining = GetWorld()->GetTimerManager().GetTimerRemaining(MyTimerHandle);
2. 인자가 없는 함수만 받을 수 있다. 인자를 전달하고 싶다면 FTimerDeligate를 사용해야한다.
FTimerDelegate TimerDel;
// 인자가 있는 함수 바인딩 (예: void MyFunction(int32 Value);)
TimerDel.BindUObject(this, &APawnDrone::MyFunction, 10);
GetWorld()->GetTimerManager().SetTimer(MyTimerHandle, TimerDel, 1.0f, false);
또는 람다 함수 내부에서 함수에 인자를 넣어 호출해줄 수도 있다.
int32 ScoreAmount = 100;
// 델리게이트 설정 없이 람다의 '캡처' 기능을 통해 인자처럼 전달 가능
GetWorld()->GetTimerManager().SetTimer(TimerHandle, [this, ScoreAmount]()
{
AddScore(ScoreAmount); // 람다 외부의 변수를 그대로 가져다 씀
}, 2.0f, false);
예시
MineItem.h에 변수로 FTimerHandle 추가
FTimerHandle ExplosionTimeHandle;
MineItem.cpp
GetWorld()->GetTimerManager().SetTimer(ExplosionTimeHandle, this, &AMineItem::Explode, 5.0f, false);
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "BaseItem.h"
#include "MineItem.generated.h"
/**
*
*/
UCLASS()
class SPARTAPROJECT_API AMineItem : public ABaseItem
{
GENERATED_BODY()
public:
AMineItem();
USphereComponent* ExplosionCollision;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item")
float ExplosionDelay; // 폭발 지연시간
// 콜리전 컴포넌트를 이용할 것이므로 삭제
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item")
float ExplosionRadius; // 폭발 반경
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item")
int32 ExplosionDamage; // 폭발 대미지
// 폭발 시간을 조정하는 타이머 핸들
FTimerHandle ExplosionTimeHandle;
virtual void ActivateItem(AActor* Activator) override;
void Explode(); // 폭발하는 함수
};
폭발 로직을 함수로 빼고, 타이머 핸들러를 사용하여, 5초 뒤에 폭발을 실행하도록 변경한다.
void AMineItem::ActivateItem(AActor* Activator)
{
// 게임 월드 -> 타이머 매니저
// 5초 뒤를 계산하는 타이머 핸들러
GetWorld()->GetTimerManager().SetTimer(ExplosionTimeHandle, this, &AMineItem::Explode, 5.0f, false); // 타이머
// Explode로 이동
//TArray<AActor*> OverlappingActors;
//ExplosionCollision->GetOverlappingActors(OverlappingActors);
//for (AActor* Actor : OverlappingActors)
//{
// if (Actor && Actor->ActorHasTag("Player")) // 액터인지와 플레이어 태그 확인
// {
// // 데미지 구현
// GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Green, FString::Printf(TEXT("Player damaged %f by MineItem"), ExplosionDamage));
// }
//}
//DestroyItem();
}