언리얼오브젝트


언리얼 엔진의 관리를 받는 특수한 객체

 

이번 시간에는 언리얼 엔진에서 모든 오브젝트의 기본을 이루는 언리얼 오브젝트에 대해 살펴보겠습니다. 

지난 강좌에서 WebService 모듈에 있는 WebConnection 객체를 생성할 때,
부모를 Object로 지정했던 것을 기억하실 겁니다. 

이는 일반 오브젝트가 아닌 언리얼 오브젝트를 의미하는데,
비주얼 스튜디오에 가서 이 언리얼 오브젝트를 상속받은 WebConnection의 클래스 선언을 살펴보겠습니다.

우리는 지정하지 않았는데, WebConnection과 Object클래스 이름의 앞에 U자가 붙어 있음을 확인할 수 있습니다.
이는 클래스가 언리얼 오브젝트로 선언되었다는 것을 의미합니다.

WebConnection 클래스 선언

언리얼 오브젝트는 언리얼 엔진의 관리를 받는 특수한 객체입니다. 
그렇다고 해서 언리얼 프로젝트에 모든 클래스가 언리얼 오브젝트가 되어야 한다는 것은 아닙니다.

일반적으로 입력에 따라 결과 값만 받고 싶은 C++ 클래스도 언리얼 프로젝트에서 전혀 문제 없이 사용할 수 있습니다. 언리얼 엔진에서는 이 둘을 구별하기 위해서 클래스 이름에 붙은 접두사를 사용하는데,

언리얼 오브젝트는 반드시 U로 시작하고
일반 C++ 클래스는 F로 시작하는 것을 권장하고 있습니다. 

언리얼 프로젝트의 접두사 규칙 

 

언리얼 오브젝트는 언리얼 엔진 내에서 객체를 쉽고, 효율적으로 관리하기 위해서 고안되었습니다.
C++을 깊숙히 몰라도 언리얼 오브젝트의 성격을 알면 마치 Java나 C#과 같은 언어처럼 사용할 수 있게 되는데요,

 

C++ 객체가 언리얼 오브젝트가 되면 자동으로 향상되는 기능은 다음과 같습니다. 

1. CDO(Class Default Object) : 객체의 초기 값을 자체적으로 관리합니다.
2. Reflection : 객체 정보를 런타임에서 실시간 조회가 가능합니다.
3. GC(Garbage Collection) : 참조되지 않는 객체를 메모리에서 자동 해제할 수 있습니다.
4. Serialization : 객체와 속성 정보를 통으로 안전하게 보관하고 로딩합니다.
5. Delegate : 함수를 묶어서 효과적으로 관리하고 호출할 수 있습니다. 
6. Replication : 네트워크 상에서 객체간에 동기화를 시킬 수 있습니다.
7. Editor Integration : 언리얼 에디터 인터페이스를 통해 값을 편집할 수 있습니다. 



언리얼 오브젝트 구성 및 생성

언리얼 오브젝트는 C++표준이 아니고, 언리얼 엔진이 자체적으로 만들어 제공하는 프레임웍이기 때문에,
일반적인 방법으로는 만들 수 없고, 언리얼 헤더 툴(Unreal Header Tool)이라는 프로그램의 도움을 받아야 합니다.

여러분들이 헤더 파일에서 클래스 선언을 언리얼 오브젝트 규격에 맞게 선언해주면,
바로 컴파일하는 것이 아닌, 언리얼 헤더 툴이 이를 파싱하고 분석합니다. 

헤더 파일의 선언이 규격에 맞지 않으면 언리얼 헤더 툴에 의해 에러가 발생되고,규격에 맞게 선언하였다면,
언리얼 헤더 툴부가적인 메타 정보를 담은 소스코드를 프로젝트의 Intermediate 폴더에 생성해 줍니다.

이 작업이 모두 끝나면 이제 본격적으로 컴파일을 진행하게 됩니다. 아래는 이러한 구성을 담은 그림입니다. 

 

언리얼 오브젝트의 구성

 

