상세 컨텐츠

본문 제목

Unreal C++ 기초 4. 로그, 리플렉션, 함수, 리플렉션

Unreal C++

by hyunjunstar 2026. 4. 7. 23:00

본문

1. 로그

로그 만들고 사용하기

DECLARE_LOG_CATEGORY_EXTERN(로그명, 로그레벨, All);

 - 로그 카테고리 선언, 반드시 .h 파일에 선언

 - 로그명 : 사용할 로그 이름(직접 정의)

 - 로그레벨 - Log : 일반 로그, Display : 출력용, Warning : 경고, Error : 위험

 - ALL : 로그 출력 허용 범위

DEFINE_LOG_CATEGORY(로그명);

 - .h 파일에 선언한 로그 카테고리 실제로 생성

 - .h 파일에만 선언하고 .cpp 파일에 선언 안하면 컴파일 에러

UE_LOG(로그명, 로그레벨, TEXT("%s 내용"), *GetName());

 - TEXT("%s 내용") : 내용 입력(반드시 문자열 유니코드 처리)

 - *GetName() : 현재 객체명 출력

// Item.h

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Components/AudioComponent.h"
#include "Item.generated.h"

// 로그 카테고리 선언
DECLARE_LOG_CATEGORY_EXTERN(LogSparta, Warning, All);

UCLASS()
class SPARTAPROJECT_API AItem : public AActor
{
	GENERATED_BODY()
	
public:	
	AItem();
    
protected:
	virtual void BeginPlay() override;
}
// Item.cpp


#include "Item.h"

// 로그 카테고리 생성
DEFINE_LOG_CATEGORY(LogSparta);

AItem::AItem()
{
}

void AItem::BeginPlay()
{
	Super::BeginPlay();
	
    	// 로그 카테고리 출력
	UE_LOG(LogSparta, Log, TEXT("%s BeginPlay"), *GetName());
   	UE_LOG(LogSparta, Display, TEXT("%s BeginPlay"), *GetName());
 	UE_LOG(LogSparta, Warning, TEXT("%s BeginPlay"), *GetName());
 	UE_LOG(LogSparta, Error, TEXT("%s BeginPlay"), *GetName());
}

로그 확인하기

Visual Studio에서 빌드 및 실행 > 툴 바 Windows 클릭 > Output Log 클릭(활성화) > 게임 시작 >

Output Log 창에서 Filter 클릭 > Show All 체크 해제 > Filters 클릭 > LogSparta(로그카테고리명) 검색 후 체크 > 로그 확인

2. 라이프 사이클

액터 라이프 사이클이란

액터는 생성 > 실행 > 제거 과정을 거치며, 각 단계마다 여러 함수가 호출이 됨

 

Constructor(생성자) > PostInitializeComponents > BeginPlay > Tick > Destroyed > EndPlay

 

Constructor(생성자) - 객체가 메모리에 생성될 때 딱 한번 호출

PostInitializeComponents - 모든 컴포넌트가 완성된 직 후 호출됨. 컴포넌트끼리 데이터 주고받기, 상호작용

BeginPlay - 게임시작 또는 배치(Spawn) 직후 호출, 가장 많이 쓰이는 함수

Tick - 매 프레임마다 호출, 이동 회전 물리 처리 및 실시간 업데이트

Destroyed - 액터가 삭제 되기 직전에 호출, 게임 종료 및 레벨 전환시 생략될 수 있음

EndPlay - 게임 종료 및 파괴(Destroyed), 레벨 전환시 호출

라이프 사이클 로그 확인하기

// Item.h

#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Item.generated.h"

// 로그카테고리 선언
DECLARE_LOG_CATEGORY_EXTERN(LogSparta, Warning, All);

UCLASS()
class SPARTAPROJECT_API AItem : public AActor
{
	GENERATED_BODY()

public:
	AItem();

protected:
	USceneComponent* SceneRoot;
	UStaticMeshComponent* StaticMeshComp;

	// 라이프 사이클 함수 선언
	virtual void PostInitializeComponents() override;
	virtual void BeginPlay() override;
	virtual void Tick(float DeltaTime) override;
	virtual void Destroyed() override;
	virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
};
// Item.cpp

