이제 이 리플렉션 시스템을 사용하는 예제를 만들어보자
새로운 클래스를 만들자
UCLASS()
class OBJECTREFLECTION_API UMyGameInstance : public UGameInstance
{
GENERATED_BODY()
public:
UMyGameInstance();
virtual void Init() override;
private:
UPROPERTY()
FString SchoolName;
};
그리고 FString 변수에 언리얼이 관리 할수 있게 UPROPERTY() 매크로를 넣어둔다.
void UMyGameInstance::Init()
{
Super::Init();
UE_LOG(LogTemp, Log, TEXT("===================="));
UClass* ClassRuntime = GetClass();
UClass* ClassCompile = UMyGameInstance::StaticClass();
//check(ClassRuntime == ClassCompile);
//ensure(ClassRuntime != ClassCompile);
//ensureMsgf(ClassRuntime != ClassCompile, TEXT("일부러 에러를 발생시킨 코드"));
UE_LOG(LogTemp, Log, TEXT("학교를 담당하는 클래스 이름 : %s"), *ClassRuntime->GetName());
UE_LOG(LogTemp, Log, TEXT("===================="));
}
GetClass();, UMyGameInstance::StaticClass(); 이 두개의 함수 전부 클래스 이름을 갖고온다.
이를 확인하기 위해 주석 처리를 해놓은 함수를 한번 보자
check 함수는 꼭 검증해야 할 사항을 넣어서 체크 하자. 만약 안에 있는 값이 true 면 아무런 일이 일어나지 않지만, false 일 경우 크래시가 나게 된다. 그리고 게임을 빌드 할 경우 함수를 제거해서 문제 없어진다.
ensure 함수는 check함수와 비슷하지만, false 일 경우에 크래시가 나지 않는다. 로드만 출력 된다.
그리고 ensureMsgf는 원하는 로그 까지 출력이 가능하다.
UMyGameInstance::UMyGameInstance()
{
SchoolName = TEXT("기본학교");
}
이렇게 생성자에 값을 먼저 넣은 뒤,
void UMyGameInstance::Init()
{
Super::Init();
UE_LOG(LogTemp, Log, TEXT("===================="));
UClass* ClassRuntime = GetClass();
UClass* ClassCompile = UMyGameInstance::StaticClass();
//check(ClassRuntime == ClassCompile);
//ensure(ClassRuntime != ClassCompile);
//ensureMsgf(ClassRuntime != ClassCompile, TEXT("일부러 에러를 발생시킨 코드"));
UE_LOG(LogTemp, Log, TEXT("학교를 담당하는 클래스 이름 : %s"), *ClassRuntime->GetName());
SchoolName = TEXT("Null 학교");
UE_LOG(LogTemp, Log, TEXT("학교 이름 : %s"), *SchoolName);
UE_LOG(LogTemp, Log, TEXT("===================="));
}
중간에 수정을 하면 Null 학교로 수정이 되지만,
UE_LOG(LogTemp, Log, TEXT("학교 이름 기본값 : %s"), *GetClass()->GetDefaultObject<UMyGameInstance>()->SchoolName);
이 함수를 사용하게 되면 생성자에서 만든 기본 값을 출력하게 된다.
예제를 위한 클래스
- 어떤 학교에서 학생과 교스가 함께 수업하는 상황의 구현
- 학교 정보는 GameInstance에서 지정
- 인물 클래스(Person)
- 학생 클래스(Student)
- 선생 클래스(Teacher)
- 클래스에 설정할 프로퍼티 정보
- Person에는 DoLesson이라는 가상함수가 있음.
- Studen의 DoLesson은 수업을 듣는 행동
- Teacher의 DoLesson은 수업을 가르치는 행동
Person
// Fill out your copyright notice in the Description page of Project Settings.
#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:
};
가상함수로 DoLesson()을 선언하고 Name을 반환, 수정을 할수 있게 GetName, SetName을 선언해준다. Name과 Year은 언리얼에서 관리할 수 있게 UPROPERTY 매크로를 같이 선언해둔다
#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;
}
기본값, DoLesson 기본 명령어를 넣어둔다.
Student
#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;
};
Header파일을 include 할때 generated.h 이건 맨 마지막에 오도록 하고 컴파일 해준다.
고유의 값인 Id만 선언한다.
#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
// Fill out your copyright notice in the Description page of Project Settings.
#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;
};
#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 클래스로 다시 돌아온다.
예제
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"), *Teacher->GetName());
NameProp->SetValue_InContainer(Teacher, &NewTeacherName);
UE_LOG(LogTemp, Log, TEXT("새로운 선생님 이름 %s"), *Teacher->GetName());
}
Init에 넣어준다.
FProperty라는 변수에 프로퍼티 정보를 갖고온다. 저렇게 하면 UPROPERTY에 대한 포인터를 갖고온다.
그리고 GetValue_InContainer로 값을 갖고오거나, SetValue_InContrainer로 값을 변경할수 있다.
Student->DoLesson();
UFunction* DoLessonFunc = Teacher->GetClass()->FindFunctionByName(TEXT("DoLesson"));
if (DoLessonFunc != nullptr)
{
Teacher->ProcessEvent(DoLessonFunc, nullptr);
}
UE_LOG(LogTemp, Log, TEXT("===================="));
함수도 위 처럼 이름으로 호출이 가능하다.
언리얼의 기본 프레임 워크가 이런 리플렉션 시스템으로 이루어져있다.
'Unreal' 카테고리의 다른 글
5. 컴포지션 (0) | 2023.09.11 |
---|---|
4. 인터페이스 (0) | 2023.08.31 |
3-1. 언리얼 오브젝트 리플렉션 시스템 (0) | 2023.07.31 |
1. 코딩 규칙 (0) | 2023.07.03 |
0. 서론 (0) | 2023.07.03 |