게임 엔진/Unreal
[Unreal Engine] Reflection System
Henzee
2025. 6. 16. 00:27
1. Reflection System
문서 링크 : Reflection System / Unreal Object Handling
1) 언리얼 오브젝트의 구성

- 언리얼 오브젝트에는 특별한 프로퍼티와 함수를 지정할 수 있음
- 관리되는 클래스 멤버 변수: UPROPERTY
- 관리되는 클래스 멤버 함수: UFUNCTION
- 에디터와 연동되는 메타 데이터를 심을 수 있음
- 리플렉션 시스템을 사용해 접근 지시자와 무관하게 값을 설정할 수 있음
- 언리얼 오브젝트에는 항상 클래스 정보를 담은 UClass 객체가 매칭되어 있음
- 클래스를 사용해 자신이 가진 프로퍼티와 함수 정보를 컴파일 타임과 런타임에서 조회할 수 있음
- NewObject API를 사용해 생성해야 함
2) 클래스 기본 오브젝트 (CDO, Class Default Object)

- 언리얼 클래스 정보에는 클래스 기본 오브젝트가 함께 포함되어 있음
- 클래스 기본 오브젝트는 줄여서 CDO라고 부름
- CDO는 언리얼 객체가 가진 기본값을 보관하는 템플릿 객체
- 한 클래스로부터 다수의 물체를 생성해 게임 콘텐츠에 배치할 때 일관성 있게 기본값을 조정하는데 유용하게 사용됨
- CDO는 클래스 정보로부터 GetDefaultObject() 함수를 통해 얻을 수 있음
- UClass 및 CDO는 엔진 초기화 과정에서 생성되므로 콘텐츠 제작에서 안심하고 사용할 수 있음
3) 언리얼 오브젝트의 특징
- 클래스 기본 객체(CDO, Class Default Object): 클래스의 기본값과 타입 정보 제공
- 리플렉션(Reflection): 런타임에서 클래스 정보 참조 기능
- 인터페이스(Interface): 모던 객체 지향 언어가 제공하는 인터페이스의 제공
- 향상된 열거형
- 델리게이트(Delegate): 객체 간의 결합을 낮출 수 있는 델리게이트 기능 제공
- 가비지 컬렉션(Garbage Collection): 자동 메모리 관리
- 향상된 구조체(Struct): 리플렉션이 가능한 구조체 지원
- 직렬화(Serialization): 객체 정보를 바이트 스트림으로 저장, 전송, 로드하는 기능
2. Reflection System을 사용한 예제 구현
1) 예제를 위한 클래스 다이어그램

- 어떤 학교에서 학생과 교수가 함께 수업하는 상황 구현
- 학교 정보는 GameInstance에서 지정
- 인물 클래스 (Person)
- 학생 클래스 (Student)
- 선생 클래스 (Teacher)
2) GameInstance 생성


3) UCLASS와 CDO의 동작 방식
- UClass



- CDO
※ 생성자 코드를 변경하는 경우 헤더 파일과 마찬가지로 에디터를 끄고 빌드 진행


4) 언리얼 오브젝트의 속성과 함수

- 클래스에 설정할 프로퍼티 정보
- Person에는 DoLesson이라는 가상 함수가 있음
- Student의 DoLesson은 수업을 듣는 행동
- Teacher의 DoLesson은 수업을 가르치는 행동
5) 클래스 추가 (Person, Student, Teacher)
- Person 클래스

// Header File
#pragma once
#include "CoreMinimal.h"
#include "UObject/NoExportTypes.h"
#include "Person.generated.h"
/**
*
*/
UCLASS()
class HELLOUNREAL_API UPerson : public UObject
{
GENERATED_BODY()
public:
UPerson();
UFUNCTION()
virtual void DoLesson();
const FString& GetName() const;
void SetName(const FString& InName);
protected:
UPROPERTY()
FString Name;
UPROPERTY()
int32 Year;
private:
};
// Cpp File
#include "Person.h"
UPerson::UPerson()
{
Name = TEXT("홍길동");
Year = 1;
}
void UPerson::DoLesson()
{
UE_LOG(LogTemp, Log, TEXT("%s님이 수업에 참여합니다."), *Name);
}
const FString& UPerson::GetName() const
{
return Name;
}
void UPerson::SetName(const FString& InName)
{
Name = InName;
}
- Student, Teacher 클래스

