.generated.h,GENERATED_BODY()가 뭔데

Lee Raccoon·2024년 5월 12일
1

언리얼 공부

목록 보기
3/11

언리얼 C++을 시작하고 항상 보는 친구들이다.

마치 C언어를 처음 접했을 때 #include<stdio.h> 같은 존재가 아닌가 싶다.
일단 다들 붙여서 붙이는데 이걸 왜 붙이는지 모르겠는..

이런 친구들에 대해 알아보도록 하자.
우선 아무 오브젝트나 만들면 이런 형식이 된다.

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "UObject/NoExportTypes.h"
#include "MyObject.generated.h"

/**
 * 
 */
UCLASS()
class HELLOUNREAL_API UMyObject : public UObject
{
	GENERATED_BODY()
	
};

자 하나하나 알아보도록 하자

include "CoreMinimal.h"

CoreMinimal.h는 엔진 설치 파일에 존재하는데, 들어가서 파일을 열어보면 수많은 헤더를 만날 수 있다.

거의 170줄에 달하는 헤더가 있는데 자세히 보면 Vector, Float 같은 많이 보던 애들도 포함되어있다.

CoreMinimal이라는 이름답게, 엔진 내의 Core에 있는 수많은 헤더파일 중에 정말 최소로 필요한 것들만 추려서 CoreMinimal로 만들어놓고 오브젝트를 만들면 알아서 추가해주는 것이다.

코어 폴더에 파일이 최소 몇천개는 돼보이는데 100대로 줄였으니 컴파일 타임에서 훨씬 이득을 보려고 한 것이겠지?

include "UObject/NoExportTypes.h"

얘는 생성한 클래스 타입에 따라 다르겠지만 지금은 그냥 깡통 UObject만 생성해서 이 헤더가 포함되었다. UObject로써 필수적인 기능이 포함된 헤더이다.
파일 들어가서 열어보니 헤더 코드 줄만 3200줄.. 어질어질하다.

둘러보니 FVector가 있었다. FVectorX, Y, Z는 그냥 그저 Float값인줄 알았는데.. FLargeWorldCoordinatesReal라는 정체를 알 수 없는 타입의 값이었다.
알아보니 게임의 월드가 매우 커진 상황에서 사용하기 위하여 만들어진 타입이며 게임 월드의 좌표 값이라는 것이 정확도 또한 매우 중요해서 기존 float의 부동 소수점 정밀도 문제를 해결하고자 만들었다고 한다. 타입 하나도 최적화나 정확도 때문에 만들어서 사용되는구나 싶은 느낌이랄까.. 신기,..

GENERATED_BODY()

순서상 #include "MyObject.generated.h"보다 이걸 먼저 봐야할 것 같다.
이 매크로가 어떻게 정의되어 있나 보면
#define GENERATED_BODY(...) BODY_MACRO_COMBINE(CURRENT_FILE_ID,_,__LINE__,_GENERATED_BODY);
이렇게 되어 있는데 BODY_MACRO_COMBINE은 그냥 인자들을 _ 붙여서 합쳐주는 거다.
지금 보면 FID_UnrealProjects_Practice_HelloUnreal_Source_HelloUnreal_MyClass_h_15_GENERATED_BODY
이렇게 된다는 뜻인데 그럼 이게 뭐고 어디에 쓰일까? 그게 바로 .generated.h에 있다.

#include "MyObject.generated.h"

자 처음에 아무것도 모르고 이 밑에다가 헤더 추가했다가 대체 왜 컴파일이 안되는지 벙찌게 만들었던 그 헤더다.
탐색기에서 검색해보고 경로보고 충격을 먹었다.
프로젝트 이름\Intermediate\Build\Win64\UnrealEditor\Inc\HelloUnreal\UHT\MyClass.generated.h
어? 겁나 꽁꽁 숨겨져 있는 기분이었는데 그렇게 길진 않네..
열어보면 생각보다 길지는 않은 코드가 있다.

