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

 

 

인프런 이득우님의 '언리얼 프로그래밍 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);
    }

     

     

     


     

     

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

     

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

     

     

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

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