※ 헤더 파일에서 ObjectName.generated.h 헤더가 맨 밑으로 와야 함
// Header File
#pragma once
#include "CoreMinimal.h"
#include "Person.h"
#include "Student.generated.h" // 해당 헤더 파일이 맨 밑으로 와야 함
/**
*
*/
UCLASS()
class HELLOUNREAL_API UStudent : public UPerson
{
GENERATED_BODY()
public:
UStudent();
virtual void DoLesson() override;
private:
UPROPERTY()
int32 Id;
};
// Cpp File
#include "Student.h"
UStudent::UStudent()
{
Name = TEXT("이학생");
Year = 1;
Id = 1;
}
void UStudent::DoLesson()
{
Super::DoLesson();
UE_LOG(LogTemp, Log, TEXT("%d학년 %d번 %s님이 수업을 듣습니다."), Year, Id, *Name);
}
// Header File
#pragma once
#include "CoreMinimal.h"
#include "Person.h"
#include "Teacher.generated.h"
/**
*
*/
UCLASS()
class HELLOUNREAL_API UTeacher : public UPerson
{
GENERATED_BODY()
public:
UTeacher();
virtual void DoLesson() override;
private:
UPROPERTY()
int32 Id;
};
// Cpp File
#include "Teacher.h"
UTeacher::UTeacher()
{
Name = TEXT("이선생");
Year = 3;
Id = 1;
}
void UTeacher::DoLesson()
{
Super::DoLesson();
UE_LOG(LogTemp, Log, TEXT("%d년차 선생님 %s님이 수업을 강의합니다."), Year, *Name);
}
6) MyGameInstance에 언리얼 오브젝트 객체 생성
※ 소스 파일에서 해당 오브젝트가 선언된 헤더가 맨 위로 와야 함
// Cpp File
#include "MyGameInstance.h" // 해당 오브젝트가 선언된 헤더가 맨 위에 있어야 함
#include "Student.h"
#include "Teacher.h"
UMyGameInstance::UMyGameInstance()
{
SchoolName = TEXT("기본학교"); // CDO에 기본값으로 저장됨
}
void UMyGameInstance::Init()
{
Super::Init();
UE_LOG(LogTemp, Log, TEXT("===================================="));
UStudent* Student = NewObject<UStudent>();
UTeacher* Teacher = NewObject<UTeacher>();
// getter, setter 함수를 이용해 멤버 변수 값 사용
Student->SetName(TEXT("학생1"));
UE_LOG(LogTemp, Log, TEXT("새로운 학생 이름 : %s"), *Student->GetName());
// Reflection System을 이용해 멤버 변수 값 사용
FString CurrentTeacherName;
FString NewTeacherName(TEXT("김선생"));
FProperty* NameProp = UTeacher::StaticClass()->FindPropertyByName(TEXT("Name"));
if (NameProp)
{
NameProp->GetValue_InContainer(Teacher, &CurrentTeacherName);
UE_LOG(LogTemp, Log, TEXT("현재 선생님 이름 : %s"), *CurrentTeacherName);
NameProp->SetValue_InContainer(Teacher, &NewTeacherName);
UE_LOG(LogTemp, Log, TEXT("새로운 선생님 이름 : %s"), *Teacher->GetName());
}
UE_LOG(LogTemp, Log, TEXT("===================================="));
Student->DoLesson();
// Reflection System 사용해 멤버 함수 값 사용
UFunction* DoLessonFunc = Teacher->GetClass()->FindFunctionByName(TEXT("DoLesson"));
if (DoLessonFunc)
{
Teacher->ProcessEvent(DoLessonFunc, nullptr);
}
UE_LOG(LogTemp, Log, TEXT("===================================="));
}