#include "Item.h"

// 로그 카테고리 생성
DEFINE_LOG_CATEGORY(LogSparta);

AItem::AItem()
{	// 생성자 로그 출력
	UE_LOG(LogSparta, Warning, TEXT("%s Constructor"), *GetName());
}

void AItem::PostInitializeComponents()
{
	Super::PostInitializeComponents();
    
	//PostInitializeComponents 로그 출력
	UE_LOG(LogSparta, Warning, TEXT("%s PostInitializeComponents"), *GetName());
}

void AItem::BeginPlay()
{
	Super::BeginPlay();

	// BeginPlay 로그 출력
	UE_LOG(LogSparta, Warning, TEXT("%s BeginPlay"), *GetName());
}

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

	// Tick 함수는 매 프레임마다 호출되므로
	// 여기서 로그를 찍으면 방대한 양의 메시지가 쌓일 수 있음.
	// 필요시 디버깅용 코드만 간단히 작성하거나, 별도 조건을 걸어 사용.
}

void AItem::Destroyed()
{
	// Destroyed 로그 출력, 삭제 후에는 출력이 생략될 수 있으니 삭제 전 확인
	UE_LOG(LogSparta, Warning, TEXT("%s Destroyed"), *GetName());

	Super::Destroyed();
}

void AItem::EndPlay(const EEndPlayReason::Type EndPlayReason)
{	
	// EndPlay 로그 확인
	UE_LOG(LogSparta, Warning, TEXT("%s EndPlay"), *GetName());

	Super::EndPlay(EndPlayReason);
}

3. Actor와 Transform

Actor

언리얼 엔진 게임 내에 존재하는 모든 오브젝트를 의미하며, 

건물, 캐릭터, 아이템 등 눈에 보이는 모든 것 = 액터

Transform

액터의 중요한 세가지 속성 위치(Location), 회전(Rotation), 크기(Scale)가 있으며, 

x축(빨간색), y축(초록색), z축(파란색) 으로 표시됩니다.

 

위치(Location)

 - 액터가 월드의 어느 지점에 있는지 나타냄

 - FVector(100.0f, 200.0f, 300.0f) 라면

 월드의 x축으로 100, y축으로 200, z축으로 300만큼 떨어진 위치를 의미합니다.

회전(Rotation)

 - 액터의 방향, 각도를 나타냄

 - Roll(x축), Pitch(y축), Yaw(z축)으로 표현합니다

크기(Scale)

 - 액터의 크기 비율

 - FVector(X, Y, Z) 형태로 표현되며, FVector(2.0f, 2.0f, 2.0f) 는 기본 크기의 2배를 의미합니다.

월드 좌표계와 로컬 좌표계

월드 좌표계

 - 게임 내 전체 기준으로 한 절대 좌표계

 - SetActorLocation(), GetActorLocation() 처럼 액터의 Transform을 변경하면 대부분 월드 좌표계를 기준으로 합니다.

로컬 좌표계

 - 액터 자신이나 부모 액터(또는 부모 컴포넌트)의 Transform을 기준으로 한 상대 좌표계

 - 계층 구조(부모 - 자식 관계)가 있는 경우, 자식은 부모의 Transform을 기준으로 움직임

부모 - 자식 컴포넌트 관계

액터는 여러 컴포넌트를 가질 수 있으며,

최상위 컴포넌트(루트 컴포넌트)를 기준으로 다른 컴포넌트들이 Attach(부착) 관계를 맺을수 있습니다.

 

부모 액터의 Transform이 변경되면 자식 액터는 상대 좌표값에 따라 함께 이동합니다.

 

부모 - 자식 관계가 맺어져 있다면, 

GetRelativeTransform() : 부모 기준의 상대 위치·회전·스케일을 가져옴

SetRelativeLocation() , SetRelativeRotation() : 부모 기준으로 자식의 위치, 회전을 조정

할 수가 있습니다.

 

