Skip to content

UE5 C++ 핵심 개념 입문 가이드

개요 — 언리얼 C++이 일반 C++과 다른 이유

Section titled “개요 — 언리얼 C++이 일반 C++과 다른 이유”

언리얼 엔진 C++(이하 UE C++)은 표준 C++을 기반으로 하지만, 엔진 고유의 리플렉션 시스템, 가비지 컬렉션, 직렬화, 에디터 통합 레이어가 얹혀 있습니다. 이 때문에 일반 C++ 경험만으로 처음 UE C++ 코드를 보면 낯선 매크로와 접두사들로 인해 당황하기 쉽습니다.

핵심 차이점을 한 줄로 요약하면 이렇습니다.

일반 C++에서는 객체의 생성과 소멸을 개발자가 직접 관리하지만, UE C++에서는 UObject를 상속한 객체는 엔진의 가비지 컬렉터가 관리합니다. 매크로는 엔진에게 객체 정보를 알려주는 “신고서” 역할을 합니다.

이 가이드에서는 UE C++ 입문자가 가장 먼저 이해해야 할 네 가지 축을 다룹니다.

핵심 내용
UObject 시스템모든 UE 객체의 뿌리, 계층 구조
매크로 시스템UCLASS, UPROPERTY, UFUNCTION의 역할
가비지 컬렉션메모리 안전을 위한 포인터 규칙
AActor 생명주기스폰부터 소멸까지 이벤트 흐름

UObject는 언리얼 엔진의 모든 엔진 관리 객체의 기반 클래스입니다. UObject를 상속하면 다음 기능을 자동으로 얻습니다.

  • 리플렉션: 런타임에 클래스·프로퍼티·함수 정보 조회 가능
  • 직렬화: 에디터 저장/로드, 네트워크 복제에 활용
  • 가비지 컬렉션: 참조가 없어지면 엔진이 자동으로 메모리 해제
  • 에디터 통합: Blueprint에서 접근, 디테일 패널 노출

UObject를 상속하지 않는 일반 C++ 구조체(예: FVector, FTransform)는 이 기능을 갖지 않으며, GC의 관리 대상도 아닙니다.

언리얼의 주요 클래스들은 모두 UObject에서 파생됩니다. 게임플레이 프로그래밍에서 자주 만나는 계층 구조는 다음과 같습니다.

UObject
└─ UActorComponent (컴포넌트 기반 클래스)
└─ USceneComponent (트랜스폼을 가진 컴포넌트)
└─ AActor (월드에 배치 가능한 객체)
└─ APawn (빙의 가능한 Actor)
└─ ACharacter (이동 컴포넌트 포함 캐릭터)
└─ UGameInstance (세션 전체 지속)
└─ USubsystem (엔진 서브시스템)

접두사 규칙이 계층을 바로 알려줍니다.

접두사의미예시
UUObject 파생 클래스UActorComponent, UTexture
AAActor 파생 클래스ACharacter, APlayerController
F일반 C++ 구조체 (UObject 아님)FVector, FHitResult
T템플릿 클래스TArray<T>, TObjectPtr<T>
I인터페이스 클래스IAbilitySystemInterface
E열거형ECollisionChannel

UObject 파생 클래스는 new 연산자 대신 엔진 전용 생성 함수를 사용해야 합니다. new로 생성하면 GC가 해당 객체를 추적하지 않아 메모리 문제가 발생합니다.

// ---- 생성 방법 세 가지 ----
// 1. NewObject<T>() — 런타임에 UObject 생성 (Actor가 아닌 컴포넌트, 에셋 등)
UMyComponent* Comp = NewObject<UMyComponent>(this, UMyComponent::StaticClass());
// 2. CreateDefaultSubobject<T>() — 생성자(Constructor) 안에서만 사용
// 컴포넌트를 Actor의 기본 서브오브젝트로 등록할 때 사용
USkeletalMeshComponent* MeshComp = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("MeshComp"));
// 3. SpawnActor<T>() — 월드에 AActor를 스폰
FActorSpawnParameters SpawnParams;
AMyActor* NewActor = GetWorld()->SpawnActor<AMyActor>(AMyActor::StaticClass(), Location, Rotation, SpawnParams);

