Unreal5/Unreal C++
[U C++] Unreal Object & Reflection System
taene_
2024. 7. 5. 19:46
Unreal Obejct System
- 언리얼 오브젝트에는 항상 클래스 정보를 담은 UClass 객체가 매칭되어 있다.
- UClass로부터 언리얼 오브젝트의 정보를 파악할 수 있다.
- UClass에는 클래스 기본 오브젝트(Class Default Object)가 연결되어 있어 이를 활용해 개발의 생산성을 향상시킬 수 있다.
- 클래스 정보와 CDO는 엔진 초기화 과정에서 생성되므로 게임 개발단계에서 안전하게 사용이 가능하다.
- 헤더 정보를 변경하거나 생성자 정보를 변경하면 에디터를 끄고 컴파일하는 것이 안정적이다.
Unreal Reflection System의 활용1
//.h
#pragma once
#include "CoreMinimal.h"
#include "UObject/NoExportTypes.h"
#include "Person.generated.h"
/**
*
*/
UCLASS()
class OBJECTREFLECTION_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:
};
- UFUNCTION(): 아래의 함수를 언리얼 엔진이 관리하게끔 하는 매크로
- UPROPERTY(): SchoolName에 관련된 값은 언리얼 엔진이 파악하고 관리할 수 있고, 리플렉션 시스템을 사용해서 SchoolName에 대한 정보를 런타임이든 컴파일타임이든 언제든지 얻어올 수 있게 된다.
//.cpp
#include "MyGameInstance.h"
UMyGameInstance::UMyGameInstance()
{
//생성자 코드에서 Class Default Object의 기본값을 변경하는 경우,
//헤더파일 변경의 경우와 같이 에디터를 끄고 다시 빌드하고 시작해야한다.
SchoolName = TEXT("OO대학교");
//엔진이 초기화되는 과정에서 Class Default Object나 UClass 정보들이 그때 만들어지고,
//이것들이 다 만들어진 이후에 에디터나 게임 등의 어플리케이션이 가동된다.
}
void UMyGameInstance::Init()
{
Super::Init();
UE_LOG(LogTemp, Log, TEXT("============="));
//UMyGameInstance에 대한 클래스 정보를 얻어오기
UClass* ClassRuntime = GetClass(); //GetClass() - UMyGameInstance에 대한 클래스 정보를 런타임에서 얻을 수 있다.
UClass* ClassCompile = UMyGameInstance::StaticClass(); //컴파일타임에서 얻을 수 있다. 위와 동일한 객체를 가리킨다.
//검증 코드들
//check(ClassRuntime == ClassCompile);
//ensure(ClassRuntime != ClassCompile);
//ensureMsgf(ClassRuntime != ClassCompile, TEXT("다르다!!"));
UE_LOG(LogTemp, Log, TEXT("학교를 담당하는 클래스 이름: %s"), *ClassRuntime->GetName());
SchoolName = TEXT("한라대학교");
UE_LOG(LogTemp, Log, TEXT("학교 이름: %s"), *SchoolName);
UE_LOG(LogTemp, Log, TEXT("학교 이름 기본값: %s"), *GetClass()->GetDefaultObject<UMyGameInstance>()->SchoolName);
UE_LOG(LogTemp, Log, TEXT("============="));
}
- check(): 반드시 검증하고 넘어가야 하는 상황이 발생할때 check()라는 Assertion 함수를 사용한다.
- ensure(): check()와 다르게 이곳에 걸리면 에디터가 안꺼지고도 문제점 확인이 가능하다.(output log's Red Line)
- ensureMsgf(): 이곳에 걸리면 문자열 로그가 추가로 올라온다.
Unreal Reflection System의 활용2
//.cpp
#include "MyGameInstance.h" //해당 cpp파일의 헤더파일을 include하는 것이 맨위에 와야함
#include "Student.h"
#include "Teacher.h"
UMyGameInstance::UMyGameInstance()
{
//생성자 코드에서 Class Default Object의 기본값을 변경하는 경우,
//헤더파일 변경의 경우와 같이 에디터를 끄고 다시 빌드하고 시작해야한다.
SchoolName = TEXT("OO대학교");
//엔진이 초기화되는 과정에서 Class Default Object나 UClass 정보들이 그때 만들어지고,
//이것들이 다 만들어진 이후에 에디터나 게임 등의 어플리케이션이 가동된다.
}
void UMyGameInstance::Init()
{
Super::Init();
/*
UE_LOG(LogTemp, Log, TEXT("==========================="));
//UMyGameInstance에 대한 클래스 정보를 얻어오기
UClass* ClassRuntime = GetClass(); //GetClass() - UMyGameInstance에 대한 클래스 정보를 런타임에서 얻을 수 있다.
UClass* ClassCompile = UMyGameInstance::StaticClass(); //컴파일타임에서 얻을 수 있다. 위와 동일한 객체를 가리킨다.
//검증 코드들
//check(ClassRuntime == ClassCompile); //반드시 검증하고 넘어가야 하는 상황이 발생할때 check()라는 Assertion 함수를 사용한다.
//ensure(ClassRuntime != ClassCompile); //check()와 다르게 이곳에 걸리면 에디터가 안꺼지고도 문제점 확인이 가능하다.(output log's Red Line)
//ensureMsgf(ClassRuntime != ClassCompile, TEXT("다르다!!")); //이곳에 걸리면 문자열 로그가 추가로 올라온다.
UE_LOG(LogTemp, Log, TEXT("학교를 담당하는 클래스 이름: %s"), *ClassRuntime->GetName());
SchoolName = TEXT("한라대학교");
UE_LOG(LogTemp, Log, TEXT("학교 이름: %s"), *SchoolName);
UE_LOG(LogTemp, Log, TEXT("학교 이름 기본값: %s"), *GetClass()->GetDefaultObject<UMyGameInstance>()->SchoolName);
*/
UE_LOG(LogTemp, Log, TEXT("==========================="));
//Person을 상속받는 Student와 Teacher 객체 생성
UStudent* Student = NewObject<UStudent>();
UTeacher* Teacher = NewObject<UTeacher>();
Student->SetName(TEXT("학생1"));
UE_LOG(LogTemp, Log, TEXT("새로운 학생 이름: %s"), *Student->GetName());
//======= Reflection System 활용 =======
//클래스의 정보에서 클래스의 속성 정보인 프로퍼티를 이름으로 검색해서 뺴오고,
//이 프로퍼티에 대해서(Name 속성) 우리가 지정한 인스턴스(Teacher)의 값을 빼오는 구조
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();
UFunction* DoLessonFunc = Teacher->GetClass()->FindFunctionByName(TEXT("DoLesson"));
if (DoLessonFunc)
{
Teacher->ProcessEvent(DoLessonFunc, nullptr);
}
UE_LOG(LogTemp, Log, TEXT("==========================="));
}
Unreal Reflection System의 특징
- 리플렉션 시스템을 사용해 언리얼 오브젝트의 특정 속성과 함수를 이름으로 검색할 수 있다.
- FProperty: 리플렉션 시스템, 언리얼 오브젝트의 속성 (타입)
- FindPropertyByName(TEXT("PropertyName")): 속성을 이름으로 검색하는 함수
- UFunction: 리플렉션 시스템, 언리얼 오브젝트의 함수 (타입)
- FindFunctionByName(TEXT("FunctionName")): 함수를 이름으로 검색하는 함수
- FProperty: 리플렉션 시스템, 언리얼 오브젝트의 속성 (타입)
- 리플렉션 시스템을 사용해 접근 지시자와 무관하게 값을 설정할 수 있다.
- FProperty->GetValue_InContainer( '지정한 언리얼 오브젝트', '속성과 같은 타입의 빈 변수' ): 언리얼 오브젝트의 지정한 속성을 변수에 가져오는 함수
- FProperty->SetValue_InContainer( '지정한 언리얼 오브젝트', '값이 있는 속성과 같은 타입의 변수' ): 언리얼 오브젝트의 지정한 속성을 지정한 변수로 설정하는 함수
- 리플렉션 시스템을 사용해 언리얼 오브젝트의 함수를 호출할 수 있다.
- ProcessEvent('지정한 언리얼 오브젝트의 함수', nullptr)
#include 주의사항
- cpp파일의 #include에서, 해당 cpp파일의 헤더파일의 include가 맨 위에 있어야한다.
- h파일의 #include에서, #include ".generated.h"가 맨 아래에 있어야 한다.