로컬 좌표계를 이용하면 여러 컴포넌트를 한번에 움직이거나 특정 컴포넌트만 부모 기준으로 움직이게 만들 수 있습니다.

C++ 코드로 Transform 조정하기

Transform 조정 함수

SetActorLocation(FVector NewLocation) : 액터 위치 이동

SetActorRotation(FRotator NewRotation) : 액터 회전

SetActorScale3D(FVector NewScale) : 액터 크기 변경

GetActorLocation() , GetActorRotation() , GetActorScale3D() : 현재 Transform 정보 가져오기 

SetActorTransform(FTransform NewTransform) : 위치·회전·스케일을 한 번에 설정
// Item.cpp

void AItem::BeginPlay()
{
 	Super::BeginPlay();

 	// 위치, 회전, 스케일 설정하기
 
 	// (300, 200, 100) 위치로 이동
 	SetActorLocation(FVector(300.0f, 200.0f, 100.0f));
 
 	// Yaw 방향(z축)으로 45도 회전
 	SetActorRotation(FRotator(0.0f, 45.0f, 0.0f));
 
 	// 모든 축을 2배로 스케일
 	SetActorScale3D(FVector(2.0f));
}

FTransform

위치, 회전, 크기를 하나로 묶어서 관리하기 위한 구조체

Translation - 위치 FVector

Rotation - 회전 FRotator

Scale3D - 크기 FVector

// Item.cpp

void AItem::BeginPlay()
{
	Super::BeginPlay();

	FVector NewLocation(300.0f, 200.0f, 100.0f);	// 위치
	FRotator NewRotation(0.0f, 90.0f, 0.0f);	// pitch Y축, yaw Z축, roll X축
	FVector NewScale(2.0f);	// 크기

	FTransform NewTransform(NewRotation, NewLocation, NewScale);

	SetActorTransform(NewTransform);
}

 

4. Tick 함수

Tick() 이란

매 프레임마다 호출되는 함수

 - 랜더링, 물리, 액터 업데이트가 매 프레임마다 실행됨

 - 그중 액터 로직은 Tick()에서 처리

PrimaryActorTick.bCanEverTick = true; / true > tick 호출, false > tick 호출 안함

DeltaTime

이전 프레임부터 현재 프레임까지 걸린 시간(초)

- 60FPS 약 0.0167초

- 120FPS 약 0.0083초

FPS가 높을수록 DeltaTime이 작아지고 낮을수록 DeltaTime이 커짐

DeltaTime을 사용하는 이유

어느 FPS에서도 동일한 실제 속도를 유지하기 위해서 사용

DeltaTime을 곱해서 초 단위 기준으로 이동 및 회전을 계산

공식 : 이동량 = 속도 × DeltaTime

ex) x  += Speed * DeltaTime;

AddActorLocalRotation(FRotator(0.0f, 90.0f * DeltaTime, 0.0f));

Yaw(z축)을 틱당 90씩 회전 시키고 싶으면 위 코드처럼 하면됨

Tick 함수와 DeltaTime을 활용해서 회전 구현

! - Not 연산자 

FMath::IsNearlyZero - 값이 0에 가까우면 true

!FMath::IsNearlyZero - 값이 0이 아니면 true

// Item.h

// 회전 속도를 나타내는 변수 선언
float RotationSpeed;
// Item.cpp

AItem::AItem()
{
	// Tick() 함수를 사용하기 위해 생성자에서 활성화
	PrimaryActorTick.bCanEverTick = true;

	// 기본 회전속도 90.0f 설정(1초에 90도 회전)
	RotationSpeed = 90.0f;
}
// Item.cpp

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

	// RotationSpeed의 값이 거의 0인지 체크
	if (!FMath::IsNearlyZero(RotationSpeed))
	{
    	// 액터를 로컬 좌표계 기준 Yaw(z축)으로 프레임마다 일정하게 회전시키기 위해 
        // RotationSpeed(90.0f) * Deltatime로 프레임당 약 1.5도씩 회전, 1초에 90도 회전
		AddActorLocalRotation(FRotator(0.0f, RotationSpeed * DeltaTime, 0.0f));
	}

}

