일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- effective stl
- exception
- 영화 리뷰
- UE4
- 상속
- 게임
- 루아
- 다형성
- 오블완
- lua
- 티스토리챌린지
- Effective c++
- inheritance
- virtual function
- c++
- 암시적 생성 함수
- 예외
- operator new
- 언리얼
- resource management class
- Smart Pointer
- 영화
- implicit creation function
- 참조자
- more effective c++
- 메타테이블
- implicit conversion
- 암시적 변환
- 스마트 포인터
- reference
- Today
- Total
스토리텔링 개발자
[UE4] 언리얼 프로퍼티(리플렉션) 시스템(Unreal Property System) 본문
개요
언리얼의 리플렉션에 대해 정리하겠습니다.
리플렉션이란
위키백과에서는 리플렉션을 아래와 같이 정의합니다.
런타임 시점에 사용되는 자신의 구조와 행위를 관리(type introspection)하고 수정할 수 있는 프로세스를 의미한다.
즉,
자신의 정보를 런타임에 해석하고 활용할 수 있는 기능
이라 할 수 있습니다.
예를 들어보겠습니다.
- 변수 enum을 string으로 가져오고 싶을 때.
- 클래스 인스턴스가 특정 함수를 가지고 있는지 string으로 확인하고 싶을 때.
- 클래스 인스턴스의 클래스 이름에 어떤 문자열이 포함되어 있는지 확인하고 싶을 때.
언리얼 리플렉션 시스템
리플렉션 기능은 엔진에서 제공하는 기능입니다.
엔진에서 리플렉션을 지원하는 시스템이
언리얼 프로퍼티 시스템 혹은 언리얼 리플렉션 시스템
입니다.
마크 업
리플렉션을 사용하기 위해서는 미리 정보들을 수집해야 합니다.
즉, 성능을 잡아먹습니다.
그를 감수하면서 모든 요소에 리플렉션 정보를 수집할 필요가 있을까요?
필요한 요소에서만 리플렉션을 수집하도록 하는 것이 효율적일 것입니다.
리플렉션을 사용하려면 그러겠다고 마킹 표시를 해줘야 합니다.
그 마킹을 바로 마크 업이라고 합니다.
리플렉션 데이터 수집 과정
아래는 마크 업 후 리플렉션 시스템이 정보를 수집하기 시작하는 과정입니다.
1.
리플렉션 시스템이 관리하도록 하고 싶은 타입이나 변수에 올바른 매크로 함수를 달아줍니다.
ex) UENUM(), UCLASS(), USTRUCT(), UFUNCTION(), UPROPERTY()
2.
헤더 파일 상단에 다음과 같은 include문을 추가합니다.
해당 파일에 리플렉션이 필요한 타입이 존재함을 Unreal Header Tool (UHT)에 알리기 위함입니다.
#include "FileName.generated.h"
3.
프로젝트를 컴파일 할 때 Unreal Header Tool (UHT)가 관련 정보를 수집합니다.
4.
이제 해당 타입 혹은 변수의 리플렉션 기능을 맘껏 사용하면 됩니다.
내부 구현 사항
Unreal Build Tool(UBT)와 Unreal Header Tool(UHT)가 함께 런타임 리플렉션 데이터를 생성합니다.
이 둘은 아래의 순으로 동작합니다.
1.
Unreal Build Tool(UBT)가 헤더를 스캔하여 리플렉션 마크업된 헤더들이 포함된 모듈들을 모아둡니다.
2.
이전 컴파일 이후 변경사항이 있다면 Unreal Header Tool(UHT)가 실행되며 리플렉션 데이터를 갱신합니다.
3.
Unreal Header Tool(UHT)는 헤더 파싱, 리플렉션 데이터 빌드 후 리플렉션 데이터가 들어있는 C++ 코드를 생성합니다.(.generated.h)
4.
Unreal Header Tool(UHT)는 또한 추가적인 사용성 향상을 위한 함수들(GENERATED_UCLASS_BODY(), GENERATED_USTRUCT_BODY() 매크로로 생성되는)도 생성합니다.
리플렉션 데이터가 C++ 코드로 저장되면 직렬화가 가능해지므로 동기화가 보장됩니다.
리플렉션 데이터
UHT가 수집한 리플렉션 정보들은 각각의 리플렉션 데이터 인스턴스에 저장됩니다.
리플렉션은 정보의 묶음이기 때문입니다.
아래는 대표적인 리플렉션 데이터 클래스들의 상속 구조입니다.
참고로 UStruct는 구조체의 리플렉션 데이터가 아니라 클래스, 구조체, 함수의 부모 클래스입니다.
클래스 / 구조체의 리플렉션 데이터 가져오기
아마도 가장 자주 사용해서 익숙한 클래스와 구조체의 리플렉션 데이터를 가져오는 방법을 알아보도록 하겠습니다.
클래스나 구조체의 리플렉션 데이터를 가져오는 방법은 크게 두 가지가 있습니다.
1.
타입::StaticClass() / 타입::StaticStruct() 를 호출하여 가져옵니다.
2.
타입_인스턴스->GetClass() 를 호출하여 가져옵니다.
구조체는 제공하지 않습니다.
클래스처럼 특정 부모 클래스를 반드시 상속 받아야 하는(UObjectBase) 제약이 없기 때문입니다.
즉, 부모 클래스가 없어도 되는 구조체의 특성상 상속받는 함수로 리플렉션 함수를 제공할 수 없습니다.
이 방법의 장점은 다형성으로 인해 여러 타입이 들어있는 상황에서도 그 인스턴스의 타입을 가져올 수 있다는 점입니다.
아래는 예시입니다.
TArray<UObject*> Tests;
Tests.Add(UFirstTestObject::StaticClass()->GetDefaultObject()); // UFirstTestObject
Tests.Add(USecondTestObject::StaticClass()->GetDefaultObject()); // USecondTestObject
// 같은 컨테이너에 넣은 오브젝트들이 각각 다른 UClass의 오브젝트일 수 있다.
for(auto Obj : Tests)
{
if(Obj->GetClass() == UFirstTestObject::StaticClass())
UE_LOG(LogTemp, Log, TEXT("UFirstTestObject입니다.");
else if(Obj->GetClass() == USecondTestObject::StaticClass())
UE_LOG(LogTemp, Log, TEXT("USecondTestObject입니다.");
}
리플렉션 데이터의 모든 멤버 변수 확인하기
UStruct(여기서 UStruct는 UScriptStruct와 다릅니다.)의 정보를 순회하기 위해서 TFieldIterator 반복자를 사용합니다.
아래는 UProperty(멤버 변수 리플렉션 정보)를 가져오는 예제입니다.
for(TFieldIterator<UProperty> Iter(GetClass()); Iter; ++Iter)
{
UProperty* Property = *Iter; // 각 멤버 변수에 대한 리플렉션 정보
...
}
만약 멤버 함수 리플렉션 정보를 가져오고 싶다면 아래와 같이 변형하면 간단합니다.
for(TFieldIterator<UFunction> Iter(GetClass()); Iter; ++Iter)
{
UFunction* Func = *Iter; // 각 멤버 함수에 대한 리플렉션 정보
...
}
물론 멤버 변수나 내부적인 함수를 포함할 수 없는 함수(UFunction)에는 동작하지 않습니다.
'개발 > 언리얼 엔진' 카테고리의 다른 글
[UE4] 컬링(Culling) (0) | 2021.09.17 |
---|---|
[UE4] 오브젝트 생성(Create Object) (0) | 2021.09.13 |
[UE4] 프로퍼티(UPROPERTY) (0) | 2021.08.18 |
[UE4] 스마트 포인터(Smart Pointer) (0) | 2021.08.09 |
[UE4] 동기 / 비동기 에셋 로딩 (0) | 2021.07.15 |