Ctrl+Alt+F7 키를 눌러 프로젝트를 다시 빌드해봅시다. 
다시 출력 로그를 자세히 살펴보면 빌드 과정의 초기에 언리얼 빌드 툴이 동작한다는 로그가 보이게 됩니다. 

 

언리얼 헤더 툴의 로그

 


언리얼 오브젝트 선언규칙

UObject 클래스는 언리얼 오브젝트 최상단에 위치한 기본 클래스



이렇다면 이렇게 언리얼 헤더 툴이 인식할 수 있는 언리얼 오브젝트의 선언 규칙은 다음과 같습니다.
WebConnection 선언과 한번 비교해보시기 바랍니다.  

 

언리얼 오브젝트의 선언 규칙 

 

1. 클래스이름.generated.h를 반드시 가장 마지에 include 시켜주어야 합니다.

2. 클래스 선언 전에 UCLASS매크로를 사용해야 합니다.

3. 언리얼 오브젝트의 접두사는 U, A 그리고 S가 있습니다.
- 액터 기반이라면 A를 
- 액터 기반이 아니라면 모두 U 
- UI를 담당하는 슬레이트만 S를 사용하면 됩니다.

4. UObject 클래스는 언리얼 오브젝트 최상단에 위치한 기본 클래스입니다.
이로 상속받은 클래스는 모두 언리얼 오브젝트가 됩니다. 

5. 클래스 선언 내부에 GENERATED_BODY() 매크로도 선언해줍니다. 

여기까지는 언리얼 오브젝트를 생성할 때 필수적으로 선언해야 할 규칙이고,
아래와 같이 추가적으로 매크로를 지정해 언리얼 오브젝트를 확장시켜 나갈 수 있습니다



추가 매크로

 

언리얼 오브젝트에 사용하는 추가 매크로

 

1. 모듈이름_API 매크로는 상황에 따라 부가 설정을 해주는 매크로입니다. 
예를 들어 
윈도우 에디터에서 사용할 수 있게 dll로 빌드할 때에는 이 매크로는 MS의 DLL문법인 __declspec(dllexport)구문으로 자동 치환됩니다.
  이렇게 선언해야 다른 모듈에서 현재 모듈 내 클래스에 접근할 수 있게 됩니다.
게임 빌드시에는 굳이 외부에 공개할 필요 없이 모든 모듈이 하나의 exe에 통합되므로 이 때는 
빈 문자열로 치환됩니다.

2. 언리얼 오브젝트의 생성자는 오브젝트의 기본 값을 지정하는데 사용됩니다.
차후에 설명할 클래스 기본 객체인 CDO를 생성할 때 이 생성자 코드가 한번 실행됩니다. 

3. 멤버 변수 위에 UPROPERTY 매크로를 얹어주면 이 변수는 앞으로 언리얼 엔진의 관리를 받게 됩니다.
향후 언리얼 오브젝트가 소멸되더라도 언리얼 엔진의 관리를 받아 해당 멤버 변수의 메모리도 같이 자동으로 소멸되며, 메모리 사용량도 체크할 수 있습니다.

4. 멤버 함수 위에 UFUNCTION 매크로를 얹어주면 블루프린트와 연동되게 할 수 있으며,
딜리게이트나 리플리케이션과 같은 함수를 사용할 수 있어서 멤버 함수의 활용폭이 늘어납니다. 

 


게임인스턴스

 

시작하면 엔진을 초기화하고 가장 먼저 실행하는 오브젝트
그리고 게임이 종료될 때 까지 GameInstance는 살아있고 프로그램이 종료될 때 가장 마지막에 소멸 


이제 처음 시작에 제작한 ArenaBattle 모듈에 있는 ABGameInstance 클래스에 대해 살펴봅시다.
ABGameInstance의 클래스 이름 앞에 ‘U’접두사가 붙어 있으니,
이제 우리는 액터가 아닌 언리얼 오브젝트라는 것을 쉽게 파악할 수 있습니다. 

 

