TIL

[UE5] 언리얼 C++기초 정리

think95592 2026. 6. 5. 20:42

1. 액터 클래스에 컴포넌트 추가하기

액터(Actor)는 배치 가능한 기본 단위이며, 실질적인 기능과 비주얼은 컴포넌트(Component)를 조립하여 구현합니다. C++에서 컴포넌트를 추가할 때는 반드시 생성자(Constructor)에서 작업해야 합니다.

핵심 단계

  1. 헤더 파일(.h): 컴포넌트 포인터 변수를 선언하고, 에디터에서 접근할 수 있도록 UPROPERTY 매크로를 붙입니다.
  2. 소스 파일(.cpp): 생성자에서 CreateDefaultSubobject를 통해 컴포넌트를 생성하고 루트 컴포넌트를 설정합니다.
C++
 
// MyActor.h
#pragma once

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

UCLASS()
class MYPROJECT_API AMyActor : public AActor
{
    GENERATED_BODY()
    
public:	
    AMyActor();

protected:
    // 컴포넌트를 담을 포인터 변수 선언
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
    class USceneComponent* MyRootComponent;

    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
    class UStaticMeshComponent* MyMeshComponent;
};
C++
 
// MyActor.cpp
#include "MyActor.h"
#include "Components/StaticMeshComponent.h"

AMyActor::AMyActor()
{
    // 1. 루트로 사용할 씬 컴포넌트 생성 및 지정
    MyRootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("RootComponent"));
    RootComponent = MyRootComponent;

    // 2. 스태틱 메시 컴포넌트 생성 및 루트에 부착
    MyMeshComponent = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("MeshComponent"));
    MyMeshComponent->SetupAttachment(RootComponent);
}

2. 액터 라이프사이클 & 틱(Tick) 함수로 트랜스폼 조정

액터는 게임 월드에 생성되고 소멸할 때까지 고유의 수명 주기(Lifecycle)를 가집니다. 주요 이벤트 함수는 다음과 같습니다.

주요 라이프사이클 이벤트

  • 생성자 (Constructor): 에디터 로드 및 기본 에셋 세팅 (게임 플레이 중에는 호출되지 않음).
  • BeginPlay(): 액터가 게임 월드에 배치되거나 스폰되어 게임이 시작될 때 딱 한 번 호출됩니다. 초기화 로직을 넣기에 적합합니다.
  • Tick(float DeltaTime): 매 프레임마다 호출되는 함수입니다. 지속적인 움직임이나 상태 업데이트를 처리합니다.
  • EndPlay(): 액터가 월드에서 제거(Destroy)되거나 게임이 종료될 때 호출됩니다.

Tick 함수로 트랜스폼(위치/회전) 조정하기

매 프레임 액터를 회전시키거나 이동시키고 싶다면 Tick 함수 내부에서 AddActorLocalRotation이나 SetActorLocation 같은 함수를 사용합니다. 이때 프레임률(FPS)에 상관없이 일정한 속도를 유지하기 위해 DeltaTime을 반드시 곱해줘야 합니다.

C++
 
// MyActor.cpp 내부 구현 예시

void AMyActor::BeginPlay()
{
    Super::BeginPlay();
    
    // 기본적으로 Tick이 켜져 있도록 설정 (생성자에서 해도 됨)
    PrimaryActorTick.bCanEverTick = true; 
}

void AMyActor::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);

    // 1. 매 프레임 회전값 계산 (초당 90도 회전)
    FRotator NewRotation = FRotator(0.0f, 90.0f * DeltaTime, 0.0f);
    AddActorLocalRotation(NewRotation);

    // 2. 매 프레임 위아래로 움직이는 로직 (예시)
    FVector NewLocation = GetActorLocation();
    float RunningTime = GetGameTimeSinceCreation();
    NewLocation.Z += FMath::Sin(RunningTime * 2.0f) * 0.5f; // 사인파를 이용한 부유 효과
    SetActorLocation(NewLocation);
}

3. C++ 클래스와 리플렉션(Reflection) 시스템

C++은 기본적으로 리플렉션(프로그램이 실행 중에 자기 자신의 구조를 조사하는 기능)을 지원하지 않는 정적 언어입니다. 하지만 언리얼 엔진은 가비지 컬렉션, 블루프린트 연동, 에디터 디테일 패널 노출, 직렬화(Serialization) 등을 구현하기 위해 자체적인 리플렉션 시스템(프로그램 등록 정보 시스템)을 구축했습니다.

어떻게 작동하나요?

UHT(Unreal Header Tool)가 소스 코드를 빌드하기 전 헤더 파일을 파싱하여 특수한 매크로들을 감지하고, 필요한 리플렉션 데이터를 .generated.h 파일에 자동으로 생성합니다.

핵심 매크로 4총사

매크로 대상 설명 주요 컴파일러 지시어 (지정자)
UCLASS() 클래스 이 C++ 클래스를 언리얼 엔진이 인식하도록 등록합니다. Blueprintable (블루프린트로 상속 가능)
USTRUCT() 구조체 언리얼 전용 구조체로 등록합니다. BlueprintType
UPROPERTY() 변수 에디터 노출, 블루프린트 연동, 가비지 컬렉션(메모리 관리) 대상 지정을 담당합니다. EditAnywhere, BlueprintReadWrite, Category
UFUNCTION() 함수 블루프린트에서 호출하거나, RPC(네트워크) 함수로 사용할 수 있게 만듭니다. BlueprintCallable, BlueprintPure

💡 중요 포인트 (가비지 컬렉션)

언리얼 엔진의 메모리 관리 시스템(Garbage Collection)의 혜택을 받으려면, 해당 언리얼 오브젝트(UObject 파생 클래스) 포인터 변수 위에 반드시 **UPROPERTY()**를 써주어야 합니다. 그렇지 않으면 엔진이 해당 오브젝트가 사용 중인지 모르고 메모리에서 임의로 지워버려 댕글링 포인터(Dangling Pointer)로 인한 크래시가 발생할 수 있습니다.