언리얼엔진5/[Part1] 이득우의 언리얼 프로그래밍

[이득우의 언리얼 프로그래밍 Part1 필기] 5-6. 언리얼 엔진 리플렉션 시스템

Rocketbabydolls 2024. 2. 13. 16:39

언리얼 엔진 게임 프레임워크는 리플렉션 시스템을 기반으로 짜여져 있기 때문에 이해하는 것이 중요하다.

 

리플렉션 시스템이란?

  • 프로그램 실행 시간에 자기 자신을 조사하는 것.
  • 다시 말하자면 프로그램이 실행 중에 동적으로 프로그램 내의 클래스의 데이터들에 접근하고 조작하는 것을 지원하는 시스템이다.
  • C++ 에서는 이것을 지원하지 않아서 언리얼에서 자체적으로 만들었다.

 

우리가 컴파일 전 작성해놓은 매크로를 헤더 툴이 분석해서 리플리케이션을 구성하는 소스코드를 만들어준다.

 

UPROPERTY() , UFUNCTION() 과 같이 매크로를 적어놓고 선언을 해주면 엔진의 가비지 컬렉터가 관리 해주는데 그렇지 않을 떄는 직접 프로그래머가 관리 해주면 된다.

 

UField

->UStruct

   ->UClass

이렇게 상속 받고 UStruct에서 리플렉션이 시작된다.

StaticClass() 혹은 GetClass() 를 통해 리플리케이션 정보에 접근 할 수 있다. (iterator 사용 가능)

 

 

 

 

StaticClass() : 컴파일타임에 이미 정해져있는 클래스

GetClass() : 런타임엥서 실제 객체의 클래스를 조회할때 사용된다.

 

https://devshovelinglife.tistory.com/715

 

[Unreal] GetClass 와 StaticClass 의 차이

GetClass vs StaticClass StaticClass 는 컴파일 타임에서 UClass 타입의 정보를 얻어오는 것이며, GetClass 는 런타임에서 실제 객체의 클래스를 조회할때 사용된다. AMyActor* ActorPtr = NewObject(...); UObject* ObjPtr = A

devshovelinglife.tistory.com

 

 

정리해보자

 

 

언리얼 디폴트 오브젝트(CDO)

  • 언리얼 객체가 가진 기본 값을 보관한다.
  • 클래스 한개로부터 다수의 물체를 생성해 게임 콘텐츠에 배치할 때 일관성 있게 기본 값 조정 하는데 사용한다.
  • 엔진 초기화 과정에서 생성된다(안심하고 사용 가능).
  • 자동으로 프로퍼티를 초기화한다(0).
  • CDO에는 값을변경해도 기본값 그대로 저장된다. 생성자에서 저장해놓은 기본값은 후에 변경을 해도 기본값 호출 가능

 

UCLASS()
class OBJECTREFLECTION_API UMyGameInstance : public UGameInstance
{
	GENERATED_BODY()	
public:
	UMyGameInstance();

	virtual void Init() override;

private:
	UPROPERTY()
	FString SchoolName;
	
};

 

 

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);

}

 

출력 결과

=======================
MyGameInstacne
청강문화산업대학교
기본학교

 

 

check() 사용은 중요하다. 게임 빌드 시에 check() 구문은 사라지기 때문에 괜찮다.

GPT의 설명

더보기

언리얼 엔진의 빌드 시에 Check() 구문은 코드의 런타임 상태를 확인하여 버그나 잠재적인 오류를 찾는 데 사용됩니다. 이러한 Check() 구문들은 디버그 모드에서는 실행되지만, 릴리스 모드에서는 실행되지 않습니다. 따라서 디버그 빌드에서는 잠재적인 문제를 조기에 발견할 수 있으며, 릴리스 빌드에서는 추가 오버헤드 없이 최적화된 코드를 실행할 수 있습니다.

 

Check() 구문은 프로그램의 상태를 검사하고, 조건이 참이 아니면 프로그램을 중단시키는 역할을 합니다. 이를 통해 개발자는 코드 실행 중에 예상치 못한 문제를 조기에 발견하고 디버깅할 수 있습니다.

 

일반적으로 언리얼 엔진 코드에서 Check() 구문은 코드의 무결성을 유지하고 예기치 않은 상황을 방지하기 위해 사용됩니다. 개발자는 자신의 코드에서 Check() 구문을 사용하여 프로그램이 예상대로 동작하는지 확인하고, 잠재적인 버그를 미리 발견할 수 있습니다.

ensure()

  • 에디터 Crash 없이 크래시 요소를 확인 할 수 있다.
  • ensureMsgf( 확인할 구문 혹은 변수 ,TEXT("에러 시 문자열")) <- 에러 시 에러 문자열 출력해준다.

 

CDO 에 대한 추가 정리

 

GPT가 말하는 CDO 의 이점

더보기

1. 일관성 있는 초기화: 엔진이 시작될 때 클래스의 CDO가 생성되면, 모든 인스턴스는 이러한 초기화된 기본값을 기반으로 시작됩니다. 이는 게임 실행 시 예기치 못한 동작을 방지하고, 일관된 게임 플레이 경험을 제공합니다.

 

2. 데이터 접근의 안정성: CDO를 사용하여 클래스의 기본값을 설정하면, 게임의 다른 부분에서 이러한 값을 사용할 때 예상치 못한 오류를 방지할 수 있습니다. 이는 데이터 무결성을 유지하고 프로그램의 안정성을 향상시킵니다.

 

3. 성능 최적화: 게임 시작 시점에서 CDO를 생성하는 것은 런타임 동안 클래스의 인스턴스를 생성하는 것보다 효율적입니다. 클래스의 인스턴스가 필요할 때마다 CDO를 복제하거나 참조하여 사용할 수 있으므로, 객체 생성과 소멸에 따른 오버헤드를 줄일 수 있습니다.

	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"));  #CDO 를 활용해서 속성의 참조를 가져옴
	if (NameProp)
	{
		NameProp->GetValue_InContainer(Teacher, &CurrentTeacherName);  # CurrentTeacherName 에 Teacher 대입
		UE_LOG(LogTemp, Log, TEXT("현재 선생님 이름 %s"), *CurrentTeacherName);

		NameProp->SetValue_InContainer(Teacher, &NewTeacherName); # Set 도 가능
		UE_LOG(LogTemp, Log, TEXT("새로운 선생님 이름 %s"), *Teacher->GetName());
	}

	UE_LOG(LogTemp, Log, TEXT("============================="));

	Student->DoLesson();
	UFunction* DoLessonFunc = Teacher->GetClass()->FindFunctionByName(TEXT("DoLesson")); # UFunction 포인터로 받아서 함수 참조 가져옴
	if (DoLessonFunc)
	{
		Teacher->ProcessEvent(DoLessonFunc, nullptr); # ProcessEvent 로 함수 호출
	}

	UE_LOG(LogTemp, Log, TEXT("============================="));