파괴는 직접 호출하지 않습니다. Actor의 경우 Destroy()를 호출하면 다음 GC 사이클에서 메모리가 해제됩니다. 일반 UObject는 참조가 없어지면 GC가 자동 처리합니다.

// Actor 제거 요청 (실제 메모리 해제는 GC가 수행)
MyActor->Destroy();
// 절대 하지 말아야 할 것
// delete MyActor; // UObject에 delete는 크래시 또는 undefined behavior

언리얼 헤더 툴(UHT, Unreal Header Tool)은 빌드 시 .h 파일의 매크로를 분석해 리플렉션 코드를 자동 생성합니다. 매크로는 “엔진에게 이 클래스/프로퍼티/함수를 등록해달라”는 선언입니다.

UCLASS 매크로는 해당 C++ 클래스를 언리얼 리플렉션 시스템에 등록합니다. 매크로 없이는 엔진이 이 클래스의 존재를 알 수 없습니다.

MyActor.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "MyActor.generated.h" // 반드시 마지막 include — UHT 생성 코드 포함
UCLASS(BlueprintType, Blueprintable) // 지정자(Specifier)로 동작 제어
class MYGAME_API AMyActor : public AActor
{
GENERATED_BODY() // UHT가 채워넣을 자동 생성 코드 위치
public:
AMyActor();
};

주요 UCLASS 지정자:

지정자의미
BlueprintType이 클래스를 Blueprint 변수로 사용 가능
Blueprintable이 클래스를 Blueprint에서 상속 가능
Abstract인스턴스 직접 생성 불가 (순수 기반 클래스)
NotBlueprintableBlueprint 상속 불가
MinimalAPI다른 모듈에 최소한의 API만 노출

2.2 UPROPERTY — 프로퍼티 리플렉션 및 GC 연동

Section titled “2.2 UPROPERTY — 프로퍼티 리플렉션 및 GC 연동”

UPROPERTY는 멤버 변수를 엔진에 등록합니다. 이를 통해 에디터 노출, 직렬화, 네트워크 복제, 그리고 GC 추적이 가능해집니다.

UCLASS()
class MYGAME_API AMyActor : public AActor
{
GENERATED_BODY()
public:
// EditAnywhere: 에디터 모든 위치에서 편집 가능
// BlueprintReadWrite: Blueprint에서 읽기/쓰기 가능
// Category: 디테일 패널 카테고리
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Stats")
float Health = 100.f;
// VisibleAnywhere: 에디터에서 보기만 가능 (수정 불가)
// BlueprintReadOnly: Blueprint에서 읽기만 가능
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
TObjectPtr<UStaticMeshComponent> MeshComponent;
// Replicated: 네트워크 복제 대상
UPROPERTY(Replicated, BlueprintReadOnly, Category = "Stats")
int32 Score = 0;
// EditDefaultsOnly: Blueprint 클래스 기본값에서만 편집 (인스턴스 편집 불가)
// TSubclassOf<T>: 특정 클래스 또는 그 서브클래스만 지정 가능
UPROPERTY(EditDefaultsOnly, Category = "Config")
TSubclassOf<AActor> ProjectileClass;
};

주요 UPROPERTY 접근 지정자:

지정자에디터 접근Blueprint 접근
EditAnywhere기본값 + 인스턴스 모두 편집
EditDefaultsOnly기본값만 편집
EditInstanceOnly인스턴스만 편집
VisibleAnywhere보기만
BlueprintReadWrite읽기 + 쓰기
BlueprintReadOnly읽기만

