Unreal 게임 개발/Unreal 강의 개인 정리

Unreal Composition & Enum UMETA 출력

daisy0461 2023. 12. 28. 02:05

Composition (컴포지션)

  • 객체지향 설계에서 상속이 가진 Is-A 관계만 의존해서는 설계와 유지보수가 힘들다.
  • Composition은 객체지향 설계에서 Has-A 관계를 구현하는 설계 방법이다.
  • Composition을 통해 거대한 클래스를 효과적으로 설계하는데 유용하게 사용할 수 있다.

Composition의 필요성

이전 글인 Interface의 Person과 Student, Teacher, Staff의 예제를 그대로 사용해서 설명한다.

현재 Person의 구성원을 보았을 때 모두 School Card가 필요해보여 Person내에 Card를 구현했다.

하지만 이후 Person에서 Card가 필요하지 않은 객체도 Person을 상속받게 되면 Card 또한 받는다.

이렇게 Is-A관계에서 Has-A 관계로 바꿔줘 객체지향에 어울리게 하는 방법이 Composition이다.

 

Unreal에서 Composition 구현 방법

  • 하나의 Unreal Object는 항상 CDO(Create Default Object)를 가지고 있다.
  • Unreal Object에 다른 Unreal Object를 조합할 때 2가지 선택지가 존재한다. 
  1. CDO에 미리 언리얼 오브젝트를 생성해 조합. (언제나 필수적으로 포함)
    생성자에서부터 추가가 되며 cpp에서 CreateDefaultSubobject()로 만들어준다.
  2. CDO에 빈 포인터만 넣고 런타임에 언리얼 오브젝트를 생성해 조합. (필요할 때 선택적 포함)
    게임 런타임동안 작동하는 코드에 NewObject()를 통해 생성한다.
  3. CreateDefaultSubobject()와 NewObject()를 통해 내가 소유한 Object를 Subobject라고 하며
    나를 소유한 Unreal Object는 Outer라고 한다.

위 과정은 결국 원래의 Object에 다른 Object를 추가하는 것이다.

헤더 파일에서 오브젝트를 받을 때 전방선언을 통해 헤더파일을 다 포함하지 않고 사용하며

보통 오브젝트는 *로 받아온다.

 

UE4까지는 다음과 같이 헤더파일에서 선언했었지만

UPROPERTY()
class UCard* Card;

UE5부터는 아래와 같은 방식으로 선언하라고 되어있다.

UPROEPRTY()
TObjectPtr<class UCard> Card;

TObjectPtr로 감싸서 사용하라고 되어있다. 아래 링크에서 C++ Object Pointer Properties를 보면 나와있다.

헤더파일에서만 다음과 같이 사용하고 cpp에서는 그냥 *만 사용해도 무관하다.

https://docs.unrealengine.com/5.0/en-US/unreal-engine-5-migration-guide/

 

Unreal Engine 5 Migration Guide

Steps and requirements for migrating your Unreal Engine 4 projects to Unreal Engine 5.

docs.unrealengine.com

 

enum UMETA 출력 방법

다음과 같이 UCard에 enum type이 정의가 되어있다.

UENUM()
enum class ECardType : uint8
{
	Student = 1 UMETA(DisplayName = "For Student"),
	Teacher UMETA(DisplayName = "For Teacher"),
	Staff UMETA(DisplayName = "For Staff"),
	Invaild
 }

Student = 1 을 통해서 자동으로 Teacher에는 2, Staff에는 3이라는 값이 들어간다.

 

cpp에서 DisplayName을 출력하기 위해서 다음과 같은 과정이 필요하다.

//TEXT에 있는 절대 주소값을 이용해서 원하는 Type정보를 들고온다.
//C++에서 생성된 객체들은 /Script 라는 절대 주소를 가지고
//그 다음은 프로젝트 이름(모듈 이름)을 적어준다.
const UEnum* CardEnumType = FindObject<UEnum>(nullptr, TEXT("/Script/ProjectName.ECardType"));

if(CardEnumType){
	//GetDisplayNameTextByValue는 int64값만 받기 때문에 int64로 Cast해준다.
	FString CardMetaData =CardEnumType -> GetDisplayNameTextByValue((int64)CardType).ToString();
	UE_LOG(LogTemp, Log, TEXT("%s"), *CardMetaData);
}