Game !

 


언리얼 오브젝트

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

언리얼 오브젝트가 아닌, 입력에 따라 결과 값만 받고싶은 C++ 클래스도 언리얼 프로젝트에서 전혀 문제없이 쓸 수 있다.
언리얼 엔진에서는 이러한 오브젝트를 구분하기 위해 클래스에 접두어를 사용한다.
- 언리얼 오브젝트 (언리얼 엔진의 관리를 받는 객체) : U로 시작.
ex) UObject, UMeshComponent...
- 일반 C++ 오브젝트 ( C++ 네이티브 객체) : F로 시작.
ex) FName, FVector...

언리얼 오브젝트를 생성하기 위한 최상단 기본 클래스는 UObject 이다.


언리얼 오브젝트는 언리얼 엔진이 자체적으로 만들어 제공하는 프레임웍이기 때문에
언리얼 헤더 툴 ( UHT ) 이라는 프로그램의 도움을 받아야 한다.
헤더파일에서 *클래스 선언을 언리얼 오브젝트 규격에 맞게 선언해주면 언리얼 헤더 툴 ( UHT ) 이 이를 먼저 파싱하고 분석한다.
규격에 맞지 않으면 에러를 출력하고, 규격이 맞으면 UHT 이 부가적인 메타 정보를 담은 소스코드를
프로젝트 내의 Intermediate 폴더에 메타 파일을 생성한다.
이 작업이 모두 끝나면 컴파일이 진행된다.

* 클래스 선언을 언리얼 오브젝트 규격에 맞게 선언 ▶ UCLASS( ) 매크로를 사용해서 선언해준다.
참고로 멤버 변수는 UPROPERTY( ), 멤버 함수는 UFUNCTION( ) 매크로를 지정해주면 앞으로 언리얼 엔진의 관리를 받게된다. (언리얼 오브젝트 기능을 사용할 수 있게된다)


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

 

 


UCLASS( ) 매크로 ( 헤더파일 class 위에 선언되어 있는 것 ) ▶ 이것은 매크로 함수일 뿐...

클래스 선언 시, 언리얼 오브젝트로 사용하고 싶을 때 지정해주는 매크로

UCLASS( ) 와 *GENERATED_BODY( ) 는 짝을 이루어야 한다.

* GENERATED_BODY( ) : 
리플렉션된 클래스나 구조체에 필수적. UHT 에 의해 클래스 본문에 추가적인 함수나 typedef 를 주입해준다.


UCLASS( ) 매크로는 UObject 에게 자신이 언리얼에서 기반으로 삼은 유형에 대해 설명해주는 UClass 타입을 레퍼런스로 넘겨준다. ( UHT 에 의해 )

 

 


UClass ( 클래스 타입 )

언리얼 오브젝트에 대한 클래스 계층 구조 정보 + 멤버 변수/멤버 함수 를 모두 기록한다.
CDO 를 유지한다.

UClass 타입은 StaticClass( ) 또는 GetClass( ) 함수로 가져올 수 있다. (=접근이 가능하다)
- 컴파일 단계 : StaticClass( ) ▶ UClass 타입의 정보를 얻어올 때 사용
- 런타임 단계 : GetClass( ) ▶ 실제 객체의 클래스를 조회할 때 사용
> 이 함수들은 우리가 선언한 적이 없지만 GENERATED_BODY( ) 에 의해 UHT 이 추가적인 함수를 생성해줬기 때문에 호출이 가능하다.

cf.
StaticClass 함수는 컴파일 단계에 이미 정해진 것이다.
하지만 포인터가 가리키는 실제 객체 타입은 런타임에서 달라질 수 있으므로
실제 객체의 클래스를 리턴하는 GetClass 함수의 리턴 값은 다를 수 있다.

 

 


CDO ( Class Default Object )

*실행 초기의 런타임 과정에서는 언리얼 오브젝트마다 클래스 정보와 함께 언리얼 오브젝트의 인스턴스가 생성된다. 이 특별한 인스턴스는 언리얼 오브젝트의 기본 세팅을 지정하는데에 사용된다.
이 특별한 인스턴스를 CDO 라고 한다.

*실행 초기의 런타임 과정 = 생성자
언리얼 오브젝트 클래스의 생성자는 인스턴스를 초기화해서 CDO 를 만들기 위한 목적으로 사용된다.
이 생성자 코드는 초기화에서만 실행되고 실제 게임 플레이에서 생성자 코드는 사용할 일이 없다.
언리얼 엔진에서 게임 플레이에서 사용할 초기화 함수는 Init 또는 BeginPlay 함수를 제공한다.
( → 아.. 그래서 생성자에 코드 짜고 빌드하면.. 클래스 블루프린트 열었을 때 그 내용들이 적용되어있는거구나... )

언리얼 클래스 생성자 심화 : 생성자에 매개변수를 추가할 수 있는데 
UTESTObject::UTESTObject(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) 의 경우
FObjectInitializer 매개변수는 const 마킹이 되어있음에도 불구하고 내장된 mutable 함수를 통해서 프로퍼티와 서브오브젝트를 덮어쓰도록 설정할 수 있다. 
그로 인해서 생성되는(덮어씌워진) UObject는 이러한 매개변수로 들어온 변경사항에 영향을 받을 것이며, 이를 사용해서 등록된 프로퍼티나 컴포넌트의 값을 변경할 수 있다.


CDO 를 만드는 이유
언리얼 오브젝트를 생성할 때마다 매번 초기화하지 않고, 기본 인스턴스( CDO ) 를 복사해서 프로퍼티만 변경하는 방식의 매커니즘으로 구성되어 있다.
( 수많은 오브젝트들이 로드가 될 경우 캐릭터를 하나씩 처음부터 생성하고 초기화시키는 것보다, 미리 큰 기본 객체 덩어리를 복제한 후 속성값만 바꾸고 로드하는 것이 훨씬 빠르기 때문이다 )
▶ CreateNewObject( ) 함수나 SpawnActor( ) 같은 함수로 생성할 경우 생성된 오브젝트에 CDO 가 복사될 것이다.


언리얼 에디터도 일종의 언리얼 어플리케이션이다보니, 사용하는 모듈이 초기화되거나 갱신된 코드로 컴파일이 진행되면 CDO 가 생성된다.
뿐만 아니라, 에디터의 플레이 버튼을 누르면 에디터와 독립된 게임을 시뮬레이션 해야하기 때문에 또 CDO 가 생성된다.

 

 


언리얼 오브젝트 로딩 과정

1. 모듈 로딩 ( 작은 퍼센트 창 뜨는 거 )
2. 모듈( 프로젝트 )에 있는 모든 UObject의 클래스 정보 등록
3. 각 클래스 정보의 CDO 인스턴스 생성
4. 언리얼 오브젝트(클래스)의 생성자를 호출해 CDO 정보를 완성

▶ 이 모든 과정이 끝나면 초기화 수치가 100% 이 된다. 이 때 비로소 에디터가 뜨게 된다.