중요: UPROPERTY가 없는 UObject* 원시 포인터는 GC가 추적하지 않습니다. 해당 포인터가 참조하는 객체가 GC에 의해 수집되면 포인터는 **댕글링 포인터(dangling pointer)**가 됩니다.

2.3 UFUNCTION — 함수 리플렉션 및 Blueprint 노출

Section titled “2.3 UFUNCTION — 함수 리플렉션 및 Blueprint 노출”

UFUNCTION은 멤버 함수를 엔진에 등록합니다. Blueprint 호출, 네트워크 RPC, 이벤트 바인딩 등에 필요합니다.

UCLASS()
class MYGAME_API AMyActor : public AActor
{
GENERATED_BODY()
public:
// BlueprintCallable: Blueprint에서 호출 가능한 노드로 노출
UFUNCTION(BlueprintCallable, Category = "Combat")
void TakeDamage_Custom(float DamageAmount);
// BlueprintPure: 실행 핀 없는 순수 함수 (값 반환만)
UFUNCTION(BlueprintPure, Category = "Stats")
float GetHealthPercent() const;
// BlueprintImplementableEvent: C++에서 선언, Blueprint에서 구현
UFUNCTION(BlueprintImplementableEvent, Category = "Events")
void OnHealthChanged(float NewHealth);
// BlueprintNativeEvent: C++에 기본 구현 있음, Blueprint에서 오버라이드 가능
// 구현부는 함수명_Implementation으로 정의
UFUNCTION(BlueprintNativeEvent, Category = "Events")
void OnDeath();
virtual void OnDeath_Implementation();
// Server: 클라이언트에서 호출 → 서버에서 실행되는 RPC
// Reliable: 패킷 유실 없이 보장
UFUNCTION(Server, Reliable, WithValidation, Category = "Network")
void ServerRequestJump();
// NetMulticast: 서버에서 호출 → 모든 클라이언트에서 실행
UFUNCTION(NetMulticast, Unreliable, Category = "Network")
void MulticastPlayEffect();
};

2.4 GENERATED_BODY — 자동 생성 코드 자리표시자

Section titled “2.4 GENERATED_BODY — 자동 생성 코드 자리표시자”

GENERATED_BODY()는 UHT가 리플렉션 코드를 삽입할 위치를 지정합니다. UCLASS, USTRUCT, UENUM 매크로를 사용하는 모든 타입의 선언부에 반드시 포함해야 합니다.

UCLASS()
class AMyActor : public AActor
{
GENERATED_BODY() // 이 자리에 UHT가 생성자, 리플렉션 테이블 등을 자동 삽입
// ...
};
USTRUCT(BlueprintType)
struct FMyData
{
GENERATED_BODY() // 구조체도 동일하게 필요
UPROPERTY(EditAnywhere)
FString Name;
UPROPERTY(EditAnywhere)
int32 Value = 0;
};

3. 가비지 컬렉션 (Garbage Collection)

Section titled “3. 가비지 컬렉션 (Garbage Collection)”

언리얼의 GC는 표시-정리(Mark-and-Sweep) 방식으로 동작합니다.

  1. 루트셋(Root Set) 탐색: GUObjectArray에 등록된 모든 UObject를 순회합니다.
  2. 도달성 분석: 루트셋에서 참조를 따라가며 도달 가능한 객체에 마킹합니다.
  3. 정리: 마킹되지 않은(참조 없는) 객체의 메모리를 해제합니다.

GC는 기본적으로 60초마다 실행되며, 프레임 사이에 분산 처리(Incremental GC)됩니다. 개발자는 ForceGarbageCollection(true)로 즉시 실행을 요청할 수 있지만 일반 게임플레이 코드에서는 불필요합니다.

3.2 UPROPERTY가 GC에서 중요한 이유

Section titled “3.2 UPROPERTY가 GC에서 중요한 이유”

GC가 참조를 추적하는 유일한 방법은 UPROPERTY로 등록된 포인터를 통해서입니다.

