Unreal5/Unreal C++

[U C++] Unreal Object & Reflection System

taene_ 2024. 7. 5. 19:46

Unreal Obejct System

  1. 언리얼 오브젝트에는 항상 클래스 정보를 담은 UClass 객체가 매칭되어 있다.
  2. UClass로부터 언리얼 오브젝트의 정보를 파악할 수 있다.
  3. UClass에는 클래스 기본 오브젝트(Class Default Object)가 연결되어 있어 이를 활용해 개발의 생산성을 향상시킬 수 있다.
  4. 클래스 정보와 CDO는 엔진 초기화 과정에서 생성되므로 게임 개발단계에서 안전하게 사용이 가능하다.
  5. 헤더 정보를 변경하거나 생성자 정보를 변경하면 에디터를 끄고 컴파일하는 것이 안정적이다.

 

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->GetValue_InContainer( '지정한 언리얼 오브젝트', '속성과 같은 타입의 빈 변수' ): 언리얼 오브젝트의 지정한 속성을 변수에 가져오는 함수
    • FProperty->SetValue_InContainer( '지정한 언리얼 오브젝트', '값이 있는 속성과 같은 타입의 변수' ): 언리얼 오브젝트의 지정한 속성을 지정한 변수로 설정하는 함수
  • 리플렉션 시스템을 사용해 언리얼 오브젝트의 함수를 호출할 수 있다.
    • ProcessEvent('지정한 언리얼 오브젝트의 함수', nullptr)

 

#include 주의사항

  1. cpp파일의 #include에서, 해당 cpp파일의 헤더파일의 include가 맨 위에 있어야한다.
  2. h파일의 #include에서, #include ".generated.h"가 맨 아래에 있어야 한다.