정의
리플렉션은 프로그램이 실행시간에 자기 자신을 조사하는 기능이다.
C++는 어떠한 형태의 리플렉션도 지원하지 않기 때문에 기본적으로 컴파일 타임에만 타입 정보가 존재한다. 이때, 언리얼의 리플렉션을 이용하면 컴파일 타임에만 존재하던 타입 정보를 런타임에 들여다볼 수 있게 해준다.
부가설명
언리얼은 자체적으로 C++클래스, 구조체, 함수, 멤버변수, 열거형의 관련된 정보들을 수집, 질의, 조작하는 별도의 리플렉션 시스템이 구축되어 있다. 전형적으로 이러한 리플렉션을 "프로퍼티 시스템"이라고 부른다.
(리플렉션은 그래픽 용어이기도 해서 헷갈릴 수 있다)
리플렉션 시스템에 보이도록 했으면 하는 유형이나 Property에 주석을 달아주면 UHT가 해당 프로젝트를 컴파일할 때 해당 정보를 수집하게 된다.
게임엔진에 리플렉션이 필요한 이유
게임 엔진에는 여러가지 시스템이나 프레임워크가 있는데, 그밖에 동적으로 링크되는 사용자 정의 클래스같은 객체들도 게임 엔진 내의 시스템이 관리해야 한다.
그러나 컴파일 타임에는 해당 클래스들을 모르기 때문에, 동적인 타입 정보가 필요하다.
따라서 이러한 동적인 타입 정보를 알려주는것이 바로 리플렉션이다.
C#과 C++ 리플렉션의 비교
C#과 달리 C++은 언어 차원에서 런타임 리플렉션을 지원하지 않는다.
정적 다형성을 위한 도구(각종 기법, 라이브러리)는 많이 있지만 런타임 리플렉션과는 성격이 다르고, 런타임 타입 정보를 나타내는 RTTI도 리플렉션의 기능으로써 요구하는 수준에 미치지 못한다.
RTTR 이라는 오픈소스 라이브러리를 통해 C++에서 리플렉션 라이브러리를 직접 구현하여 사용할 수 있긴 하다.
언리얼 리플렉션 클래스
언리얼 엔진은 UCLASS(), UFUNCTION(), UPROPERTY()와 같은 매크로를 붙여 리플렉션 되는 클래스/멤버 함수/멤버 변수임을 의미한다.
이때, UCLASS 매크로를 붙이려면 UObject의 자식클래스여야 한다. Native C++ 같은 클래스는 매크로를 붙일 필요도 없고 그에따라 리플렉션 정보도 생기지 않게 된다.
USTRUCT() 와 UENUM()을 이용해 구조체와 열거형의 정의도 가능하다.
GENERATED_BODY() 매크로는 자동 생성된 코드 중에서, 클래스 Body 내에 들어가야 할 내용을 여기 삽입해달라 라는 의미의 마커로 사용된다.
리플렉션 유형으로 마킹하기 : 마크업
#include "FileName.generated.h"
헤더에 리플렉션이 있는 유형으로 마킹하려면, 파일 상단에 위와 같은 include를 추가해주어야 한다. 그러면 리플렉션이 있는 유형은 이 파일을 고려해야 하고, 시스템 구현에도 필요하다는 것을 UHT에 알려준다.
언리얼 C++의 클래스/ 구조체 비교
C++에서는 클래스나 구조체의 명확한 차이점이 없지만, 언리얼 엔진의 C++에서는 두 경우를 명확히 구분한다.
언리얼 엔진 빌드 과정
UCLASS나 USTRUCT같은 매크로들은 Big 매크로인데, 이는 언리얼 헤더 툴에게 알려주기 위한 용도이다.
언리얼엔진의 빌드 과정을 살펴보면, visual studio에서 빌드 버튼을 눌렀을 때 바로 C++ 컴파일러가 실행되는 것이 아닌, UHT(언리얼 헤더 툴)이 먼저 실행되는 것을 볼 수 있다. 이때 UHT는 간이 C++ 구문 분석기(파서)라고 한다.
UHT는 사용자가 앞에 작성한 소스코드를 전부 훑으면서 UCLASS나 USTRUCT같은 매크로들을 전부 찾아 리플렉션 정보를 먼저 수집한다.
분석된 정보를 기반으로 프로그램이 실행되었을 때 런타임 리플렉션 객체를 생성하기 위한 코드를 보이지 않는 곳에 자동으로 생성하게 된다.
언리얼 엔진은 사용자가 작성한 코드(버전 관리 되는 코드)는 '소스폴더' 내에 작성하도록 되어있고, UHT는 'Intermediate 폴더' 내 깊숙한 곳에 저장된다.
UHT의 역할이 끝나면 사용자가 작성한 코드와, 자동 생성된 코드가 실제 C++ 컴파일러의 입력으로 들어가서 온전한 클래스가 만들어지게 된다.
언리얼 리플렉션 객체의 종류/구조
리플렉션 객체들 스스로도 UObject의 자손 클래스이다.
클래스나 구조체 함수처럼 멤버를 갖는 것들은 USTRUCT로 분류하고,
멤버 변수나 함수 파라미터를 의미하는 UPROPERTY는 여러가지 Type으로 세분화된다는 것을 눈여겨 보면 된다.
UProperty가 세분화 되어있는 이유
C#과 달리 언리얼 C++은 UProperty 객체가 세분화 되어있다.
언리얼 엔진에는 여러가지 시스템이 존재하는데, 이 시스템들이 Field를 다루는 방식에 있어서 Field의 Type에 따라 달라져야 하는 경우가 존재하기 때문이다.
객체 A가 존재할 때, A가 Int형인지, String형인지에 따라 비교하는 방식이 달라져야 하는데, 그런 부분을 UProperty 객체로 세분화되어 정의되어 있는 것이다.
이러한 리플렉션 기능은 언리얼 엔진 내의 여러가지 시스템들이 이러한 리플렉션 객체들에 의존하고 있다.
그 중 가비지 컬렉션은 리플렉션에 의해 구현되었다 해도 될 정도로 많이 의존하고 있다.
(가비지 컬렉션에 대한 설명은 다른 게시글에 정리)
참조 영상
https://www.youtube.com/watch?v=VpEe9DbcZIs&t=304s
https://www.unrealengine.com/ko/blog/unreal-property-system-reflection
언리얼 프로퍼티 시스템 (리플렉션)
리플렉션(Reflection)은 프로그램이 실행시간에 자기 자신을 조사하는 기능입니다. 이는 엄청나게 유용한 데다 언리얼 엔진 테크놀로지의 근간을 이루는 것으로, 에디터의 디테일 패널, 시리얼라
www.unrealengine.com
'👩🏻💻기초지식 > Unreal' 카테고리의 다른 글
[Unreal] FName, FText, FString (0) | 2023.11.07 |
---|---|
[Unreal] 리플리케이션 (Replication) : Actor (0) | 2023.10.19 |
[Unreal] 언리얼 서버 모델 (0) | 2023.08.30 |
[Unreal] 가비지 컬렉션 (Garbage Collection) (0) | 2023.08.16 |
[Unreal] 서비스 노드란? (BTService) (0) | 2023.08.09 |