UCLASS()
class AMyActor : public AActor
{
GENERATED_BODY()
// 올바른 방식: UPROPERTY로 등록 → GC가 추적 → 안전
UPROPERTY()
TObjectPtr<UStaticMeshComponent> SafeComponent;
// 위험한 방식: UPROPERTY 없음 → GC가 추적 불가 → 댕글링 포인터 위험
UStaticMeshComponent* DangerousComponent; // 하지 마세요
};

UPROPERTY가 없는 원시 포인터에 할당된 UObject가 다른 곳에서 참조가 끊기면, GC가 해당 객체를 수집할 수 있습니다. 이후 해당 포인터를 역참조하면 크래시가 발생합니다.

UE5는 UObject 참조를 위한 전용 포인터 타입을 제공합니다.

// TObjectPtr<T> — UE5에서 TObjectPtr<T>이 UObject 멤버 포인터의 표준
// UPROPERTY와 함께 사용하면 GC 추적 + 에디터 직렬화 모두 지원
UPROPERTY(VisibleAnywhere)
TObjectPtr<UStaticMeshComponent> MeshComp;
// TWeakObjectPtr<T> — 약한 참조 (GC 수집을 막지 않음)
// 참조 대상이 삭제될 수 있는 경우 사용 (캐시, 관찰자 패턴)
TWeakObjectPtr<AActor> TargetActor;
void CheckTarget()
{
// IsValid() / Get()으로 안전하게 접근
if (TargetActor.IsValid())
{
TargetActor->DoSomething();
}
}
// TStrongObjectPtr<T> — 명시적으로 GC 수집 방지
// 테스트 코드나 특수 케이스에서만 사용
TStrongObjectPtr<UMyObject> PinnedObject;

비 UObject 타입(일반 C++ 클래스)을 힙에 할당해야 한다면 표준 스마트 포인터를 사용합니다.

// TSharedPtr<T> — 공유 소유권 (레퍼런스 카운팅)
TSharedPtr<FMyStruct> SharedData = MakeShared<FMyStruct>();
// TUniquePtr<T> — 단독 소유권
TUniquePtr<FMyStruct> UniqueData = MakeUnique<FMyStruct>();
// TSharedRef<T> — null이 될 수 없는 TSharedPtr
TSharedRef<FMyStruct> RefData = MakeShared<FMyStruct>();

3.4 흔한 실수와 메모리 안전 패턴

Section titled “3.4 흔한 실수와 메모리 안전 패턴”
// 실수 1: UPROPERTY 없는 원시 포인터 멤버
// 해결: 항상 UPROPERTY + TObjectPtr 사용
UPROPERTY()
TObjectPtr<UMyComponent> MyComp; // 올바름
// 실수 2: 지역 변수에 NewObject로 생성 후 저장하지 않음
void BadFunction()
{
UMyObject* TempObj = NewObject<UMyObject>(this);
// TempObj를 UPROPERTY 멤버에 저장하지 않으면 다음 GC에서 수집됨
}
// 해결: UPROPERTY 멤버에 저장
void GoodFunction()
{
MyStoredObject = NewObject<UMyObject>(this); // UPROPERTY 멤버에 저장
}
// 실수 3: IsValid() 없이 포인터 역참조
void BadDereference()
{
TargetActor->DoSomething(); // TargetActor가 Destroy() 호출 후면 크래시
}
// 해결: IsValid() 또는 IsValidLowLevel() 체크
void SafeDereference()
{
if (IsValid(TargetActor))
{
TargetActor->DoSomething();
}
}

: IsValid(Obj)는 포인터가 null인지, Pending Kill(소멸 대기)인지 모두 체크합니다. 단순 Obj != nullptr는 Pending Kill 상태를 잡지 못합니다.