여기에 위에서 봤던
FID_UnrealProjects_Practice_HelloUnreal_Source_HelloUnreal_MyClass_h_15_GENERATED_BODY가 매크로로 만들어져 있는 걸 볼 수 있다!

#define FID_UnrealProjects_Practice_HelloUnreal_Source_HelloUnreal_MyObject_h_15_GENERATED_BODY \
PRAGMA_DISABLE_DEPRECATION_WARNINGS \
public: \
	FID_UnrealProjects_Practice_HelloUnreal_Source_HelloUnreal_MyObject_h_15_SPARSE_DATA \
	FID_UnrealProjects_Practice_HelloUnreal_Source_HelloUnreal_MyObject_h_15_SPARSE_DATA_PROPERTY_ACCESSORS \
	FID_UnrealProjects_Practice_HelloUnreal_Source_HelloUnreal_MyObject_h_15_EDITOR_ONLY_SPARSE_DATA_PROPERTY_ACCESSORS \
	FID_UnrealProjects_Practice_HelloUnreal_Source_HelloUnreal_MyObject_h_15_RPC_WRAPPERS_NO_PURE_DECLS \
	FID_UnrealProjects_Practice_HelloUnreal_Source_HelloUnreal_MyObject_h_15_ACCESSORS \
	FID_UnrealProjects_Practice_HelloUnreal_Source_HelloUnreal_MyObject_h_15_INCLASS_NO_PURE_DECLS \
	FID_UnrealProjects_Practice_HelloUnreal_Source_HelloUnreal_MyObject_h_15_ENHANCED_CONSTRUCTORS \
private: \
PRAGMA_ENABLE_DEPRECATION_WARNINGS

그니까 이 많은 것들이 저 GENENATED_BOBY() 에 들어간다는 것.
내부를 살펴보니 시리얼라이저 이런 것도 있는 걸 보니 오브젝트가 갖는 특성에서 본 기억이 있다. 아무래도 언리얼이 제공해주는 그런 기능들의 코드를 내가 만든 오브젝트의 정보를 바탕으로 자동으로 추가해주는 것이다.?
이런 자동 생성은 이 코드 경로처럼 UHT(Unreal Header Tool)라는 친구가 프로젝트를 빌드할 때 알아서 해주는 것이다.

실제로 빌드할 때 UHT가 실행되는 것을 확인할 수 있다.

코드 작업할 때 헤더를 추가한다던가 하는 작업으로 GENERATED_BODY()의 라인 넘버가 바뀌면 코드가 빨간 줄 파티로 바뀌고 빌드하면 괜찮아지고는 했는데 지금 생각해보니 라인 넘버가 달라져서 바뀐 이름이 MyObject.generated.h안에 들어있는 매크로 이름과 달라서 찾을 수 없기 때문에 난리가 났던 거 였구나!

UCLASS()

크게 알아볼건 없는 것 같은데 매우 중요한 매크로다.
클래스를 Unreal의 리플렉션 시스템에 등록한다. 언리얼 오브젝트로써 등록을 한다는 것이다.
이걸 안달면 그냥 언리얼이 알지도 못한다는 거 아닌가?
물론 진짜 C++만 사용해서 게임 내의 콘텐츠가 아닌 저수준에서 무언가를 처리하고 싶다면 필요없겠지만..

HELLOUNREAL_API

이것은 내 프로젝트 이름_API 인데, 이것은 주로 라이브러리 또는 모듈의 공개 설정을 지정하는 역할을 한다.
보통 언리얼 엔진은 수많은 모듈로 구성되어 있고 각 모듈은 특정 기능을 담당하게 된다. 이 때 모듈 간에 함수나 클래스를 공유하기 위해서는 해당 요소가 외부에 공개되어야 한다.

한마디로 이게 없으면 비공개로 설정이 돼서 외부 모듈을 불러올 수가 없는데, 그럼 엔진이 제공하는 Core 같은 필수적인 애들을 사용할 수가 없는 것 같다.
반드시 있어야겠지..? 디폴트로 붙여서 생성시켜주는 덴 이유가 있다.

profile
영차 영차

0개의 댓글