언리얼 오브젝트 리플렉션 시스템의 활용하여 언리얼 오브젝트 리플렉션 시스템을 사용해 언리얼 오브젝트를 다루는 방법의 학습하자.

 

 

인프런 이득우님의 '언리얼 프로그래밍 Part1 - 언리얼 C++의 이해' 강의를 참고하였습니다. 
😎 [이득우의 언리얼 프로그래밍 Part1 - 언리얼 C++의 이해] 강의 들으러 가기!

 

 

목차

     

     


     

     

    언리얼 리플렉션 Unreal Reflection II

     


     

     

    예제 클래스 다이어그램

     

    • 어떤 학교에서 학생과 교수가 함께 수업하는 상황을 구현하고 싶다.
    • 학교 정보는 GameInstance에서 지정
    • 인물 클래스 Person
      • 학생 클래스 Student
      • 선생 클래스 Teacher


     

     

    예제 코드

     

    Person.h

    더보기
    #pragma once
    #include "CoreMinimal.h"
    #include "UObject/NoExportTypes.h"
    #include "Person.generated.h"
    
    UCLASS()
    class PRACTICEUNREAL_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;
    };

     

    Person.cpp

    더보기
    #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.h

    더보기
    #pragma once
    #include "CoreMinimal.h"
    #include "UObject/NoExportTypes.h"
    #include "Person.h"
    #include "Student.generated.h"
    
    UCLASS()
    class OBJECTREFLECTION_API UStudent : public UPerson
    {
    	GENERATED_BODY()
    public:
    	UStudent();
    	virtual void DoLesson() override;
    
    private:
    	UPROPERTY()
    	int32 Id;
    };

     

    Student.cpp

    더보기
    #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);
    }

     

     

    Teacher.h

    더보기
    #pragma once
    #include "CoreMinimal.h"
    #include "UObject/NoExportTypes.h"
    #include "Person.h"
    #include "Teacher.generated.h"
    
    UCLASS()
    class OBJECTREFLECTION_API UTeacher : public UPerson
    {
    	GENERATED_BODY()
    public:
    	UTeacher();
    	virtual void DoLesson() override;
    
    private:
    	UPROPERTY()
    	int32 Id;
    };

     

    Teacher.cpp

    더보기
    #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);
    }

     

     

     

     

    MyGameInstance.h

    더보기
    #pragma once
    #include "CoreMinimal.h"
    #include "Engine/GameInstance.h"
    #include "MyGameInstance.generated.h"
    
    UCLASS()
    class OBJECTREFLECTION_API UMyGameInstance : public UGameInstance
    {
    	GENERATED_BODY()	
    public:
    	UMyGameInstance();
    	virtual void Init() override;
    
    private:
    	UPROPERTY()
    	FString SchoolName;
    };

     

    MyGameInstance.cpp

    더보기
    #include "MyGameInstance.h" // 해당 언리얼 오브젝트에 선언된 헤더가 가장 위에 위치해야 한다.
    #include "Student.h"
    #include "Teacher.h"
    
    #include 순서도 중요하다. 아래와 같이 다른 언리얼 오브젝트 헤더가 더 위에 위치한 경우 헤더툴이 에러를 띄운다.
    #include "Student.h"
    #include "Teacher.h"
    #include "MyGameInstance.h" 
    
    UMyGameInstance::UMyGameInstance()
    {
    	SchoolName = TEXT("기본학교");
    }
    
    void UMyGameInstance::Init()
    {
    	Super::Init();
    
    	UE_LOG(LogTemp, Log, TEXT("============================="));
    	UClass* ClassRuntime = GetClass();
    	UClass* ClassCompile = UMyGameInstance::StaticClass();
    
    	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("============================="));
    
    	UStudent* Student = NewObject<UStudent>();
    	UTeacher* Teacher = NewObject<UTeacher>();
    	Student->SetName(TEXT("학생1"));
    	UE_LOG(LogTemp, Log, TEXT("새로운 학생 이름 %s"), *Student->GetName());
    
    	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("============================="));
    }

     

     

    실행화면

     


     

     

     

    리플렉션 시스템 (위의 예제 코드 분석)  

     


     

     

    NewObject<클래스 이름>()

     

    C++에서의 new 키워드가 언리얼 UObject에서는 NewObject<클래스 이름>() 과 유사하다.

    UStudent* Student = NewObject<UStudent>();
    UTeacher* Teacher = NewObject<UTeacher>();

     

     

    리플렉션을 통한 프로퍼티 변경 

     

    Get, Set 함수를 사용하는 방법이 아닌, 언리얼 엔진에서 제공하는 리플렉션 시스템을 사용해서 변경

    	FString CurrentTeacherName;
    	FString NewTeacherName(TEXT("이득우"));
    
    	FProperty* NameProp = UTeacher::StaticClass()->FindPropertyByName(TEXT("Name")); // 속성 이름("Name")으로 검색해서 포인터를 가져옴
    	if (NameProp)
    	{
    		NameProp->GetValue_InContainer(Teacher, &CurrentTeacherName);
    		UE_LOG(LogTemp, Log, TEXT("현재 선생님 이름: %s"), *CurrentTeacherName);  // "이선생" (CDO 기본값)
    
    		NameProp->SetValue_InContainer(Teacher, &NewTeacherName);
    		UE_LOG(LogTemp, Log, TEXT("새로운 선생님 이름: %s"), *Teacher->GetName()); // "이득우"
    	}

     


     

     

    리플렉션을 통한 함수 호출

     

    런타임에서 동작 중인 객체의 UFUNCTION 프로퍼티 중에서 DoLesson의 함수 포인터를 가져와  실행

    	Student->DoLesson();
        
    	// FindFunctionByName 함수를 통해 DoLesson 함수를 이름으로 검색해서 DoLessonFunc의 포인터를 가져옴
    	UFunction* DoLessonFunc = Teacher->GetClass()->FindFunctionByName(TEXT("DoLesson"));
    	if (DoLessonFunc) // "DoLesson"이름의 함수를 찾았다면
    	{
    		Teacher->ProcessEvent(DoLessonFunc, nullptr); // Teacher 인스턴스를 지정하고 ProcessEvent 함수를 이용해서 함수 포인터를 넘겨줌
    	}

     

    Student.cpp

    더보기
    #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);
    }

     

    Teacher.cpp

    더보기
    #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);
    }

     

     

     


     

     

    정리:  언리얼 리플렉션 시스템의 활용

     

    • 리플렉션 시스템을 사용해 언리얼 오브젝트의 특정 속성함수이름으로 검색할 수 있다.
    • 리플렉션 시스템을 사용해 접근 지시자 무관하게 속성값을 설정할 수 있다.
    • 리플렉션 시스템을 사용해 언리얼 오브젝트의 함수를 호출할 수 있다.

     

     

    언리얼 엔진의 기본 프레임웍은 리플렉션을 활용해 구축되어 있으므로,

    언리얼 엔진을 이해하기 위해서는 리플렉션 시스템을 이해하는 것이 필요함.