AActor는 월드에 스폰되는 순간부터 소멸될 때까지 정해진 순서로 이벤트 함수가 호출됩니다. 이 순서를 모르면 초기화 코드를 잘못된 타이밍에 넣어 버그를 만들기 쉽습니다.

[스폰/로드]
1. 생성자(Constructor) — 기본값 설정, 서브오브젝트 생성
2. PostInitProperties() — 생성자 이후 프로퍼티 초기화 완료
3. PostLoad() (로드된 경우) — 에셋/레벨에서 로드 시 호출
[초기화]
4. PostActorCreated() — 에디터/게임에서 새로 생성 시
5. PostInitializeComponents() — 모든 컴포넌트 InitializeComponent() 완료 후
[플레이 시작]
6. BeginPlay() — 게임플레이 로직 시작 (가장 많이 사용)
7. Tick(float DeltaSeconds) — 매 프레임 호출 (활성화된 경우)
[소멸]
8. EndPlay(EEndPlayReason) — BeginPlay의 쌍대, 정리 코드 작성
9. BeginDestroy() — GC 수집 직전 (직접 오버라이드는 드묾)
10. IsReadyForFinishDestroy() — 비동기 소멸 대기 여부
11. FinishDestroy() — 메모리 해제 직전 마지막 콜백
MyActor.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "MyActor.generated.h"
UCLASS()
class MYGAME_API AMyActor : public AActor
{
GENERATED_BODY()
public:
AMyActor();
protected:
// 컴포넌트 초기화 완료 후 — 컴포넌트 간 의존성 설정에 적합
virtual void PostInitializeComponents() override;
// 게임플레이 로직 시작 지점 — 대부분의 초기화 코드는 여기에
virtual void BeginPlay() override;
// 매 프레임 호출
virtual void Tick(float DeltaTime) override;
// 소멸 또는 레벨 언로드 시 — 리소스 정리, 이벤트 언바인드
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
private:
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components",
meta = (AllowPrivateAccess = "true"))
TObjectPtr<UStaticMeshComponent> MeshComponent;
UPROPERTY(EditDefaultsOnly, Category = "Config")
float RotationSpeed = 45.f;
// 타이머 핸들 — 타이머 취소에 필요
FTimerHandle EffectTimerHandle;
};
MyActor.cpp
#include "MyActor.h"
#include "Components/StaticMeshComponent.h"
AMyActor::AMyActor()
{
// Tick 활성화 여부 — 불필요하면 false로 성능 절약
PrimaryActorTick.bCanEverTick = true;
PrimaryActorTick.TickInterval = 0.f; // 0 = 매 프레임, 0.1 = 초당 10회
// 생성자에서 컴포넌트 생성 — CreateDefaultSubobject는 생성자에서만 유효
MeshComponent = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("MeshComponent"));
SetRootComponent(MeshComponent);
}
void AMyActor::PostInitializeComponents()
{
Super::PostInitializeComponents();
// 컴포넌트가 모두 초기화된 이후 시점
// 예: 컴포넌트의 이벤트에 델리게이트 바인딩
if (MeshComponent)
{
MeshComponent->OnComponentHit.AddDynamic(this, &AMyActor::OnHit);
}
}
void AMyActor::BeginPlay()
{
Super::BeginPlay(); // 부모 구현 호출 필수
// 게임 시작 시 1회 초기화 로직
UE_LOG(LogTemp, Log, TEXT("AMyActor BeginPlay: %s"), *GetName());
// 타이머 예시: 3초 후 Effect 호출
GetWorldTimerManager().SetTimer(
EffectTimerHandle,
this,
&AMyActor::PlayEffect,
3.f,
false // 반복 여부
);
}
void AMyActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
// 매 프레임 회전
AddActorLocalRotation(FRotator(0.f, RotationSpeed * DeltaTime, 0.f));
}
void AMyActor::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
// EndPlayReason으로 소멸 원인 파악 가능
switch (EndPlayReason)
{
case EEndPlayReason::Destroyed:
UE_LOG(LogTemp, Log, TEXT("Actor destroyed: %s"), *GetName());
break;
case EEndPlayReason::LevelTransition:
UE_LOG(LogTemp, Log, TEXT("Level transition: %s"), *GetName());
break;
default:
break;
}
// 타이머 정리
GetWorldTimerManager().ClearTimer(EffectTimerHandle);
// 부모 구현 호출 — 반드시 마지막에
Super::EndPlay(EndPlayReason);
}
void AMyActor::PlayEffect()
{
// 타이머 콜백 예시
UE_LOG(LogTemp, Log, TEXT("Effect played!"));
}
void AMyActor::OnHit(UPrimitiveComponent* HitComp, AActor* OtherActor,
UPrimitiveComponent* OtherComp, FVector NormalImpulse,
const FHitResult& Hit)
{
if (OtherActor && OtherActor != this)
{
UE_LOG(LogTemp, Log, TEXT("Hit by: %s"), *OtherActor->GetName());
}
}