ABGameInstance 클래스의 선언 

 

ABGameInstance는 GameInstance 오브젝트를 상속받고 있습니다.
언리얼 엔진에서 GameInstance 오브젝트는 어플리케이션(혹은 앱)을 관리하는데 사용됩니다.

사용자가 게임을 시작하면 엔진을 초기화하고 가장 먼저 실행하는 오브젝트가 GameInstance입니다.
그리고 게임이 종료될 때 까지 GameInstance는 살아있고 프로그램이 종료될 때 가장 마지막에 소멸됩니다. 

이러한 특징으로 인해 GameInstance의 멤버를 확장해나가면,
게임의 전체 라이프싸이클(LifeCycle)에서 사용되는 데이터를 관리할 수 있습니다.

GameInstance 오브젝트가 초기화될 때,
Init이라는 함수를 호출하는데, 이를 상속받으면 우리가 어플리케이션의 초기화 루틴을 만들 수 있게 됩니다. 

이제 에디터를 열고 툴바에 있는 [세팅 -> 프로젝트 세팅] 메뉴를 눌러,
프로젝트 설정 다이얼로그를 연 후에 에디터와 게임의 초기화 설정을 주로 지정하는데 사용되는 맵 & 모드로 갑니다. 

여기서 하단의 Game Instance Class 항목의 값을 우리가 제작한 ABGameInstance로 변경하면 게임이 시작될 때 ABGameInstance의 인스턴스가 자동 생성되고 이 인스턴스로 어플리케이션이 관리됩니다. 

 게임 인스턴스 클래스의 설정

ABGameInstance에서 부모의 Init 함수를 오버라이드(Override)하여 짤막하게 로그를 남기도록 구현해봅시다.
언리얼에서 로그를 남기기 위해서는 언리얼 엔진에서 제공하는 UE_LOG라는 매크로를 사용하면 편리합니다.  
UE_LOG 매크로는 아래 세 가지 인자로 구성되어 있습니다.

1. Log Category : 로그를 구분할 수 있는 카테고리를 지정합니다.
언리얼 엔진에서는 LogClass , LogTemp와 같은 기본 카테고리를 제공하고 있으며, 모듈마다 로그를 남길 수 있도록 모듈 별로 대부분 카테고리가 지정되어 있습니다. 물론 우리가 원하는 카테고리를 직접 선언할 수 있습니다.

2. Verbosity : Log, Warning, Error로 나뉘며, 각각 흰색, 노란색, 빨간색으로 표시됩니다.
Error타입 로그의 경우 프로그램을 멈출 수 있게 설정하는 것도 가능합니다.

3. Format String : C에서 제공하는 Printf 문과 유사하게 사용합니다.
뒤에 Variable Argument를 추가할 수 있으며, 언리얼이 제공하는 2바이트 문자를 지원하는 TEXT 매크로를 사용해 문자열로 로그를 지정하거나 포맷을 정의합니다.

아래는 ABGameInstance 언리얼 오브젝트에 로그를 찍는 기능을 추가한 코드입니다.

//h
#include "CoreMinimal.h"
#include "Engine/GameInstance.h"
#include "ABCGameInstance.generated.h"

UCLASS()
class ABC_API UABCGameInstance : public UGameInstance
{
	GENERATED_BODY()

public:
	virtual void Init() override;
	
};

//cpp
#include "ABCGameInstance.h"
void UABCGameInstance::Init()
{
	Super::Init();
	UE_LOG(LogClass, Warning, TEXT("%s"), TEXT("Game Instance Init!"));
}

 

이제 메뉴의 [창 > 개발자 툴 > 출력 로그]를 선택해 로그를 확인할 수 있는 창을 열어봅시다. 

 

[그림] 출력 로그 메뉴

 

이제 플레이 버튼을 누르면 로그 창에서 Game Instance Init! 이라는 노란 로그를 확인할 수 있게 됩니다.

 

로그의 확인

 

 

 

 

 


[참고] [1-4] 언리얼 오브젝트

+ Recent posts