티스토리 뷰
언리얼 엔진에서 사용되는 함수들 중에서 Outer를 인자로 요구하는 함수들이 있습니다: NewObject, CreatePackage, SavePackage... 대부분의 경우, 해당 함수를 호출하는 클래스(this)를 인자로 집어넣으면 상관 없다고 하지만, 이유도 모르고 무작정 this를 설정했다가는 나중에 생각지도 못한 난관에 봉착할 수 있습니다.
Outer가 무엇일까요? 저같은 경우, 이 괴상망측한 용어를 NewObject를 처음 사용하게 되었을 때 만났습니다. 처음에는 단어 의미도 몰라서 검색했던 기억이 납니다. Outer에 대한 공식 문서의 언급은 UObject 인스턴스 생성에서 처음볼 수 있습니다. 해당 문서에서는 Outer를 다음과 같이 설명하고 있습니다.
Outer : 생성중인 Object 에 대한 Outer 로 설정할 UObject 입니다.
Outer의 설명을 알기 위해서 Outer가 뭔지 알아야 하는 설명을 알려주고 있습니다... 넘어가도록 하겠습니다.
Outer
언리얼 AnswerHub에서의 Outer에 대한 설명을 인용하겠습니다 :
객체의 "Outer"는 객체를 "소유"하는 객체입니다. 예를 들어, 구성 요소는 액터 또는 상위 구성 요소가 소유하고 액터는 해당 레벨이 소유합니다. UObject에서 파생 된 클래스의 객체를 생성 할 때마다 생성된 객체를 Outer 제공합니다. (CreateDefaultSubobject는 암시적으로 현재 객체(=this)를 Outer Object로 제공합니다.)
때때로 객체는 해당 객체를 소유하지 않고 다른 객체에 대한 포인터를 갖습니다. 예를 들어 UNavMovementComponent에는 형제 UPathFollowingComponent에 대한 포인터가 있습니다. 이 경우, UPathFollowingComponent는 Outer로 UNavMovementComponent를 갖지 않습니다.
설명이 너무 깔끔하게 되어 있어서 제가 설명할 부분은 없는 것 같으므로 예시를 한 번 들어보겠습니다. 레벨에 위아래로 둥둥 떠다니는 액터를 만든다고 하겠습니다(Unreal 공식 문서의 C++ 프로그래밍 듀토리얼의 FloatingActor를 연상하시면 충분할 것 같습니다). 일단 둥둥 떠다니는 로직은 신경쓰지 않도록 하고, 이 액터를 만들기 위해서는 최소한 '스태틱 메시 컴포넌트' 정도는 갖춰야 할 것입니다. 둥둥 떠다니는 로직에 대한 재사용성을 위해서 별도의 Custom Movement 컴포넌트를 만들 수도 있겠습니다.
설명의 단순화를 위해 스태틱 메시 컴포넌트만을 가지도록 하는 액터를 간단히 소개하겠습니다:
/* header file */
class SomeModule_API AFloatingActor : public AActor
{
// Something about AActor...
private:
UFUNCTION(BlueprintReadWrite, VisibleAnywhere, Category=Custom, meta=(AllowPrivateAccess=true))
class UStaticMeshComponent* SMComp;
};
/* source file */
// codes...
AFloatingActor::AFloatingActor()
{
SMComp = CreateDefaultSubobjet<UStaticMeshComponent>(TEXT("SMComp"));
RootComponent = SMComp;
}
스태틱 메시를 정적으로 생성한 모습입니다. 여기서 사용되는 CreateDefaultSubobject는 내부적으로 Outer를 this로 설정합니다. 즉, FloatingActor는 자연스레 SMComp의 Outer Object가 됩니다. 액터가 액터 컴포넌트를 소유한다는 관점에서 바라봤을 때 합리적이라고 볼 수 있습니다.
UStaticMeshComponent가 AActor를 Outer로 가지게 된다는 것을 방금 알게 되었습니다. 여기서 중요한 것은 AActor 역시 Outer를 가지게 된다는 것입니다. 즉, SomeObject->GetOuter()->GetOuter()->... 이런 식으로 Outer Chain이 형성되어 있다는 얘기이기도 합니다. 이것을 확인하기 위해서는 호출 가능한 함수에서 다음과 같이 로그를 찍어보면 알 수 있습니다:
UObject* Outer = this;
while(Outer != nullptr)
{
// 현재 객체의 이름을 로그에 남깁니다. 의사코드로 작성하겠습니다.
UE_LOG(SomeLogClass, Log, TEXT("Current Object Class : %s"), *Outer->Class());
Outer->GetOuter():
}
AActor 클래스에서 이것을 실행하게 되면 다음과 같은 결과를 얻을 수 있습니다 :
Current Object Class : FloatingActor_c
Current Object Class : Level
Current Object Class : World
Current Object Class : Package
즉, 레벨은 액터를 소유하고 있고, 월드는 레벨을, 패키지가 월드를 소유하고 있는 것을 마지막으로 Outer Chain이 끝을 맺게 됩니다. Outer Chain을 위로 타고 올라갔을 때 끝을 한 번에 알고 싶다면 다음과 같은 함수를 사용할 수 있습니다:
UPackage * GetOutermost() const
언리얼 엔진에서는 이 Outer를 통해서 형성된 OuterChain을 GetOuter()를 통해서 어떤 패키지에 소유되어 있는 지 알 수 있습니다. 또한 이 Outer는 GC에 유용한 정보를 제공합니다. 가비지 컬렉팅의 경우, 예를 들어, 액터가 Destroy된다면 이것을 Outer로 삼고 있는 모든 객체에 대해서 다음 가비지 컬렉팅에 수집되어 메모리가 해제됩니다.
레퍼런스
How can I understand the data member 'Outer' in the UObjectBase class
이득우의 게임블로그
Unreal Engine 4.9 Release Note
'개발 > Unreal Engine 4' 카테고리의 다른 글
Unreal UMG와 HUD, 그리고 Slate(Unreal UMG, HUD, and Slate) (2) | 2019.07.13 |
---|---|
언리얼 반복자(Unreal Iterator) (0) | 2019.07.04 |
런타임에서 스태틱 메시 정점 읽어오기(Get static mesh vertices in Runitme) (0) | 2019.06.10 |
언리얼 모듈 컴파일(Unreal Module Compile) (0) | 2019.06.08 |
언리얼 경로 함수(Unreal Path Helper) (1) | 2019.06.03 |
- Total
- Today
- Yesterday
- C7568
- 퍼포스 개요
- C# 람다식
- 구간합
- P4 Streams
- code copyright
- 언리얼 엔진
- 구글테스트
- Perforce Streams
- 퍼포스 스트림
- C# lambda expression
- c++ hot reload
- C++
- Auto
- visual studio hot reload
- c++ 핫 리로드
- C++ Compile error
- visual studio 핫 리로드
- C# 익명함수
- MSVC C1083
- P4 Stream
- GoogleTest
- UE4
- 행렬
- 코드 저작권
- Perforce Stream
- Visual Studio C1083
- game hot reload
- 알고리즘
- DXGI
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 | 31 |