4.3 PrimaryActorTick 설정과 성능 고려

Section titled “4.3 PrimaryActorTick 설정과 성능 고려”

Tick은 강력하지만 비용이 높습니다. 불필요한 Tick은 성능에 악영향을 줍니다.

AMyActor::AMyActor()
{
// 기본: Tick 비활성화 (필요할 때만 켜는 것이 권장)
PrimaryActorTick.bCanEverTick = false;
}
// 런타임에 Tick 동적 활성화/비활성화
void AMyActor::EnableTick(bool bEnable)
{
SetActorTickEnabled(bEnable);
}
// Tick 간격 조정 — 모든 오브젝트가 매 프레임 업데이트될 필요는 없음
// 예: AI 판단 로직은 0.1~0.5초 간격으로도 충분
PrimaryActorTick.TickInterval = 0.1f; // 초당 10회

Tick 대신 타이머를 쓸 수 있는 경우: 일정 주기로 한 번씩 실행되는 로직은 SetTimerTick + 카운터보다 효율적입니다.


5. 실습 예시 — 미니 아이템 컴포넌트 구현

Section titled “5. 실습 예시 — 미니 아이템 컴포넌트 구현”

앞서 배운 내용을 종합한 간단한 아이템 Actor를 만들어 봅니다. 플레이어가 가까이 오면 자동으로 획득되고, 일정 시간 후 리스폰되는 아이템입니다.