5. Reflection

Reflection (Blueprint + C++)

C++ 클래스의 변수 및 함수 정보를 언리얼 엔진이 이해하는 데이터로 변환해서 Blueprint랑 언리얼 에디터에서 사용 가능하게 만드는 기능이며, 

C++의 변수와 함수를 Blueprint에서 사용할 수 있게 하여 각 단점이 보완되며 효율적인 개발이 가능

C++ 뼈대(로직, 성능)
Blueprint 조작(연출, 설정)
Reflection 연결

장점

 - 코드 수정 없이 값 조정 가능

 - 바로 테스트 가능

 - 비개발자도 값 수정 가능

 - C++의 기능을 시각적으로 사용 가능

 - 대규모 프로젝트 진행시 개발 효율과 협업 효과 극대화

단점

 - 약간의 성능 비용

Blueprint

장점

 - 빠르게 만들고 바로 테스트 가능

 - 비개발자도 사용이 가능하며 UI, 연출, 이벤트 처리에 좋음

단점

 - 노드가 많아지면 가독성이 떨어짐

 - 성능이 C++보다 안좋음

 - 복잡한 로직 구현이 힘듬

C++

장점

 - 성능이 좋고 복잡한 시스템 구현이 가능

 - 엔진 깊은 부분까지 제어 가능

단점

 - 수정 후 컴파일이 필요하며 과정이 오래걸림

 - 비개발자가 접근하기 힘듬

 - 테스트 반복이 번거로움

블루프린트 클래스 생성 및 리플렉션 사용 

#include "Item.generated.h"

 - 클래스의 리플레션 및 엔진 통합에 필요한 코드가 들어있음

 - 리플렉션을 사용하기 위해서는 .h 파일에 아래 코드를 선언해야 하며 반드시 인클루드 마지막줄에 선언해야함

UCLASS()

 - 해당 클래스를 언리얼 엔진의 리플렉션 시스템에 등록

 - UCLASS()를 사용해야지만 블루프린트, 에디터에서 이 클래스를 인식하고 사용이 가능

GENERATED_BODY()

 - 언리얼 코드 생성 도구가 사용하는 코드 삽입을 하는 역할

 - 클래스 내부에 필요한 리플렉션 정보를 자동으로 생성해줌

// Item.h

#include "CoreMinimal.h"
#include "GameFrmework/Actor.h"
#include "Item.generated.h"

UCLASS()
class REFLTEST_API AItem : public AActor
{
	GENERATED_BODY()
}

코드 작성 후 언리얼 에디터에서

콘텐츠 브라우저 > 블루프린트 클래스로 상속해줄 C++ 클래스 우클릭 > Create Blueprint class based on 클래스명 클릭 > 

저장할 폴더 지정 및 이름 작성(BP_클래스명) > Create Class 클릭 > BP_클래스명 생성(부모 = C++ 클래스)

UCLASS() 매크로의 주요 지정자

UCLASS에 옵션을 주지 않으면 블루프린트에서 상속, 변수로 참조가 가능한 형태로 등록

주요 옵션

 - Blueprintable : 블루프린트에서 상속 가능한 클래스로 지정

 - NotBlueprintable : 블루프린트에서 이 클래스를 상속할 수 없도록 지정

 - BlueprintType : 블루프린트에서 변수나 참조로 사용할 수 있게 지정(상속용X)

변수에 리플렉션 적용하기

지정자가 없을시 엔진은 변수 존재는 인식하지만, 에디터와 블루프린트에서는 보이지 않음

UPROPERTY(편집범위, Blueprint접근, Category, meta옵션)

1. 편집 가능 범위 지정자 - 어디서 수정할 수 있는지 (1개만 사용 가능)

VisibleAnywhere : 보기만 가능, 수정 X

EditAnywhere : 클래스 기본값, 인스턴스 모두에서 수정 가능

EditDefaultsOnly : 클래스 기본값만 수정 가능

EditInstanceOnly : 배치된 오브젝트(인스턴스)만 수정 가능

