언리얼 리플렉션

MoOrY·2023년 3월 24일
0

언리얼 엔진

목록 보기
14/41

출처:
https://www.unrealengine.com/ko/blog/unreal-property-system-reflection
https://kyoun.tistory.com/77?category=749179

리플렉션

Reflection은 프로그램이 실행시간에 자기 자신을 조사하는 기능이다.
에디터의 디테일 패널, Serialization, 가비지 콜렉션, 네트워크 리플리케이션, BP->C++커뮤니케이션 등,
다수의 시스템에 탑재되어 있으며, 언리얼의 기반이 되는 기술이다.

퓨어 C++은 어떠한 형태의 리플렉션도 지원하지 않아 언리얼에서 자체적으로
c++클래스, 구조체, 함수, 변수, enum 관련 정보를 수집,질의,조작하는 별도의 시스템을 구축했다.

이러한 리플렉션을 프로퍼티 시스템이라 부른다.

Unreal Header Tool

언리얼 헤더 툴(Unreal Header Tool, UHT)은 UObject 시스템을 지원하는 맞춤형 파싱 및 코드 생성 툴이다.

파싱: 일련의 문자열을 분석, 분해하여 원하는 형태로 재구성하는것

리플렉션 시스템에 보이도록 하고 싶은 유형이나 프로퍼티에 주석을 달아주면
Unreal Header Tool(UHT)가 그 프로젝트를 컴파일할때 해당 정보를 수집한다.

코드 컴파일은 다음 두 단계를 거쳐 진행된다.

  • 언리얼 관련 클래스 메타데이터의 C++ 헤더를 파싱하고 여러 UObject 관련 기능을
    구현하는 맞춤형 코드를 생성하는 UHT가 호출된다.

  • 일반 C++ 컴파일러가 호출되어 결과를 컴파일합니다.

마크업

헤더에 리플렉션이 있는 유형으로 마킹을 하려면 파일 상단에 특수한 include를 추가해 줘야한다.
그러면 리플렉션이 있는 유형은 이 파일을 고려해야 하고, 시스템 구현에 필요하다는 것을 UHT에 알려준다.

#include "FileName.generated.h"

이제 UENUM(), UCLASS(), USTRUCT(), UFUNCTION(), UPROPERT()를 사용하여
헤더에 다양한 유형과 멤버 변수 주석을 달 수 있다.
이 매크로들은 해당 유형 및 멤버 선언 전에 오며, 지정자 키워드를 담을 수 있다.

UCLASS

클래스를 생성할때 클래스 상단에 UCLASS()를 사용하여 리플렉션 되었음을 나타내고,
클래스 정의 안에는 GENERATED_UCLASS_BODY()를 사용하여 짝을 이룬다
두 매크로는 리플랙션된 클래스나 구조체에 필수적인데,
클래스 본문에 추가적인 함수나 typedef를 주입하기 때문

하나의 클래스에 리플렉션된 프로퍼티와 아닌 프로퍼티(매크로 사용 안한 멤버)를 사용해도 되지만,
리플렉션 되지 않은 프로퍼티는 해당 리플렉션에 의존하는 시스템 모두에 보이지 않는다는점을 주의.
리플렉션 되지 않은 UObject포인터 그대로를 저장하면 가비지 콜렉터가 레퍼런스를 확인할수 없기때문에 위험하다.

헤더파일에 UObject를 적법하게 선언하면, UHT가 헤더파일을 분석하고,
이 과정이 완료되면 Intermediate(중간에 일어나는) 폴더에 UObject의 정보를 담은 메타파일이 생성된다.
메타정보는 UClass라는 특별한 클래스를 통해 보관되며, UObject의 다음 정보를 기록하고 있다.

메타정보가 저장하는 UObject 정보
1. 클래스 계층 구조 정보
2. 멤버 변수
3. 멤버 함수

이 정보들을 통해 리플렉션을 지원한다.

컴파일 단계에서 개별 UObject의 UClass가 생성된다. 단 인스턴스가 아니라 선언, 정의된다.
실행 초기 런타임 과정에서 UObject마다 UClass의 인스턴스와 UObject의 인스턴스가 생성된다.
이 과정에서 UObject의 인스턴스는 앞으로 생성될 UObject 인스턴스의 기본 세팅을 지정하는데 사용되며,
이를 클래스 기본 객체(CDO: Class Default Object)라고 한다.

UObject를 생성할 때마다 매번 초기화하지 않고, CDO를 만들어 놓고 복제하는 방식으로 구현되어 있다.
모든 NPC와 몬스터를 처음부터 생성하는 방법보다,
미리 큰 객체 덩어리를 복사한 뒤 속성 값만 변경하는 방식이 훨씬 효과적이기 때문이다.

UObject의 생성자 함수는 CDO를 생성하는데만 사용된다.
엔진이 초기화되는 런타임 과정에서 생성자가 호출되는것이지, 월드에 스폰될 때 생성되는것이 아니다.

엔진 초기 구동 런타임에 하나의 UObject가 초기화될때 다음 2개의 인스턴스가 함께 생성된다.

UClass 인스턴스
UObject의 CDO인스턴스

*언리얼 엔진은 모듈방식으로 구현되는데, 개별 모듈마다 속한 모든 UObject의 초기화를 진행한다.

UFUNCTION()

C++구현이 있고, 코드안에서 호출가능하며 다른 C++함수 또는 UFunction에 대한 호출이 가능하다는 점에서
C++함수와 동일하지만 UFunction은 BP안에서 보여지며 호출 또는 오버라이드가 가능하고
클래스의 디폴트 프로퍼티 내 델리게이트로 할당하려면, 반드시 UFunction이어야 한다.
BluprintCallable을 사용할때 반드시 Category를 지정해 주어야 오류가 나지않는다
https://jhtop0419.tistory.com/61

한계

UHT는 실제 C++ 파서가 아니다. 해당 언어의 상당부분을 이해하고, 할 수 있는 만큼 텍스트를 건너뛰기는 하지만,
리플렉션된 유형,함수,프로퍼티에만 주의를 기울인다.
몇몇은 여전히 헷갈릴 수 있기에 기존 헤더에 리플렉션된 유형을 추가할 땐
뭔가 단어를 바꾸거나, #if CPP / #endif짝으로 둘러싸 줘야 한다.

profile
필기용 블로그입니다.

0개의 댓글