PickupItem.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "PickupItem.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnItemPickedUp, APickupItem*, Item);
UCLASS(BlueprintType, Blueprintable)
class MYGAME_API APickupItem : public AActor
{
GENERATED_BODY()
public:
APickupItem();
// Blueprint에서 구독 가능한 이벤트
UPROPERTY(BlueprintAssignable, Category = "Pickup")
FOnItemPickedUp OnItemPickedUp;
// Blueprint에서 호출 가능
UFUNCTION(BlueprintCallable, Category = "Pickup")
void PickUp(AActor* Picker);
protected:
virtual void BeginPlay() override;
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
private:
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components",
meta = (AllowPrivateAccess = "true"))
TObjectPtr<UStaticMeshComponent> MeshComp;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components",
meta = (AllowPrivateAccess = "true"))
TObjectPtr<class USphereComponent> OverlapComp;
// 아이템 이름 — 에디터에서 설정
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Item",
meta = (AllowPrivateAccess = "true"))
FText ItemName;
// 리스폰 대기 시간 (초)
UPROPERTY(EditDefaultsOnly, Category = "Item")
float RespawnDelay = 5.f;
// 이미 획득된 상태인지
bool bIsPickedUp = false;
FTimerHandle RespawnTimerHandle;
UFUNCTION()
void OnOverlapBegin(UPrimitiveComponent* OverlappedComp, AActor* OtherActor,
UPrimitiveComponent* OtherComp, int32 OtherBodyIndex,
bool bFromSweep, const FHitResult& SweepResult);
void Respawn();
};
PickupItem.cpp
#include "PickupItem.h"
#include "Components/SphereComponent.h"
#include "Components/StaticMeshComponent.h"
APickupItem::APickupItem()
{
PrimaryActorTick.bCanEverTick = false; // Tick 불필요
// 루트: 충돌 구체
OverlapComp = CreateDefaultSubobject<USphereComponent>(TEXT("OverlapComp"));
OverlapComp->SetSphereRadius(100.f);
OverlapComp->SetCollisionProfileName(TEXT("Trigger"));
SetRootComponent(OverlapComp);
// 비주얼 메시 (루트에 부착)
MeshComp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("MeshComp"));
MeshComp->SetupAttachment(OverlapComp);
MeshComp->SetCollisionEnabled(ECollisionEnabled::NoCollision);
}
void APickupItem::BeginPlay()
{
Super::BeginPlay();
// 오버랩 이벤트 바인딩
OverlapComp->OnComponentBeginOverlap.AddDynamic(this, &APickupItem::OnOverlapBegin);
}
void APickupItem::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
GetWorldTimerManager().ClearTimer(RespawnTimerHandle);
Super::EndPlay(EndPlayReason);
}
void APickupItem::OnOverlapBegin(UPrimitiveComponent* OverlappedComp, AActor* OtherActor,
UPrimitiveComponent* OtherComp, int32 OtherBodyIndex,
bool bFromSweep, const FHitResult& SweepResult)
{
if (bIsPickedUp || !OtherActor)
{
return;
}
// 플레이어인지 확인 (프로젝트에 맞게 캐스트 타입 변경)
if (OtherActor->ActorHasTag(FName("Player")))
{
PickUp(OtherActor);
}
}
void APickupItem::PickUp(AActor* Picker)
{
if (bIsPickedUp)
{
return;
}
bIsPickedUp = true;
UE_LOG(LogTemp, Log, TEXT("%s picked up %s"), *Picker->GetName(), *ItemName.ToString());
// Blueprint 및 C++ 구독자에게 이벤트 전파
OnItemPickedUp.Broadcast(this);
// 비주얼 숨기기, 충돌 비활성화
SetActorHiddenInGame(true);
SetActorEnableCollision(false);
// 리스폰 타이머
GetWorldTimerManager().SetTimer(
RespawnTimerHandle,
this,
&APickupItem::Respawn,
RespawnDelay,
false
);
}
void APickupItem::Respawn()
{
bIsPickedUp = false;
SetActorHiddenInGame(false);
SetActorEnableCollision(true);
UE_LOG(LogTemp, Log, TEXT("%s respawned"), *ItemName.ToString());
}

이 가이드에서 다룬 UE C++ 입문 핵심 4가지를 정리합니다.

개념핵심 요약
UObject 시스템모든 UE 관리 객체의 기반. NewObject, CreateDefaultSubobject, SpawnActor로 생성
매크로 시스템UCLASS·UPROPERTY·UFUNCTION은 엔진 리플렉션 등록용 “신고서”
가비지 컬렉션UPROPERTY 없는 원시 포인터는 위험. TObjectPtr/TWeakObjectPtr 사용
AActor 생명주기생성자 → BeginPlayTickEndPlay 순서를 지켜 코드를 배치
  • UActorComponent vs USceneComponent: 컴포넌트 설계 패턴과 부착(Attach) 계층 구조
  • 델리게이트(Delegate): DECLARE_DYNAMIC_MULTICAST_DELEGATE를 활용한 이벤트 시스템
  • 네트워크 복제(Replication): Replicated, RepNotify, RPC 기초
  • GameplayAbilitySystem(GAS): 스킬·버프 시스템 구현 프레임워크
  • Enhanced Input System: 입력 액션과 매핑 컨텍스트 C++ 연동