2. Blueprint 접근성 지정자 - 블루프린트에서 수정할 수 있는지 (1개만 사용 가능)

BlueprintReadWrite : 읽기 + 수정 가능

BlueprintReadOnly : 읽기만 가능

3. Category 지정자 - 정리용 (1개만 사용 가능, | 로 하위 카테고리 구성 가능)

Category="Item|Components"

에디터의 Details 패널에서 폴더처럼 정리됨

4. 메타 옵션 지정자 (한번에 묶어서 여러개 사용 가능)

meta=(ClampMin="0.0")  / meta=(ClampMax="10.0") : 에디터에서 변수 입력 시 최소값, 최대값을 제한할 수 있습니다.

meta=(AllowPrivateAccess="true") : 해당 멤버가 private로 선언되어 있어도, 에디터나 Blueprint에서 접근할 수 있도록 허용해줌

// Item.h

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

UCLASS()
class REFLTEST_API AItem : public AActor
{
	GENERATED_BODY()
    
public:
	AItem();
    
protected:
	// 보기만 가능, 블루프리트에서 읽기만 가능
    	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Item|Components")
	USceneComponent* SceneRoot;
    
    	// 기본값 + 인스턴스 모두 수정 가능, 블루프리트에서 읽기 및 수정 가능
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item|Components")
	UStaticMeshComponent* StaticMeshComp;
    
    	// 기본값 + 인스턴스 모두 수정 가능, 블루프리트에서 읽기 및 수정 가능
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item|Components")
	UAudioComponent* AudioComp;
    
	// 기본값만 수정 가능, 블루프리트에서 읽기만 가능
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Item|Properties")
	float RotationSpeed;
};

함수에 리플렉션 적용하기

지정자가 없을시 엔진은 함수 존재는 인식하지만, 블루프린트에서는 직접 호출할 수 없음

UFUNCTION(Blueprint관련지정자, Category)

1. Blueprint 관련 지정자 - 블루프린트에서 어떻게 사용할지 

BlueprintCallable : 블루프린트에서 실행(노드 호출)이 가능한 함수

(버튼 누르기, 이벤트 실행, 위치 초기화 같은 동작 함수에 사용)

BlueprintPure : 값을 반환하는 함수(Getter 역할) Exec 핀 없음

BlueprintImplementableEvent : C++에서 호출하면 블루프린트에서 구현된 로직이 실행되는 함수

(선언은 C++, 구현은 블루프린트에서 작성)

3. Category 지정자 - 정리용 (1개만 사용 가능, | 로 하위 카테고리 구성 가능)

Category="Item|Actions"

블루프린트에서 함수 노드를 카테고리별로 정리

// Item.h

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

UCLASS()
class REFLTEST_API AItem : public AActor
{
	GENERATED_BODY()
    
public:
	AItem();
    
protected:
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Item|Properties")
	float RotationSpeed;
    
    	virtual void BeginPlay() override;

	// 블루프린트에서 실행 가능
	UFUNCTION(BlueprintCallable, Category="Item|Actions")
	void ResetActorPosition();

	// 블루프린트에서 값만 반환
	UFUNCTION(BlueprintPure, Category = "Item|Properties")
	float GetRotationSpeed() const;
	
    	// 함수 선언만, 구현은 블루프린트에서 작성
	UFUNCTION(BlueprintImplementableEvent, Category = "Item|Event")
	void OnItemPickedUP();
};

 

// Item.cpp


#include "Item.h"

AItem::AItem()
{
	RotationSpeed = 90.0f;
}


void AItem::BeginPlay()
{
	Super::BeginPlay();
	
    	// 블루프린트에서 구현한 함수를 C++에서 호출
	OnItemPickedUP();
}

// BlueprintCallable 함수 구현
void AItem::ResetActorPosition()
{	
	// 액터의 위치를 (0, 0, 0)으로 되돌림
	SetActorLocation(FVector::ZeroVector);
}

// BlueprintPure 함수 구현
float AItem::GetRotationSpeed() const
{	
	// RotationSpeed의 값을 반환
	return RotationSpeed;
}

 

관련글 더보기