• Unreal Engine의 초기 설정을 제공
  • 간단한 수준에서 Section으로 구분되는 Key-Value를 제공
  • 이 파일을 이용해 Build, Blatform에서 Engine 시작 시 Load되는 Object, Property의 Default 값이 설정된다.
  • 확장자: .ini

구조

  • 더보기
    [SECTION1]
    <KEY1>=<VALUE1>
    <KEY2>=<VALUE2>
     
    [SECTION2]
    <KEY3>=<VALUE3>
    • Key-Value 중 Value는 비워둬도 무방하다.

SectionName

  • 임의의 알파벳 String
  • Config System은 변수가 Code에 존재하는지와 무관하게 모든 Definition을 Load한다.

Module

Plugin

  •  
  • 더보기
    [/Script/PluginName.ClassName]

Blueprint

  • 더보기
    [/PathToUAsset/UAssetName.UAssetName_C]

Custom Section Name

  • 더보기
    [MyConfigSection]
  • Custom Section Name을 사용하면 Section 안에 포함된 Config 변수를 수동으로 Query해야 한다.

Comment

  • 더보기
    [Core.Log]
    ; 다음과 같은 줄을 사용해 DefaultEngine.ini 안에서 로그 카테고리별로 디스플레이 수준을 오버라이드하는 데 이 섹션을 사용할 수 있습니다.
    ; LogTemp=warning

Key-Value Pair

Array

  • Config File의 Array는 여러 Config File에 걸쳐 정보를 통합/축적/오버라이드하는 매커니즘을 제공한다.

Empty

  • 연산자: !
  • Array의 Contents를 비웁니다.
    • = 이후의 모든 값을 무시합니다.

Append

  • 연산자: +
  • Array에 값이 없다면 값을 추가한다.
    • 값이 있다면 아무것도 안 한다.

Remove

  • Array에서 값을 제거합니다.
  • 값이 정확히 매칭되어야 합니다.

Append Duplicate

  • Array에 값이 이미 존재하더라도 값을 배열에 추가합니다.

Array 사용 예시

  • 더보기
    MyConfigArray=7
    !MyConfigArray=ClearArray
    +MyConfigArray=2
    +MyConfigArray=3
    +MyConfigArray=4
    .MyConfigArray=2
    -MyConfigArray=4

Append Duplicate를 사용하는 예시

  • 대표적으로 그래픽 세팅이 있다.
    • Default.ini에서 모든 플랫폼에 제공되는 그래픽 세팅을 추가
    • [Platform].ini에서 각 플랫폼 별 제공하는 별도의 그래픽 세팅 추가
  • 이 경우 게임 실행 시 Default.ini에 기재된 그래픽 세팅이 먼저 로딩 되고,
    [Platform].ini에 기재된 세팅을 로드한다.
  • 이 때, Append Duplicate를 사용하면 [Platform].ini에서의 값이 Default.ini의 값을 오버라이드 하며 않고 확장한다.
  • 그 결과 두 ini 파일에 기재된 값이 동시에 공존하게 된다.

Struct

  • 더보기
    [/Script/MyModule.MyClass]
    MyStruct=(MyStructProperty1=Value1,MyStructProperty2=Value2,...)

String

  • 더보기
    [Internationalization]
    ; 다음은 코어 엔진을 현지화하기 위한 경로이며, 현지화된 게임 콘텐츠 세팅은 *Game.ini 에 있습니다
    +LocalizationPaths=../../../Engine/Content/Localization/Engine
    +CultureDisplayNameSubstitutes="ja;\u53f0\u6e7e;\u30c1\u30e3\u30a4\u30cb\u30fc\u30ba\u30bf\u30a4\u30da\u30a4"
    +CultureDisplayNameSubstitutes="\u53f0\u6e7e;\u4e2d\u534e\u53f0\u5317"
    • 따옴표(", ")는 득수 escape 문자, 16진수, UTF 시퀀스의 사용을 지원한다.
      • 이 점은 현지화/국제화에 유용하다.

Project의 Config File

  • 다음 2가지를 고려하며 작성해야 한다.
    • Unreal Engine이 인식하는 Config File Category는 무엇인지
    • 동일한 Category 내의 파일은 서로 어떻게 상호작용 하는지

Config File Category

Common

  • Engine
  • Game
  • Input
  • DeviceProfiles
  • GameUserSettings
  • Scalability
  • RuntimeOptions
  • InstallBundle
  • Hardware
  • GameplayTags

Editor-Only

  • Editor
  • EditorPerProjectUserSettings
  • EditorSettings
  • EditorKeyBindings
  • EditorLayout

Desktop-Only

  • Compat
  • Lightmass

Config File Layer

  • 각 Category에는 서로 다른 Config File이 있을 수 있다.
  • ex)
    • Engine/Config/BaseGame.ini
    • Engine/Config/Window/WindowsGame.ini
  • 위 두 File은 Game Config File Category에 속한다.
    • 각 File은 추가 Customization Layer를 제공한다.
  • 동일한 Category의 Config File은 Layer에 따라 정리가 된다.
    • 동일한 Category의 File에 중복된 Key-Value Pair가 있는 경우,
      Layer 상 뒤에 오는 파일이 앞에 오는 값을 override한다.
  • Engine Directory에 저장된 Config File은 Engine 배포를 사용하는 모든 Project에 적용된다.
  • <ProjectDirectory>에 위치한 Config File은 해당 Project에만 적용된다.
  • <Platform>에 위치한 Config File은 해당 Platform에만 적용된다.

Category Config File 순서

  1. Engine/Config/Base.ini
  2. Engine/Config/Base<CATEGORY>.ini
  3. Engine/Config/<PLATFORM>/Base<PLATFORM><CATEGORY>.ini
  4. Engine/Platforms/<PLATFORM>/Config/Base<PLATFORM><CATEGORY>.ini
  5. <PROJECT_DIRECTORY>/Config/Default<CATEGORY>.ini
  6. Engine/Config/<PLATFORM>/<PLATFORM><CATEGORY>.ini
  7. Engine/Platforms/<PLATFORM>/Config/<PLATFORM><CATEGORY>.ini
  8. <PROJECT_DIRECTORY>/Config/<PLATFORM>/<PLATFORM><CATEGORY>.ini
  9. <PROJECT_DIRECTORY>/Platforms/<PLATFORM>/Config/<PLATFORM><CATEGORY>.ini
  10. <LOCAL_APP_DATA>/Unreal Engine/Engine/Config/User<CATEGORY>.ini
  11. <MY_DOCUMENTS>/Unreal Engine/Engine/Config/User<CATEGORY>.ini
  12. <PROJECT_DIRECTORY>/Config/User<CATEGORY>.ini
  • 조금 더 자세한 정보는 Engine/Source/Runtime/Core/Public/Misc/ConfigHierarchy.h 파일 참고

Config Layer 예시

  • 더보기

    Engine/Config/BaseEngine.ini

    [/Script/EngineSettings.GameMapsSettings]
    GameDefaultMap=/Engine/Maps/Templates/OpenWorld

     

    MyExampleProject/Config/DefaultEngine.ini

    [/Script/EngineSettings.GameMapsSettings]
    GameDefaultMap=/Game/ThirdPerson/Maps/ThirdPersonMap.ThirdPersonMap
  • 위 경우, Project의 DefaultEngine.ini가 Engine의 BaseEngine.ini보다 뒤에 오기 때문에
    GameDefaultMap의 값은 ThirdPersonMap이 된다.

Code에서 Config 변수 사용

  • Config File의 변수는 UPROPERTY, USTRUCT에 자동으로 적용하거나,
    Config Manager에서 수동으로 값을 읽을 수 있다.

변수에 Config Setting 적용

자동

  • Config File Layer 안에서 자동으로 값을 Load 하도록 Class 설정

Section Format

  • 더보기
    [/Script/ModuleName.ClassName]
    • 여기서 ClassName은 U나 A 접두사를 제외한 클래스 이름이다.

자동으로 Config 변수를 Load

  • 가정
    • MyGameModule 안에 AMyConfigActor 클래스 배치
    • AMyConfigActor 안에 Config File에서 변경하고 싶은 변수 MyConfigVariable이 있다 가정
  1. UClass 선언에서 읽어 올 Config File Category를 설정
  2. Config 할 수 있도록 클래스 내 내 멤버 변수에 Config 마킹
  3. 선택한 Config File Category의 Layer 어디든 변수를 설정한다.
  • 예시
    • 더보기
      UCLASS(config=Game)
      class AMyConfigActor : public UObject
      {
      	GENERATED_BODY()
       
      	UPROPERTY(Config)
      	int32 MyConfigVariable;
      }

       

      [/Script/MyGameModule.MyConfigActor]
      MyConfigVariable=3

수동

Function

  • Engine/Source/Runtime/Core/Public/Misc/ConfigCacheIni.h에서 다음 함수들 제공
    • GetBool
    • GetInt
    • GetInt64
    • GetFloat
    • GetDouble
    • GetString
    • GetText
    • GetArray

Config Category

  • Config Category는 G<Category>Ini로 식변된다.
    • 그 값은 Engine/Source/Runtime/Core/Public/CoreGlobals.h에 있다.

Config Setting 편집

  • 다음 작업을 통해 Config Setting을 변경할 수 있다.
    • 적절한 .ini 파일의 Config 값 수정
    • Unreal Editor 내 Project Settings에 노출된 Config 값 수정
      • 단, 모든 Config Setting이 Project Settings에 노출되지는 않는다.

Code의 Config Setting 저장

  • 더보기
    UCLASS(config=Game)
    class AMyConfigActor : public UObject
    {
    	GENERATED_BODY()
     
    	UPROPERTY(Config)
    	int32 MyConfigVariable;
    }

     

    AMyConfigActor *Settings = GetMutableDefault<AMyConfigActor>();
    Settings->MyConfigVariable = 42;

     

    FString PathToConfigFile;
    Settings->SaveConfig(CPF_Config, *PathToConfigFile);

Config 관련 Console Command

  • 더보기
    GetIni [Platform@]IniFile:Section Key
    • 모든 Config Setting 값을 확인할 수 있다.
    • 심지어 현재 사용중인 Platform 이외의 Platform Setting을 포함한 모든 Config 값을 볼 수 있다.
    • 이 Command는 Load된 Config File만 검색한다.
      • Load 되지 않은 Config File에 있는 Setting에 대한 Query는 실패할 수 있다.

특정 Config File의 Property Override

  • 더보기
    -ini:<CATEGORY>:[SECTION_1]:<KEY_1>=<VALUE_1>,[SECTION_2]:<KEY_2>=<VALUE_2>,...

Category의 Default File

Category 내 모든 파일

접미사가 일치하는 Config File

모든 Config File

Config File에서 Console 변수 설정

  • Config File은 Console 변수를 명시할 수 있는 다른 Machanism을 제공한다.
  • 일부 Console 변수는 일반 Config Category에서 설정될 수 있으며,
    다른 Console 변수는 특정 Config Category에서 설정해야 합니다.
  • 일반적으로 Console 변수는 Project Directory 내 DefaultEngine.ini의 [ConsoleVariables] Section에서 설정된다.

특정 Category

  • 특정 Section에서 설정되어야만 하는 특정 Category의 Console 변수가 있다.

Rendering

  • Serction: /Script/Engine.RendererSettings
  • r.로 시작하는 모든 Console 변수

Rendering Override

  • Section: /Script/Engine/RenderOverrideSettings
  • r.SupportAllShaderPermutations에만 해당

Streaming

  • Section: /Script/Engine.StreamingSettings
  • s.로 시작하는 모든 Console 변수

Garbage Collection

  • Section: /Script/Engine.GarbageColectionSettings
  • gc.로 시작하는 Console 변수

Network Settings

  • Section: /Script/Engine.NetworkSettings
  • n.VerifyPeer, p.EnableMultiplayerWorldOriginRebasing, NetworkEmulationProfiles에만 해당

Cooker Settings

  • Section: /Script/UnrealEd.CookerSettings
  • cook.으로 시작하는 Console 변수

추가 정보를 위한 유용한 Source File

  • 다음 Engine file에서 Config System과 그 Component에 대한 추가 정보를 찾아볼 수 있다.
    • ConfigCacheIni
    • CoreGlobals
    • ConfigHierarchy

 

 

 

 

'UE5 > Architecture' 카테고리의 다른 글

[Architecture] Version Control of Asset and Package  (0) 2024.05.07
[Architecture] Command-line Arguments  (0) 2024.05.07
[Architecture] Task System  (0) 2024.05.06
[Architecture] Programming Subsystem  (0) 2024.05.06
[Architecture] String  (0) 2024.05.06

https://dev.epicgames.com/documentation/ko-kr/unreal-engine/tasks-systems-in-unreal-engine

 

언리얼 엔진의 태스크 시스템 | 언리얼 엔진 5.4 문서 | Epic Developer Community

언리얼 엔진의 태스크 시스템에 대한 개요입니다.

dev.epicgames.com

https://dev.epicgames.com/documentation/ko-kr/unreal-engine/tasks-system-references-in-unreal-engine#busywait

 

언리얼 엔진의 태스크 시스템 레퍼런스 | 언리얼 엔진 5.4 문서 | Epic Developer Community

태스크 시스템에서 실행 가능한 여러 태스크에 대한 레퍼런스입니다.

dev.epicgames.com

  • GamePlay Code를 Async하게 실행할 수 있는 기능을 제공하는 Task Manager
  • Dependent 작업에 대한 Directed Acyclic Graph를 만들고 실행할 수 있다.
  • Unreal Engine의 Task Manager인 TaskGraph를 Implement해야 한다.
    • 참고로 Task System과 Task Graph는 동일한 Back-end(Scheduler, Woker Thread)를 공유한다.

주요 기능

  • Async하게 실행해야 하는 호출 가능 Object를 제공하는 Task 실행
  • Task 완료 및 실행 결과 정보를 획득하는 동안 대기
  • 선행 Task 지정
    • Task 실행 전에 완료해야 하는 Task
  • Task 내부에 중첩된 Task를 실행
    • 부모 Task는 중촙 Task가 완료될 때까지 계속 실행이 된다.
  • Pipe(Task Chain) 빌드
  • Task간 Synchronize 및 Signaling에 필요한 Task Event 사용

실행

  • 더보기
    Launch(
            UE_SOURCE_LOCATION, 
            []{ UE_LOG(LogTemp, Log, TEXT("Hello Tasks!")); }
    );

     

    • UE_SOURCE_LOCATION
      • Debug Name
        • Task의 Debug 및 Task를 실행한 코드 탐색을 지원하는 용도
        • 가급적 고유한 이름이 좋다.
      • 이 매크로는 Source File의 Format File Name과 File이 사용되는 행에 String을 생성하는 매크로
    • Lambda 문
      • 실제로 실행되는 Task Body Object
  • Task 완료를 대기하거나, 그 결과를 받아야 하는 경우에는 Launch의 반환값을 사용한다.
    • 더보기
      FTask Task = Launch(UE_SOURCE_LOCATION, []{});
  • 또한 Task 실행 결과를 반환할 수 있다.
    • 더보기
      TTask<bool> Task = Launch(UE_SOURCE_LOCATION, []{ return true; });
    • 위 코드의 FTask도 사실 TTask<void>를 재정의 한 것이다.
  • Task는 Async하게 실행이 되며, 실행 Thread와 동시에 실행될 수 있다.
    • 때문에 순서는 따로 정의하지 않는다.
  • 하지만 Task Priority를 지정해 순서에 영향을 미칠 수 있다.
    • 더보기
      Launch(UE_SOURCE_LOCATION, []{}, ETaskPriority::High);
    • 기본값은 Normal이다.
  • 보통은 Task Body로 Lambda를 사용하지만, 호출 가능한 Object를 사용할 수도 있다.
    • 더보기
      		void Func() {}
      		Launch(UE_SOURCE_LOCATION, &Func);
       
      		struct FFunctor
      		{
      			void operator()() {}
      		};
      		Launch(UE_SOURCE_LOCATION, FFunctor{});

디테일

  • FTask는 Smart Pointer처럼 실제 Task의 Handle 역할을 한다.
    • Task의 수명 역시 Reference Count로 관리한다.
  • Task를 실행하면 수명과 필수 Resource를 할당한다.
    • Reference를 초기화 하려면 다음과 같은 방식으로 Task Handle을 초기화 해야 한다.
    • 더보기
      FTask Task = Launch(UE_SOURCE_LOCATION, []{});
      Task = {}; // release the reference
  • Task Handle을 해제해도 Task가 바로 소멸되지 않는다.
    • System이 Task 실행에 필요한 자체 Reference를 가지고 있기 때문이다.
    • 이 Reference는 Task 완료 후에 해제가 된다.

완료 대기

Taks 완료 여부 확인

  • 더보기
    bool bCompleted = Task.IsCompleted();

Task 완료 대기

Timeout 있는 Task 완료 대기

  • 더보기
    bool bTaskCompleted = Task.Wait(FTimespan::FromMillisecond(100));

모든 Task 완료 대기

  • 더보기
    TArray<FTask> Tasks = {Task1, Task2};
    Wait(Tasks);

Task 실행 결과 획득

  • 더보기
    TTask<int> Task = Launch
    (UE_SOURCE_LOCATION, []{ return 42; });
    int Result = Task.GetResult();
  • Launch는 Task 완료 및 결과가 준비될 때까지 Block된다.

Busy-Waiting

  • Task는 실행 중 현재 Thread를 Block하기 때문에 완료가 되는 것을 기다리는 것은 비효율적이다.
  • Busy-Waiting은 그 대안으로 제시가 된 기능이다.
    • Busy-Waiting를 사용한 경우, Thread는 Task를 대기 중일 때 다른 작업을 실행하려 시도한다.
  • 통제된 환경에서 사용하면 매우 유용하지만, 자체적으로 제한사항이 있어 신중하게 사용해야 한다.
    • Busy-Waiting 도중에는 실행중인 Scheduler가 Task를 선택/제어 할 수 없다.
  • 이러한 제한사항 때문에 다음 문제가 발생하기도 한다.
    • Thread가 Busy-Waiting 상태에 진입해 재진입 가능성이 없는 Mutex를 Lock한 동안,
      Scheduler가 Task를 선택해 동일한 Mutex를 Lock 하려 시도하는 Deadlock
    • 필수 경로에서 상대적으로 짧은 Task 때문에 Busy-Waiting을 수행하는 동안
      Scheduler가 더 오래 걸리는 Task를 선택해 발생하는 성능 저하

Prerequisite, Subsequent

  • Task는 다른 Task에 Dependency를 가질 수 있다.
    • 이 때 Dependency의 대상이 되는 Task를 선행 Task(Prerequisite)
    • Dependency를 가지는 Task를 후행 Task(Subsequent)라 지칭한다.
  • Prerequisite를 설정하면 여러 Task를 대상으로 DAG를 만들 수 있다.
  • 또한 Task Dependency에는 Worker Thread를 Block하지 않는다는 큰 이점이 있다.
    • Task 실행 순서를 항상 변경할 수도 있다.
  • 더보기
    FTask Prerequisite = Launch(UE_SOURCE_LOCATION, []{});
    FTask Subsequent = Launch(UE_SOURCE_LOCATION, []{}, Prerequisite);
    FTask A = Launch(UE_SOURCE_LOCATION, []{});
    FTask B = Launch(UE_SOURCE_LOCATION, []{}, A);
    FTask C = Launch(UE_SOURCE_LOCATION, []{}, A);
    FTask D = Launch(UE_SOURCE_LOCATION, []{}, Prerequisites(B, C));

중첩

  • Task Dependency가 실행에 대한 Dependency라면, Nested Task는 완료에 대한 Dependency이다.
    • Task A가 실행 도중 Task B를 실행한다 가정했을 때,
      Task A는 자체 실행이 되더라도 Task B가 완료되어야 실행이 완료될 수 있다.
  • Nested Task는 Task 기반 Async Interface를 노출할 때 흔히 볼 수 있는 패턴이다.
    • 하지만 Task B는 내부 일부 구현을 누출하는 것이므로 바람직하지 않다.
  • 더보기
    FTask TaskA = Launch(UE_SOURCE_LOCATION, 
    [] 
       { 
            FTask TaskB = Launch(UE_SOURCE_LOCATION, [] {}); 
            AddNested(TaskB);
       }
    );
    TaskA.Wait(); // `TaskA` 와 `TaskB` 가 전부 완료되어야 반환
    • AddNested는 주어진 Task를 현재 Thread에 의해 실행되는 Task에 중첩됨을 표시한다.
    • Task 내부에서 호출되지 않는 경우 Assert를 호출한다.

Pipe

  • 동시 실행이 아닌, 연속적으로 실행되는 Task로 이루어진 Chain
  • 기존의 공유 Resource에 여러 Thread가 Access 할 때에는 Synchronize를 위해 Mutex를 잠과 Lock을 건다.
    • 다만 이 방법은 Thread가 차단되어 성능이 크게 저하되는 경우가 있다.
    • 특히, Resource가 충돌 할 때에 저하가 심해진다.
  • 복잡한 Resource를 사용 하는 경우, 다음 기능을 제공하는 편이 바람직하다.
    • Async 작업을 시작할 수 있는 Interface
    • 작업의 완료 여부를 확인할 수 있는 기능
    • 완료 Event에 Delegate 하는 기능
  • Pipe는 이 중 Async Interface의 구현을 간소화하기 위해 제공되었다.
    • 공유 된 Resource마다 Pipe를 두고, 모든 Access를 Pipe에서 실행한 Task 내부에서 수행한다.
  • 더보기
    class FThreadSafeResource
    {
    public:
        TTask<bool> Access()
        {
            return Pipe.Launch(TEXT("Access()"), [this] { return ThreadUnsafeResource.Access(); });
        }
    
        FTask Mutate()
        {
            return Pipe.Launch(TEXT("Mutate()"), [this] { ThreadUnsafeResource.Mutate(); });
        }
    private:
        FPipe Pipe{ TEXT("FThreadSafeResource pipe")};
        FThreadUnsafeResource ThreadUnsafeResource;
    };
    
    FThreadSafeResource ThreadSafeResource;
    //여러 스레드에서 동일한 인스턴스에 동시 액세스함
    bool bRes = ThreadSafeResource.Access().GetResult();
    FTask Task = ThreadSafeResource.Mutate();
    • FThreadSafeResource
      • 이 Class로 Task에 따라 Public Thread Safe Async Interface를 추가할 수 있다.
      • 이 Interface는 thread Unsafe REsource를 캡슐화 한다.
  • Pipe Task는 순차적 실행을 보장하기 때문에 추가 동기화는 필요 없다.
    • 또한 가벼운 Object라 Task의 Collection을 저장하지 않는다.
  • 심각한 Performance 저하 없이 수 천개의 Pipe를 사용할 수도 있다.
    • 더보기
      FPipe Pipe{ UE_SOURCE_LOCATION };
      FTask TaskA = Pipe.Launch(UE_SOURCE_LOCATION, []{});
      FTask TaskB = Pipe.Launch(UE_SOURCE_LOCATION, []{});
      • 위에서 TaskA와 TaskB는 동시에 실행되지 않기에 서로 동기화 될 필요가 없다.
      • 다만 실행 순서가 예상과 다를 수 있다.
        • 대부분은 사실 예측이 가능하다.
  • Pipe Task는 다른 Task와 동일한 기능을 지원한다.
    • Dependency를 가진다거나
    • Behaviour 순서를 따른다거나.
  • 이 때 Pipe는 Dependency를 먼저 해결하고 다음 Task를 Pipe화 한다.
    • 즉, Prerequisite를 가진 Task는 Pipe 실행을 차단하지 않는다.
    • 또한, Prerequisite는 Pipe Task의 실행 순서를 변경할 수 있다.
  • Pipe는 녹색 Thread로 취급한다.
    • 녹색 Thread는 Worker Thread에서 실행되며, 쓰레드를 건너띌 수 있다.
    • 위 예시로 설명하면, TaskA와 TaskB는 서로 다른 Thread에서 실행할 수 있다.
  • 마지막으로 Pipe는 다음 성격도 가진다.
    • Pipe API는 Thread Safe하다.
    • Pipe Object는 Copy/Move가 불가하다.
    • 한 Task를 여러 Pipe에서 실행할 수 없다.

Event

  • Task Body가 없고 실행할 수 없는 특별한 Task Type
    • 다른 Task와 달리, 초기에 실행되지 않으며 명시적으로 Trigger 해야만 한다.
  • Synchronize 및 Primitive로 유용하다.
  • 일회성 FEvent와 유사하게 사용된다.
  • 다른 Task의 Prerequisite, Subsequent로 사용할 수 있다.

Task를 실행하되, 명시적으로 해제할 때까지 실행 보류

  • 더보기
    FTaskEvent Event{ UE_SOURCE_LOCATION };
    FTask Task = Launch(UE_SOURCE_LOCATION, []{}, Event);
    Event.Trigger();
  • Task Event를 다른 Task의 Prerequisite로 사용.
  • 처음에는 Event가 Signaling 상태가 아니므로 완료되지 않는다.
    • Task 역시 보류중인 Prerequiste가 있기 때문에, 이 Task(Event)가 해결되기 전까지 예약 및 실행되지 않는다.
  • Task Event를 Trigger하면 Signaling 상태로 전환된다.

Task Event를 Jointer Task로 활용

  • 더보기
    FTask TaskA = Launch(UE_SOURCE_LOCATION, []{});
    FTask TaskB = Launch(UE_SOURCE_LOCATION, []{});
    FTaskEvent Joiner{ UE_SOURCE_LOCATION };
    Joiner.AddPrerequisites(Prerequisites(TaskA, TaskB));
    Joiner.Trigger();
    ...
    Joiner.Wait();
  • Joiner는 속해 있는 모든 Task에 대해 Dependnecy를 가진다.
    • 즉, TaskA와 TaskB가 모두 끝나야 한다.

Task 실행 도중에 이벤트 발생을 대기

  • 더보기
    FTaskEvent Event{ UE_SOURCE_LOCATION };
    FTask Task = Launch(UE_SOURCE_LOCATION,
        [&Event]
        {
            ...
            Event.Wait();
            ...
        });
    ...
     Event.Trigger();
  • 일반적으로는 좋은 구조는 아니다.
    • 가급적 Prerequisite를 다시 제작하는 것을 권장

Task를 실행하되, 자동 완료 Flag를 설정하지 않고 필요할 때 명시적으로 완료 처리

  • 더보기
    FTaskEvent Event{ UE_SOURCE_LOCATION };
    FTask Task = Launch(UE_SOURCE_LOCATION,
        [&Event]
        {
        AddNested(Event);
        });
    ...
    Event.Trigger();

Debug 및 Profiling

  • 모든 Task, Task Event, Pipe에는 사용자가 만든 Debug Name이 있다.
    • 이를 통해 Debuger가 Runtime중에 Task를 구별할 수 있다.
  • 내부 상태를 검사하기 위해 Visual Studio Native Visualizer Tool이 제공된다.
  • Unreal Insights에서는 Task 및 Task Event를 실행/예약/수행/완료 할 때 Visualize 할 Task Trace Channel을 추가한다.

'UE5 > Architecture' 카테고리의 다른 글

[Architecture] Command-line Arguments  (0) 2024.05.07
[Architecture] Config File  (0) 2024.05.06
[Architecture] Programming Subsystem  (0) 2024.05.06
[Architecture] String  (0) 2024.05.06
[Architecture] Asset Registry  (1) 2024.05.06

https://dev.epicgames.com/documentation/ko-kr/unreal-engine/programming-subsystems-in-unreal-engine

 

프로그래밍 서브시스템 | 언리얼 엔진 5.4 문서 | Epic Developer Community

언리얼 엔진 4 의 프로그래밍 서브시스템 개요입니다.

dev.epicgames.com

  • 수명이 관리되는 Auto Instancing Class
  • 사용하기 쉬운 확장점을 제공
    • Programmer는 BP 및 Python을 바로 노출시켜 복잡한 Engine Class 수정 혹은 Override를 피할 수 있다.

지원되는 Subsystem

  • Subsystem이 생성될 시 Initialize()가 호출 됨
  • Subsystem이 종료될 시 Deinitialize()가 호출 됨
    • 이 때 Subsystem에 대한 참조가 삭제되고, 더 이상 참조가 없으면 Garbate Collect 된다.

Engine

  • UEngineSubSystem을 상속 받아서 생성
  • Module의 Startup() 함수 반환 이후 Initialize() 된다.
  • Module의 Shutdown() 함수 반환 이후 Deinitialize() 된다.

Editor

  • UEditorSubsystem을 상속 받아서 생성
  • Module의 Startup() 함수 반환 이후 Initialize() 된다.
  • Module의 Shutdown() 함수 반환 이후 Deinitialize() 된다.

GameInstance

  • UGameInstanceSubsystem을 상속받아서 생성
  • 게임 도중에 생성 됨

LocalPlayer

  • ULocalPlayerSubsystem을 상속받아서 생성
  • ULocalPlayer의 Lifetime과 동일
  • 대상 Player가 Level을 이동할 시 해당 LocalPlayer Subsystem도 같이 이동한다.

World

  • UWorldsubsystem을 상속받아서 생성
  • 각 레벨별로 존재 함

Subsystem을 사용하는 이유

  • 프로그래밍 시간이 절약된다
  • Engine Class Override를 피할 수 있다.
  • 이미 바쁜 Class에 API 추가를 피할 수 있다.
  • 사용자에게 친숙한 유형의 Note를 통해 BP로 Access할 수 있다.
  • Editor Scripting이나 Test code 작성을 위해 Python Script에 Access 할 수 있다.
  • Code Base의 모듈성과 일관성을 제공합니다.

Plugin

  • Subsystem은 특히 Plugin을 만들 때 유용하다.
  • Plugin은 작동에 필요한 코드 관련 지침이 없어도 된다.
  • 사용자는 Plugin을 게임에 추가하기만 하면, Plugin이 언제 Instancing 및 Initialize가 될 지 정확히 알 수 있다.
    • Subsystem이기 때문에
  • 결과적으로 UE4에 제공되는 API 및 기능을 사용하는 데에만 중점을 둘 수 있다.

Blueprint로 Subsystem Access

  • Sybsystem은 BP에 자동 노출이 된다.
    • 또한 Context를 이해하는 Smart Node를 추가되므로 형변환이 필요가 없다.
    • 표준 UFUNCTION() 마크업 및 규칙으로 BP에 를 사용할 수 있는 API를 제어할 수 있다.

Module? Subsystem? (Not Reference)

  • 일반적으로 Module 안에 Subsystem이 포함된 구조인데, 유추 가능한 이유가 있긴 하다.
    • 보통은 Subsystem은 어지간해서는 Dedicated Server, Client 양쪽 다 존재한다.
    • Subsystem이 Logic만 담당하면 문제가 없지만, Data를 처리하게 되면 보안 이슈가 발생한다.
    • 때문에 Subsystem을 전용 Module로 감싸고, Module을 Server에서만 추가하는 방안을 고려해볼 수 있다.
      • 이 경우 Server에는 Module이 있으니 Subsystem을 접근할 수 있다.
      • 반대로 Client에서는 Module이 없으니 Module Validation에서 걸려 Subsystem에 접근이 불가하다.
  • 특정 기능에 관해서만 동작을 하는 Manager 같은 것이 있다면 Module/Subsystem을 이용하는 것이 좋을 것 같다.

'UE5 > Architecture' 카테고리의 다른 글

[Architecture] Config File  (0) 2024.05.06
[Architecture] Task System  (0) 2024.05.06
[Architecture] String  (0) 2024.05.06
[Architecture] Asset Registry  (1) 2024.05.06
[Architecture] Asset Reference  (0) 2024.05.06

https://dev.epicgames.com/documentation/ko-kr/unreal-engine/string-handling-in-unreal-engine

 

언리얼 엔진의 스트링 처리 | 언리얼 엔진 5.4 문서 | Epic Developer Community

FName, FText, FString에 대한 레퍼런스 가이드와 언리얼 엔진에서 사용할 수 있는 문자열 클래스에 대한 개요입니다.

dev.epicgames.com

FName

  • 초경량 시스템
    • 주어진 문자열을 재사용 시, Data Table에 한번만 저장된다.
  • 초경량 시스템으로 속도가 매우 빠르다.
    • 대소문자를 구분하지 않는다.
      • 소유 String Table에 대한 Index와 Instance Number의 조합으로 저장된다.
    • 수정 및 조작이 불가하다.
    • FString에서 FName으로 변환 할 때 Hash Table을 사용한다.
      • 한번 사용된 String은 Table에 1번만 저장된다.
  • Unreal Engine에서 Asset 내 이름은 모두 FName으로 작업된다.
    • AssetName
    • Dynamic Material Instance
    • Skeletal Mesh Bone Name

Compare

  • == 연산자를 사용함.
    • index 안의 값을 비교하기에 CPU가 크게 절약된다.
  • Compare 함수도 사용 가능한데, C 계열의 Compare와 동일한 방식으로 결과를 반환한다.

Search Name Table

  • Constructor에 FName_Find를 추가했을 때, 반환 값으로 Name Table 내의 index 값을 Search 가능.
    • Name_None을 반환한 경우에는 Name Table 내에 해당 Name이 없는 것
  • 더보기
    	if( FName(TEXT("pelvis"), FNAME_Find) != NAME_None )
    	{
    		// Do something
    	}

Ftext

  • 다음 기능을 제공하여 Text Localization을 지원하는 주요 Component
    • Create Literal Localized Text 
    • Text Format
    • Convert from Number to Text
    • Convert from Date/Time to Text

Convert

  • 모든 기능이 제공은 되지만, Localization Data가 삭제 된다.

Compare

  • 일반적으로 Overload 된 연산자를 제공하지는 않음.
  • 다만 몇 가지 함수를 이용해 미묘한 차이가 있는 Included Data를 인식한다.

FString

  • 조작이 가능한 String Class
    • 대소문자 변환
    • 부분 문자열 발췌
    • 역순
  • 조작이 가능한 만큼 다른 String Class보다 가격이 비싸다.
    • Printf를 사용해 조립이 가능하다.

 

'UE5 > Architecture' 카테고리의 다른 글

[Architecture] Task System  (0) 2024.05.06
[Architecture] Programming Subsystem  (0) 2024.05.06
[Architecture] Asset Registry  (1) 2024.05.06
[Architecture] Asset Reference  (0) 2024.05.06
[Architecture] Module(+ Package)  (0) 2024.05.03

https://dev.epicgames.com/documentation/ko-kr/unreal-engine/asset-registry-in-unreal-engine

 

언리얼 엔진의 에셋 레지스트리 | 언리얼 엔진 5.4 문서 | Epic Developer Community

언리얼 엔진 에디터에서 에셋을 검색하는 방법과 에셋을 로드하기 전에 에셋 유형에 대해 자세히 알 수 있는 방법을 설명합니다.

dev.epicgames.com

  • Editor가 Load되면서 Load되지 않은 Asset에 대한 정보를 Asynchronous하게 모드은 Editor Subsystem
    • Editor가 Asset을 Load하지 않고 List를 만들 수 있도록 Memory에 저장
    • 이 정보는 Authoritative하고 Memory의 Asset이나 Disk의 File이 변경 시 자동으로 최신화가 된다.
    • 주로 Contents Browser에서 쓴다.
      • 하지만 Editor Code 어디에서도 사용은 가능하다.

Asset List 구하기

  • 더보기
    	FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
    	TArray<FAssetData> AssetData;
    	const UClass* Class = UStaticMesh::StaticClass();
    	AssetRegistryModule.Get().GetAssetsByClass(Class->GetFName(), AssetData);
  • Asset의 Load/Unlaod 여부는 FAssetData로 반환한다.
  • 또한 FAssetRegistryModule(정확히는 IAssetRegistry)에서 아래 함수로 Category 별  List를 만들 수 있다.

FAssetData를 UObject*로 변환하기

Filter

  • GetAssets() 함수를 호출할 때 FARFilter를 사용해 여러 Category 별 Filtering 되는 Asset List를 만들 수 있다.
  • 하나의 Component에는 Element가 여러개 있을 수 있다.
    • Asset은 ARFilter의 모든 Component를 충족해야 한다.
    • 하나의 Component를 충족시키려면, Asset은 그 안의 Element 아무것에나 일치하면 된다.

TagAndValue

  • FAssetData가 나타내는 Asset의 Property Value와 관련된 값
  • Asset이 저장되면서 안에 들어있는 UAsset 파일의 Header에 기록될 때 수집 된다.
    • UPROPERTY(AssetRegistrySearchable)이 선언된 것들에 대해서만 수집한다.
    • 더보기
      	/** 이 텍스처를 샘플링할 때 사용할 텍스처 필터링 모드입니다. */
      	UPROPERTY(Category=Texture, AssetRegistrySearchable)
      	TEnumAsByte<enum TextureFilter> Filter;
      • 위 변수는 저장 될 때 Key는 "Filter"로, Value는 "TF_Linear"와 같은 enum TextureFilter의 문자열을 갖는다.
  • Asset Registry는 이 Header를 읽고 그에 맞게 TagAndValues를 채운다.
    • 반드시 저장을 해야 Asset Registry가 Property를 발견할 수 있다.
  • Asset Registry가 UPROPERTY가 아닌 정보를 검색 하게 할 수도 있다.
    • GetAssetRegistryTags()를 구현하여 TagsAndValues에 값을 수동으로 추가해준다.
    • 이 함수는 UObject에서 제공한다.

Asyncronous Data Discovery

  • Asset Registry는 UAsset 파일을 Async Load로 읽는다.
    • 때문에 요청한 시간에 모든 Asset List 전체가 없을 수 있다.
  •  Editor 코드에서는 전체 List를 요청하면 Registry에서 Asset과 관련된 여러 Delegate Call-back을 제공한다.
    • 더보기
      	/** 레지스트리에 에셋이 추가될 때에 대한 콜백을 등록/해제합니다.  */
      	virtual FAssetAddedEvent& OnAssetAdded() = 0;
       
      	/** 레지스트리에서 에셋이 제거될 때에 대한 콜백을 등록/해제합니다. */
      	virtual FAssetRemovedEvent& OnAssetRemoved() = 0;
       
      	/** 레지스트리에 에셋 이름이 변경될 때에 대한 콜백을 등록/해제합니다. */
      	virtual FAssetRenamedEvent& OnAssetRenamed() = 0;
       
      	/** 에셋 레지스트리가 파일 로드를 완료했을 때에 대한 콜백을 등록/해제합니다. */
      	virtual FFilesLoadedEvent& OnFilesLoaded() = 0;
       
      	/** 백그라운드 파일 로드 진행상황 업데이트를 위한 콜백을 등록/해제합니다. */
      	virtual FFileLoadProgressUpdatedEvent& OnFileLoadProgressUpdated() = 0;
       
      	/** 현재 에셋 레지스트리가 파일을 로드중이라 모든 에셋에 대해 알지 못하면 참을 반환합니다. */
      	virtual bool IsLoadingAssets() = 0;
  • 더보기
    	void FMyClass::FMyClass()
    	{
    		// 업데이트 감지를 위해 에셋 레지스트리 모듈을 로드합니다.
    		FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
    		AssetRegistryModule.Get().OnAssetAdded().AddRaw( this, &FMyClass::OnAssetAdded );
    	}
     
    	FMyClass::~FMyClass()
    	{
    		// 델리게이트 등록해제를 위해 에셋 레지스트리 모듈을 로드합니다.
    		FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
    		AssetRegistryModule.Get().OnAssetAdded().RemoveAll( this );
    	}
     
    	void FMyClass::OnAssetAdded(const FAssetData& AssetData)
    	{
    		// 에셋 레지스트리가 에셋을 발견했습니다.
    		// 즉 방금 생성되었거나 방금 디스크에서 찾았다는 뜻입니다.
    		// 이 함수의 코드가 빠른지 확인, 아니면 수집 프로세스가 늦춰집니다.
    	}
    • Asset Registry는 Commandlet에서도 사용될 수 있다.
      • 하지만 정보가 Async Load되지 않아 Load가 완료될 때까지 LoadModule() 호출이 Block된다.
    • Code가 Asset을 Async하게 Discover하기 기다리면서 Slate UI가 있는 경우,
      진행 상황을 사용자에게 알리는 SAssetDiscoveryIndicator Widget이 들어있을 것이다.

'UE5 > Architecture' 카테고리의 다른 글

[Architecture] Programming Subsystem  (0) 2024.05.06
[Architecture] String  (0) 2024.05.06
[Architecture] Asset Reference  (0) 2024.05.06
[Architecture] Module(+ Package)  (0) 2024.05.03
[Architecture] DataAsset과 Asset Manager  (0) 2024.05.03

https://dev.epicgames.com/documentation/ko-kr/unreal-engine/referencing-assets-in-unreal-engine

 

언리얼 엔진의 에셋 참조 | 언리얼 엔진 5.4 문서 | Epic Developer Community

언리얼 엔진은 에셋 참조 방식과 메모리에 로드되는 익스텐션을 통해 제어할 수 있는 다양한 메커니즘을 제공합니다.

dev.epicgames.com

Hard Reference

  • Object A가 Object B를 직접 참조
  • Object A가 Load 되면 Object B도 같이 Load 됨.
    • 신경쓰지 않으면 다수의 Asset이 한꺼번에 Load되어 메모리를 크게 잡아먹을 수 있다.

Direct Property Reference

  • 가장 흔한 방법
  • UPROPERTY 매크로로 노출되며, 노출된 것을 Designer가 다음 두 가지 방법으로 Architype을 지정한다.
    • Blueprint 상속
    • World에 배치된 Instance
  •  
    • EditDefaultsOnly 키워드를 입력하면 Property는 한 Object의 Default Property 일부로서만 설정 가능하다.
    • 위 ConstructionStartStinger는 USoundCue나 그 자식 Class만 Architype을 지정할 수 있다.
  • 더보기
    	/** construction start sound stinger */
     
    	UPROPERTY(EditDefaultsOnly, Category=Building)
     
    	USoundCue* ConstructionStartStinger;

Construction Time Reference

  • 다음 2가지 조건을 만족 할 때 보통 사용 됨
    • Programmer가 Load 해야 할 Asset을 정확히 알고 있을 때
    • 그 Property를 Object 생성 시 일부분으로 설정하는 경우
  • ConstructorHeleprs라는 Class를 사용
    • 생성 단계 도중 Object와 Object의 Class를 찾음
  • 더보기
    		/** gray health bar texture */
     
    	UPROPERTY()
     
    	class UTexture2D* BarFillTexture;
     
    	AStrategyHUD::AStrategyHUD(const FObjectInitializer& ObjectInitializer) :
    		Super(ObjectInitializer)
    	{
    		static ConstructorHelpers::FObjectFinder<UTexture2D> BarFillObj(TEXT("/Game/UI/HUD/BarFill"));
     
    		...
     
    		BarFillTexture = BarFillObj.Object;
     
    		...
     
    	}
     
    • 위 ConstructorHelpers Class는 Memory에서 Asset을 Search하고, 없을 경우 Load 한다.
      • Load 할 때에는 Asset에 대한 전체 경로를 사용한다.
    • Asset이 존재하지 않거나, 오류로 Load 할 수 없는 경우, Property가 nullptr로 설정된다.
      • 때문에 항상 사용하기 전에 Validation Check를 해주는 편이 안전하다.

Soft Reference

Indirect Property Reference

  • TSoftObjPtr을 이용
    • Designer의 경우, 직접 Property Reference인 것처럼도 작업할 수 있다.
    • 하지먼 직접 Pointer Reference를 사용하는 대신,
      Asset의 Load 여부에 대한 검사가 가능한 Template 코드와 함꼐 Property가 String으로 저장 된다.
  • 수동으로 Load 해야 한다.
    • LoadObject<>()나 StaticLoadObject(), FStreamingManager() Object를 이용해 Load 가능
  • LoadObject<>(), StaticLoadObject()는 Synchronous하기 때문에 Frame에 영향을 줄 수 있다.
    • GamePlay에 영향을 끼치지 않을 것이 확실한 것에만 사용하자.
  • 더보기
    	UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category=Building)
    	TSoftObjectPtr<UStaticMesh> BaseMesh;
     
    	UStaticMesh* GetLazyLoadedMesh()
    	{
    		if (BaseMesh.IsPending())
    		{
    			const FSoftObjectPath& AssetRef = BaseMesh.ToStringReference();
    			BaseMesh = Cast< UStaticMesh>(Streamable.SynchronousLoad(AssetRef));
    		}
    		return BaseMesh.Get();
    	}
    • TSoftObjPtr을 사용해 Runtime에서 Mesh를 지연시켜 Load
      • Load 되었는지 여부 확인
      • Load가 안 되어 있으면 FStreamingManager::SynchronousLoad로 Load
      • Load가 되어 있으면 TSoftObjPtr 안의 UStaticMesh Pointer 반환
    • 위와 같은 방식으로 UClass를 유예식으로 Load 할 수 있다.
      • TAssetSubclassOf을 대체하여 TSoftClassPtr을 사용

Find/Load Object

  • UPROPERTY 기반이 아니라 Runtime 상에서 String을 통해 Object Reference를 구할 때 사용하는 방법 중 하나
    • 다른 하나는 FindObject<>()로, 이미 Create/Load 된 UObject에서만 사용 할 때 유용함
  • 여기서 소개할 LoadObject<>()는 아직 Load되지 않은 Object에 적합함.
    • 내부에서 FindObject<>()와 동일한 역할을 수행하기 때문에 별도의 Find 과정이 불필요하다.
  • 더보기
    	DefaultPreviewPawnClass = LoadObject<UClass>(NULL, *PreviewPawnName, NULL, LOAD_None, NULL);
     
    	if (!DefaultPreviewPawnClass->IsChildOf(APawn::StaticClass()))
    	{
    		DefaultPreviewPawnClass = nullptr;
    	}

 

'UE5 > Architecture' 카테고리의 다른 글

[Architecture] String  (0) 2024.05.06
[Architecture] Asset Registry  (1) 2024.05.06
[Architecture] Module(+ Package)  (0) 2024.05.03
[Architecture] DataAsset과 Asset Manager  (0) 2024.05.03
[Architecture] Async Asset Loading  (0) 2024.05.03

https://dev.epicgames.com/documentation/ko-kr/unreal-engine/unreal-engine-modules

 

언리얼 엔진 모듈 | 언리얼 엔진 5.4 문서 | Epic Developer Community

모듈은 언리얼 엔진 소프트웨어 아키텍처의 구성 요소입니다. 코드를 모듈로 정리하여 더 효율적이고 유지보수하기 좋은 프로젝트를 만들 수 있습니다.

dev.epicgames.com

  • Unreal Engie Software Architecture에서 가장 기본적인 구성 요소
  • 특정 Editor Tool, Runtime Ability, Library 등의 기능을 독립된 코드 단위로 캡슐화
  • 모든 Project와 Plugin은 자체적인 Primary Module이 있다.
    • 하지만 이와 별개의 Module을 따로 정의하여 코드 정리를 할 수 있다.
  • C++ 20 Module과는 무관하다.

장점

  • 적절한 코드 분리를 강제하고 기능을 캡슐화 하여 구현을 숨긴다.
  • Module은 각기 다른 Compile Unit으로 Compile 되기 때문에 빌드 시간이 대폭 단축된다.
    • 변경되지 않은 Module은 컴파일 대상에서 제외된다.
  • Dependency Graph에서 서로 연결되며, IWYU 표준에 따라 Header에 실제 사용하는 코드만 포함하도록 제한한다.
    • 이는 컴파일 하는 도중 Project에서 사용하지 않는 Module은 안전하게 제외할 수 있다는 의미이기도 하다.
  • Runtime 중 지정한 Module의 Load/Unload 시간을 조절할 수 있다.
    • 이를 통해 활성화 할 시스템을 관리하여 Project의 Performance를 최적화 할 수 있다.
  • Compile 할 Platform 등 특정 조건에 따라 Project를 포함하거나 제외할 수 있다.

구성

https://dev.epicgames.com/documentation/ko-kr/unreal-engine/how-to-make-a-gameplay-module-in-unreal-engine

 

언리얼 엔진에서 게임플레이 모듈을 만드는 방법 | 언리얼 엔진 5.4 문서 | Epic Developer Community

언리얼 엔진 게임의 런타임 모듈을 처음부터 생성하는 방법을 알아봅니다.

dev.epicgames.com

  1. Project의 Source 폴더 최상위 경로에 Module용 Directory 생성
    • 이 때, Directory의 이름은 Module의 이름과 동일해야 한다.
    • 만약 Source 폴더의 Sub Directory에 Module을 만드려 한다면,
      Module 이름도 Sub Directory를 모두 포함해야 한다.
  2. Module의 Root Directory에 [ModuleName].Build.cs 파일을 생성하고, 다른 Module과의 Dependency를 정의한다.
    • Dependency를 정의하면 UBS(Unreal Build System)에서 Module을 발견한다.
  3. Module의 Root Directory에 Private, Public 폴더를 생성한다.
  4. Private 폴더에 [ModuleName]Module.cpp 파일을 생성한다.
    • 이 파일을 통해 Module 및 Module 관리를 위한 일반 함수를 시작/중단할 수 있다.
  5. .uproject나 .uplugin 파일에 속한 Module에 환경설정 정보를 추가한다.
    • 추가하는 목적은 Module을 언제 Load할지 정하기 위해서다.
    • Name, Type, Platform, Loading Phase 정보가 포함되어야 한다.
  6. 이 Module을 사용해야 하는 모든 Module의 Build.cs 파일에 dependency로 Module을 나열한다.
    • Project Primary Module에 대한 Build.cs 파일이 포함될 수 있다.
  7. [Module].Build.cs 파일이 변경되거나, Source 파일이 다른 Folder로 옮길 때 아래 함수로 Solution 파일을 생성한다.
    1. GenerateProjectFiles.bat
    2. .uproject 우클릭 -> Generate Project Files
    3. Editor에서 File -> Refresh -> Visual Studio -> Project

구조 이해하기

  • 모든 Module은 Plugin이나 Project의 Source Directory에 위치해야 한다.
  • Module의 Root Folder는 해당 Module과 이름이 동일해야 한다.
  • Root Folder의 Module마다 다음 항목을 가지고 있어야 한다.
    • [ModuleName].Build.cs
    • Module의 C++ 코드
    • Private, Public 폴더

Build.cs 파일에서 Dependency 설정

  • UBS는 IDE Solution 대신 Target.cs 파일과 Module의 Build.cs 파일에 따라 빌드를 한다.
    • UBT가 IDE Solution를 무시한다.
    • 때문에 Solution을 인식하도록 하려면 모든 Module의 Root Directory에 [ModuleName].build.cs 파일을 배치해야 한다.
  • 더보기
    	using UnrealBuildTool; 
     
    	public class ModuleTest: ModuleRules 
     
    	{ 
     
    	public ModuleTest(ReadOnlyTargetRules Target) : base(Target) 
     
    	{ 
     
    	PrivateDependencyModuleNames.AddRange(new string[] {"Core"}); 
     
    	} 
     
    	}
  • DependencyModuleNames 목록에 Module Name을 추가하면 사용할 수 있는 Module이 Code에 설정된다.

PrivateDependencyModuleNames

  • .cpp 파일처럼 Module을 Private으로만 사용하는 경우
  • Project의 Compile 시간을 줄여주기에 어지간해서는 Private Dependency를 사용하는 것이 좋다.
  • Header File 내에서 Forward Declaration을 사용해 수 많은 Dependency를 Private로 만들 수 있다.

PublicDependencyModuleNames

  • .h 파일처럼 Module에 있는 Class를 사용하는 경우
  • 이 Module에 Dependency를 가진 다른 Module도 이 항목에 등록된 Module의 Class를 사용할 수 있다.

Private/Public Folder

  • Module이 일반 C++ Module인 경우,
    해당 Module의 Root Directory 하위의 Private/Public Folder에 C++ 파일을 배치해야 한다.
    • ModuleType이 .uproject 혹은 .uplugin에서 External로 설정되지 않은 경우
  • 이 파일들은 C++ 코드에서 제공하는 접근자와 관련이 없다.
    • 대신 다른 Module의 Module Code에 대한 이용성을 제어한다.
    • 보통 이런 Folder를 사용할 때에는 .cpp File이 Private Folder에 배치되어야 한다.
  • .h File을 Private Folder에 배치한 경우
    • 보유 중인 Module 외의 다른 Module에 노출되지 않는다.
  • .h File을 Public Folder에 배치한 경우
    • UBS가 Contents를 현재 Module에 Dependency를 가진 모든 Module에 노출한다.
    • 외부 Module의 Class는 Public Folder에 포함된 Class를 확장할 수 있다.
      • 때문에 Public Folder에 포함된 Class, Struct, Enum을 사용하는 Variable, Reference를 생성할 수 있다.
      • 또한 코드 단위에서 제공하는 C++ 접근자가 그대로 동작한다.
  • 다른 대상에 Dependency를 가지지 않는 Module을 작업하는 경우에는
    Private, Public Folder를 사용할 필요가 없다.
    • 단, 이 경우에는 Folder 외부 모든 Code는 Private Folder에 있는 것처럼 행동한다.
    • 대표적인 예시로 Game에서 Dependency Chain의 끝에 자주 위치하는 Primary Module이 있다.
  • Unreal Editor에서 New Class Wizard로 새 Class를 생성하면 자동으로 Folder간 병렬 구조를 형성해준다.

C++에서 Module 구현

  • Module을 나머지 C++ Project에 노출하려면 다음 두가지 작업이 필요하다.
    • IModuleInterface를 확장하는 Class 생성
    • IMPLEMENT_MODULE Macro에 추가
  • 가장 간단한 방법은 Private Directory의 Module에서 .cpp 파일을 생성하고,
    이름을 [ModuleName]Module.cpp로 지정하는 것
  • 더보기
    	#include "Modules/ModuleManager.h"
     
    	IMPLEMENT_MODULE(FDefaultModuleImpl, ModuleTest);
  • FDefaultModuleImpl
    • IModuleInterface를 확장하는 빈 class
    • .cpp File에 구현할 Class를 직접 작성하여 더 디테일한 구현이 가능
  • IModuleInterface에는 Load/Unload를 수행할 때 Trigger 되는 여러 함수가 포함되어 있다.
  • https://docs.unrealengine.com/4.26/en-US/API/Runtime/Core/Modules/IModuleInterface/
  •  
 

IModuleInterface

Interface class that all module implementations should derive from.

docs.unrealengine.com

Project에서 Module 사용하기

  • 신규 Unreal Engine Project나 Plugin을 생성할 때마다 자체적인 Primary Module이 Source Folder에 자동 생성된다.
    • 외부 Module은 Primary Module의 Build.cs에 추가하여 Project에 포함시킬 수 있다.
  • UBT는 Compile 속도를 최적화 하기 위해 Project의 Dependency Chain에서 발견 한 Module만 Compile 한다.
    • 즉,  그 어떠한 Project에서 사용하는 Build.cs 파일에 포함되어 있지 않으면 컴파일에서 제외된다.

Module Load 방식 제어

 

https://dev.epicgames.com/documentation/ko-kr/unreal-engine/API/Runtime/Projects/EHostType__Type/?application_version=5.4

 

dev.epicgames.com

 

E Loading Phase/Type | Unreal Engine 5.4 Documentation | Epic Developer Community

Phase at which this module should be loaded during startup.

dev.epicgames.com

기타 파라미터

    • IncludelistPlatforms/ExcludelistPlatforms(배열)
      • 목록에 포함된 Platform에서 Comiple 할 Module을 포함/제외한다.
        • Win32
        • Win64
        • Mac
        • Linux
        • Android
        • IOS
    • IncludelistTargets/ExcludelistTargets(배열)
      • 목록에 포함된 Build Target에서 Comiple 할 Module을 포함/제외한다.
        • Game
        • Server
        • Client
        • Editor
        • Program
    • IncludelistTargetConfigurations/ExcludelistTargetConfigurations(배열)
      • 목록에 포함된 Build Configuration에서 Compile 할 Module을 포함/제외한다.
        • Debug
        • DebugGame
        • Development
        • Shipping
        • Test
    • IncludelistPrograms/ExcludelistPrograms(배열)
      • 특정 Program Name 범위 내에서 Compile 할 Module을 포함/제외한다.
    • AdditionalDependencies(배열)
      • Module에서 필요로 하는 Additional Dependency를 지정한다.
      • 대신, Build.cs 파일에서 지정해줘야 한다.

Module API 지정자

  • 가장 쉽게 생각해볼 수 있는 방법
    • Function/Class/Data를 자기 Module DLL 파일에 public인 것으로 Tag를 다는데 사용
  • 예를 들어 Engine Module에 있는 함수를 ENGINE_API로 마킹하면,
    Engine을 Import 하는 Module은 그 함수를 직접 접근할 수 있다.
    • 단, 이 방법은 Engine을 Modular Mode(Desktop에서의 DLL파일)로 Compile 할 때에만 사용된다.
  • 위와 반대로 모든 Code를 하나의 Exe 파일에 몰아 넣는 경우도 있다.
    • 이를 Monolithic Mode라 지칭한다.
  • Build Type은 UnrealBuildTool Setting이나 Platform, Build Configuration에 따라 제어된다.
  • 실제로 API 매크로는 UBT의 Compile 방식에 따라 다음과 같이 분류된다.
    • __declspec(dllexport).
      • Modular
    • __declspec(dllimport).
      • Import 중인 Module에 대한 Public Module Header를 포함할 때
    • __declspec().
      • Monolithic
  • API 매크로는 다른 Module에서 정적으로 Import 되는 Module에 대해서만 의미가 있다.
    • 대표적으로 "Core" Module이 있다.
    • UE4의 거의 모든 Module은 *.Build.cs 파일에 "Core"를 Import Dependency로 지정한다.
  • 다수의 Module은 절대 정적으로 Import 될 일 없다.
    • 대표적인 예로 "SceneOutliner" Module
  • 이러한 Module들은 "동적으로 Load 되는 Module"이라 지칭한다.
    • 이러한 Module들이 재밌는 점은 일종의 Plugin처럼 시동 시 발견되고, 종종 즉시 Reload가 가능하다.
  • API 매크로가 사용되는 곳은 대부분 이전 Code에서 새로운 Module이 그 DLL에서 Code에 접근하고자 할 때이다.
    • 새로운 Code에서는 API 매크로가 훨씬 적게 사용된다.
    • 그 대신 DLL 경계를 넘나들며 함수성을 노출시키는 예쁜 Interface Layer를 셋업한다.

Package

https://dev.epicgames.com/documentation/ko-kr/unreal-engine/packaging-unreal-engine-projects

 

프로젝트 패키징 | 언리얼 엔진 5.4 문서 | Epic Developer Community

언리얼 게임 프로젝트의 배포용 패키지 만들기입니다.

dev.epicgames.com

  • .uasset, .umap 파일을 포함하는 Container
    • 게임 내 다양한 리소스(BP, Texture, Mesh 등)와 Level을 저장한다.
  • Package는 각 Asset의 수정사항을 추적하고, Asset 간의 Dependency를 관리하는데 사용된다.
  • Directory 설정에 따른 성능이 천차만별이기 때문에 Directory 정리를 잘 해줘야 한다.

Configuration

  1. Contents Directory 구조 정의
    • Project의 Content Directory 안에 구조를 설정한다.
      • 반드시 Content Directory에 직접 설정 될 필요 없고, Sub Folder를 사용해도 된다.
  2. Asset 관리
    • 각 Folder 내에 관련된 Asset을 Group으로 나눈다.
      • 예를 들어, 특정 Character에 대한 모든 Asset(BP, Texture, Animation 등)을 한 Folder 내에 저장한다.
      • 이 방식은 논리적인 구조를 통해 팀원이 필요한 Asset을 쉽게 찾고, 관리할 수 있도록 한다.
  3. Dependency, Reference 관리
    • Asset간의 Dependency를 적절히 관리하기 위해 Unreal Engine의 Asset Manager를 사용한다.
    • Asset Manager는 Project의 모든 Asset간의 Dependency를 추적하고,
      필요한 Asset만 Load하여 불필요한 Load를 줄인다.
  4. Compile, Build Optimization
    • UBS는 변경된 Asset만 Compile/Build한다.
    • 따라서 Package 단위로 Asset을 관리하면 특정 부분의 수정이 전체 Build에 미치는 영향을 최소화 할 수 있다.
    • 또한 Level Streaming과 같은 기능을 활용해 필요할 때 특정 Level이나 Asset을 Load하도록 설정할 수 있다.

'UE5 > Architecture' 카테고리의 다른 글

[Architecture] String  (0) 2024.05.06
[Architecture] Asset Registry  (1) 2024.05.06
[Architecture] Asset Reference  (0) 2024.05.06
[Architecture] DataAsset과 Asset Manager  (0) 2024.05.03
[Architecture] Async Asset Loading  (0) 2024.05.03

https://dev.epicgames.com/documentation/ko-kr/unreal-engine/data-assets-in-unreal-engine

 

언리얼 엔진의 데이터 에셋 | 언리얼 엔진 5.4 문서 | Epic Developer Community

언리얼 엔진의 데이터 에셋에 대한 정보입니다.

dev.epicgames.com

https://dev.epicgames.com/documentation/ko-kr/unreal-engine/asset-management-in-unreal-engine

 

언리얼 엔진에서 에셋 관리 | 언리얼 엔진 5.4 문서 | Epic Developer Community

에셋 로드 및 언로드

dev.epicgames.com

 

Data Asset

  • UDataAsset을 상속받은 Class들
  • 다양한 System 관련 정보들을 저장할 수 있다.
  • Game의 핵심 매커니즘이 아닌 비교적 덜 중요한 요소들에 주로 사용한다.

Primary Data Asset

  • UPrimaryDataAsset을 상속받은 Class들
    • Asset Manager는 GetPrimaryAssetId 함수를 통해 확보한 ID에서 Primary Asset을 직접 조작할 수 있다.
    • 반대로 Data Asset을 Primary Data Asset으로 만드려면 GetPrimaryAssetId 함수를 override하여
      유효한 FPrimaryAssetId 를 반환해야 한다.
  • FPrimaryAssetId는 다음 두 가지 요소로 구성되어 있다.
    • Primary Asset Type
      • Asset 그룹을 알려준다
    • Primary Asset Name
      • Contents Browser에서 노출되는 애셋 이름을 기본값으로 사용한다.
  • Asset Manager가 직접적으로 처리하고 Load 가능한 Asset들이 포함된다.
    • 대표적으로 UWorld
  • GetPrimaryAssetId 함수를 구현해 Asset Bundle을 지원하는 Asset Manager에서 수동으로 Load/Unload 할 수 있다.
    • 사용 시 Project Setting -> Asset Manager 탭에서 참조하고자 하는 Primary Asset의 Directory를 설정해야 한다.
    • 이 때, PrimaryAssetType에 작성한 Text가 GetPrimaryAssetId()에서 반환하는 값과 같아야 한다.

Secondary Data Asset

  • 공식적으로 지칭된 것은 아님
    • Asset Manager가 직접 처리하고 Load하지 못하고, Primary Asset에 의존적으로 처리되는 DataAsset을 지칭
  • 보통 대부분의 Asset들은 UWorld가 Load 될 때 같이 Load 되는 Secondary Asset이라 직접 Load 할 필요가 없다.

Asset Manager

  • Primary Asset의 Search 및 Load Process를 관리하는 Singleton
  • Asset Manager는 2가지 Type의 Asset을 처리한다.
    • Blueprint Class
    • BP 이외의 Asset
      • Level
      • UDataAsset Instance
  • Primary Asset Type에는 특정 BaseClass가 연결되며, 환경설정에서 Blueprint Class를 저장할지 여부가 지정된다.

Bueprint Class

  • Primary Asset의 Base Class가 될 수 있는 요소
    • Primary Data Asset
    • Primary Data Asset의 자손
    • GetPrimaryAssetId를 오버라이드 하는 Actor Subclass
  • 새로운 Blueprint Primary Asset을 생성하려면 위 요소를 Base로 하는 Blueprint Class를 생성하면 된다.
  • C++에서 Blueprint Primary Asset에 Access 하는 방법
    • GetPrimaryAssetObjectClass 함수 호출
    • 이름에 'Class'가 포함된 Blueprint Asset Manager 함수 호출
  • Blueprint Primary Asset Class를 확보하면 다른 Blueprint Class처럼 취급할 수 있다.
    • 새 Instance Spawn
    • GetDefaults 함수를 사용해 Blueprint와 연결된 Class Default Object의 ReadOnly Data에 Access
  • Blueprint Class를 Instance화 할 필요가 없는 경우 다음 두가지가 가능해진다.
    • UPrimaryDataAsset을 상속받은 Data-Only Blueprint에 Data 저장
    • Base Class에서 Child Class를 도출
      • UPrimaryDataAsset을 확장하는 UMyShape 클래스를 생성
      • UMyShape를 상속 받아 BP_MyRectangle라는 BP Class를 생성
      • MP_MyRectangle을 상속 받은 BP_MySquare BP Class 생성
      • 이 때 BP_MySquare의 PrimaryAssetId는 MyShape:BP_MySquare가 된다.

Blueprint 이외의 Asset

  • Primary Asset Type이 Blueprint Data를 저장할 필요가 없는 경우에 사용
  • Blueprint 이외의 Asset은 Code Access 측면에서 더 간편하고, 메모리 효율도 더 우수하다.
  • Editor에서 생성하는 방법
    • Advanced Contents Browser에서 New Data Asset을 생성
    • 혹은 Custom UI를 사용하여 새 New Level을 생성
  • Asset이 생성되는 원리는 Blueprint Class가 생성되는 원리와 다르다.
    • 생성한 Asset은 Class 그 자체가 아니라 Instance이다.
    • 때문에 Class나 다른 Asset을 상속 받을 수 없다.
  • Asset의 Class에 Access하는 방법
    • GetPrimaryAssetObject와 같은 C++ 함수 사용
    • 이름에 'Class'가 포함되지 않은 Blueprint 함수 사용
  • Class가 Load되면 직접 Access하여 Data를 읽을 수 있다.

StreamableManager

  • Primary Asset에 포함된 구조체
    • Object를 Async Load하는 작업 수행
    • Object를 Unload 할 수 있을 때까지 Streamable Handle을 이용해 Memory를 보존
  • Singleton인 Asset Manager와 달리 다양한 부분에서 서로 다른 용도의 Streamable Manager가 존재 함.

Asset Bundle

  • Primary Asset과 관련된 특정 Asset의 이름이 지정된 목록
  • 생성하는 방법
    • Meta Tag가 'AssetBundles'인 UObject의 TSoftObjectPtr이나
      FStringAssetReference 멤버로 구성된 UPROPERTY Section 에 Tag 지정
      • Tag 값은 Secondary Asset을 저장할 Bundle 이름을 나타낸다.
      • 더보기
        	/** 메시 */
        	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = Display, AssetRegistrySearchable, meta = (AssetBundles = "TestBundle"))
        	TSoftObjectPtr<UStaticMesh> MeshPtr;
        • 예를 들어,
          위 MeshPtr에 저장된 Static Mesh Asset은 UObject를 저장했을 때 TestBundle에 추가가 된다.
  • Runtime 시점에 Project의 Asset Manager Class로 등록
    • 이 경우 Programmer가 FAssetBundleData을 채워 Asset Manager에 전달하는 코드를 작성해야 한다.
    • 그 목적으로 Programmer는 아래 행동을 할 수 있다.
      • UpdateAssetBundleData 함수 override
      • Bundle의 Secondary Asset과 연결하려는 Primary Asset ID로 AddDynamicAsset 호출

Disk에 저장된 Primary Asset Regist 및 Load

  • 대부분의 Primary Asset은 Contents Browser에 표시되고, Disk에 저장된 Asset 파일로 존재한다.
    • 때문에 Artist나 Designer가 편집할 수 있다.
  • Programmer가 이런 용도의 Class를 생성하는 가장 간단한 방법은 UPrimaryDataAsset을 상속받는 것이다.
    • 이 Class에는 Asset Bundle Data를 Load/Save 하는 기능이 내장되어 있다.
    • 이 함수는 Asset Bundle을 사용하기 위해 필요한 기능을 구현한 가장 간단한 예시이기에
      다른 Base Class를 사용하려는 경우에 살펴보아도 좋다.
  • 더보기
    	/** 맵 화면에서 사용자가 선택할 수 있는 존 */
    	UCLASS(Blueprintable)
    	class MYGAME_API UMyGameZoneTheme : public UPrimaryDataAsset
    	{
    		GENERATED_BODY()
     
    		/** 존 이름 */
    		UPROPERTY(EditDefaultsOnly, Category=Zone)
    		FText ZoneName;
     
    		/** 이 존에 진입할 때 로드될 레벨 */
    		UPROPERTY(EditDefaultsOnly, Category=Zone)
    		TSoftObjectPtr<UWorld> LevelToLoad;
     
    		/** 맵에 이 존을 표현하는 데 사용되는 블루프린트 클래스 */
    		UPROPERTY(EditDefaultsOnly, Category=Visual, meta=(AssetBundles = "Menu"))
    		TSoftClassPtr<class AGameMapTile> MapTileClass;
    	};
    • 위 Class는 Virtual 환경에서 Zone Type을 지정하는 방법을 알 수 있는 예시코드다.
    • Zone Type은 Game 전체 Map 화면에 World를 시각적으로 표현할 때 사용할 Art Asset을 Game에 알려준다.
    • UMyGamezoneTheme은 UprimaryDataAsset을 상속 받았다.
      • 따라서 Asset의 약식 이름과 NativeClass를 사용하는 유효한 버전의 GetPrimaryAssetId가 존재한다.
      • 예를 들어, Forest라는 이름으로 저장된 UMyGameZoneTheme의 Primary Asset Id는 'MyGameZoneTheme:Forest'이다.
    • Editor에서 UMyGameZoneTheme Asset을 저장할 때마다 PrimaryDataAsset::AssetBundleData가
      업데이트 되어 해당 Asset을 Secondary Asset에 추가한다.
  • Primary Asset을 Regist/Load 하려면 다음 액션을 수행해야 한다.
    1. Project의 Custom Asset Manager Class를 Engine에 알린다.
      • DefaultEngine.ini 파일에서 [/Script/Engine.Engine] Section의 AssetManagerClassName 변수를 설정한다.
      •  [/Script/Engine.Engine]
         AssetManagerClassName=/Script/Module.UClassName

        • 여기서 Module은 Project의 Module Name을 뜻한다.
        • UClassName은 사용하려는 UClass의 이름을 뜻한다.
      • 단, Project에 특수 기능이 필요하지 않는 경우에는 이 단계를 건너뛴다.
    2. Primary Asset을 AssetManager에 Regist한다.
      • Project Settings 메뉴에서 구성
        • Primary Asset Types to Scan 검색/등록 할 Primary Asset의 Type, 위치 및 용도 나열
          Directories to Exclude Primary Asset Scan 대상에서 명시적으로 제외되는 Directory
          Test Asset을 제외하는데 유용
          Primary Asset Rules Asset 처리 방법이 명시된 구체적인 Override 나열
          Only Cook Production Assets DevelopmentCook으로 지정된 Asset이 Cooking 과정에서 오류를 유발하도록 설정
          최종 배포 빌드에서 Test Asset 제거에 유용
          Primary Asset ID Redirects 이 목록에 ID가 표시되는 Primary Asset 관련 Data를 조회할 때 제공되는 대체 ID로 교체
          Primary Asset Type Redirects Primary Asset 관련 Data를 조회할 때 원래 Type 대신 이 목록에서 제공되는 Type으로 교체
          Primary Asset Name Redirects Primary Asset 관련 Data 조회 할 때 제공되는 Asset Name으로 교체
      • 시작 시점에서 Primary Asset을 Asset Manager Class에 등록하도록 Programming
        • AssetManager::StartInitialLoading 함수 override
        • 해당 함수에서 ScanPathsForPrimaryAssets 호출
        • 이 방식의 경우, 동일한 Type의 Primary Asset은 하나의 Sub Folder에 넣는 것이 좋다
          • Search/Register 속도가 빨라진다.
    3. Asset을 Load한다. 
      • AssetManager 함수를 사용해 적시에 Primary Asset를 Load/Unload 할 수 있다.
        • LoadPrimaryAssets / UnloadPrimaryAssets
        • LoadPrimaryAsset / UnloadPrimaryAsset
        • LoadPrimaryAssetsWithType / UnloadPrimaryAssetsWithType
      • 이 방식을 사용하면 Asset Manager가 해당 Asset Bundle을 참조해 Secondary Asset을 Load 한다.

Runtime 상에 생성된 Primary Asset Regist/Load

  • Runtime 상에서 Primary Asset을 Regist/Load 하는 이유를 이해하는데 유용한 함수가 있다.

ExtractSoftObjectPaths

  • UScriptStruct의 모든 UPROPERTY를 검사하고, Asset Reference를 식별한 뒤 Asset Name Array에 저장
    • Asset Name Array는 Asset Bundle을 만들 때 사용할 수 있다.
    • 더보기
      virtual void ExtractSoftObjectPaths
      (
          const UStruct * Struct,		// Asset Reference를 검색 할 UStruct
          const void * StructValue,		// Struct를 가르키는 void pointer
          TArray< FSoftObjectPath > & FoundAssetReferences,		// Struct에서 찾은 Asset Reference를 반환
          const TArray< FName > & PropertiesToSkip		// FoundAssetReferences에서 제외할 Property Name Array
      ) const

RecursivelyExpandBundleData

  • Primary Asset과 관련된 모든 Reference를 찾고, 재귀적 확장을 통해 Asset Bundle의 모든 Dependency를 탐색.
    • 더보기
      virtual void RecursivelyExpandBundleData
      (
      	/*
          * Asset Reference가 포함되어 있는 Bundle Data
          * 재귀적으로 확장되며, 연관된 Assset Set을 Load하는데 유용하다.
          */
          FAssetBundleData & BundleData
      ) const

       

      	// theater ID에서 추출한 이름 구성
      	UMyGameAssetManager& AssetManager = UMyGameAssetManager::Get();
      	FPrimaryAssetId WorldMapAssetId = FPrimaryAssetId(UMyGameAssetManager::WorldMapInfoType, FName(*WorldMapData.UniqueId));
       
      	TArray<FSoftObjectPath> AssetReferences;
      	AssetManager.ExtractSoftObjectPaths(FMyGameWorldMapData::StaticStruct(), &WorldMapData, AssetReferences);
       
      	FAssetBundleData GameDataBundles;
      	GameDataBundles.AddBundleAssets(UMyGameAssetManager::LoadStateMenu, AssetReferences);
       
      	// 재귀적 레퍼런스 확장을 통해 존에서 타일 블루프린트 픽업
      	AssetManager.RecursivelyExpandBundleData(GameDataBundles);
       
      	// 다이내믹 에셋 등록 
      	AssetManager.AddDynamicAsset(WorldMapAssetId, FSoftObjectPath(), GameDataBundles);
       
      	// 프리로드 시작
      	AssetManager.LoadPrimaryAsset(WorldMapAssetId, AssetManager.GetDefaultBundleState());

'UE5 > Architecture' 카테고리의 다른 글

[Architecture] String  (0) 2024.05.06
[Architecture] Asset Registry  (1) 2024.05.06
[Architecture] Asset Reference  (0) 2024.05.06
[Architecture] Module(+ Package)  (0) 2024.05.03
[Architecture] Async Asset Loading  (0) 2024.05.03

https://dev.epicgames.com/documentation/ko-kr/unreal-engine/asynchronous-asset-loading-in-unreal-engine

 

언리얼 엔진의 비동기 에셋 로딩 | 언리얼 엔진 5.4 문서 | Epic Developer Community

언리얼 엔진은 런타임 시 에셋을 로드하고 언로드하는 메서드를 제공합니다.

dev.epicgames.com

FSoftObjectPaths

  • Asset의 전체 이름으로 된 String이 들어있는 구조체
    • Property를 만들 시 UObject*로 나타냄
  • Cooking과 REdirector가 처리되기 때문에 Device에서 정상 동작을 보장한다.

FSoftObjPtr

  • FSoftObjectPaths를 감싸고 있는 TWeakObjPtr
  • Editor UI에서 특정 Class만 선택되도록 할 수 있다.
  • 실제 대상 Asset은 Get 함수로 Raw Object를 반환
  • 일반적으로는 Artist나 Designer가 수동으로 Setup 하는 것이 베스트
    • 다만 다음 조건에서는 Asset Registry나 Object Library를 사용하는 것이 좋다.
      • 특정 조건을 만족하는 질의를 모든 Asset Load 없이 시행하려는 경우

Asset Registry

  • Asset에 대한 MetaData를 저장하고 검색, 질의를 가능케 하는 System
  • 보통 Editor의 Contents Browser에서 정보를 표기하기 위해 사용됨.
    • 하지만 GamePlay 코드 상에서도 Load되지 않은 Asset에 대한 MetaData 질의를 하는데 사용 할 수 있다.
  • MetaData 질의에 사용하기 위해서는 해당 Data의 PROPERTY()에 AssetRegistrySearchable Tag를 추가해줘야 함.
  • 질의 결과는 FAssetData Object로 반환
    • Object 정보 뿐 아니라 Mark 된 Property가 들어있는 Key-Value Map도 포함되어 있다.

Object Library

  • Load된 Object와 그렇지 않은 Object를 합친 리스트가 FAssetData에 있는 경우로, 공유 Base Class를 상속한다.
  • 검색할 경로를 입력해 Load하면, 그 경로에 있는 모든 Asset이 Object Library에 추가된다.
  • Contents 폴더 일부분을 각기 다른 유형으로 지정하고 사용하면
    Artist/Designer가 Master List를 건들지 않고 Asset을 추가할 수 있다.
    • 이 때, Master List는 Asset을 로드하고 있는 게임 내 고유한(Singleton) List를 지칭한다.
    • 더보기

       

      	if (!ObjectLibrary)
      	{
      		   ObjectLibrary = UObjectLibrary::CreateLibrary(BaseClass, false, GIsEditor);
      		   ObjectLibrary->AddToRoot();
      	}
      	ObjectLibrary->LoadAssetDataFromPath(TEXT("/Game/PathWithAllObjectsOfSameType");
      	if (bFullyLoad)
      	{
      		   ObjectLibrary->LoadAssetsFromAssetData();
      	}
  • 위 코드는 새 Object Library를 생성하고, BaseClass를 할당하여 주어진 경로의 모든 Asset Data를 로드한다.
    • Option을 통해 실제 Asset Load도 가능.
  • Asset이 작은 경우에는 통째로 Load한다.
    • Asset이 Cooking 중인 경우에는 모두 Cooking이 된 후에 Load도 가능하다.
    • Cooking 도중 Asset Registry 질의를 하고 반환된 Asset을 Load하는 한,
      Object Library는 개발 환경에서나 라이브 환경에서나 동일하게 동작한다.
  • Object Library에 Asset Data가 들어가 있는 상태라면, 질의를 통해 특정 Asset만 선택적으로 Load 할 수도 있다.
  • 더보기
    	TArray<FAssetData> AssetDatas;
    	ObjectLibrary->GetAssetDataList(AssetDatas);
     
    	for (int32 i = 0; i < AssetDatas.Num(); ++i)
    	{
    		   FAssetData& AssetData = AssetDatas[i];
     
    		   const FString* FoundTypeNameString = AssetData.TagsAndValues.Find(GET_MEMBER_NAME_CHECKED(UAssetObject,TypeName));
     
    		   if (FoundTypeNameString && FoundTypeNameString->Contains(TEXT("FooType")))
    		   {
    				  return AssetData;
    		   }
    	}
    • 위 코드는 TypeName에 "FooType"이 들어있는 것들을 검색하고, 그 중 첫번째 것을 반환한다.
      • AssetData가 생긴 후,
        ToStringReference()를 호출하고 FSoftObjectPath로 변환하면 Async Load가 가능하다.

StreambleManager

  • 가장 간단한 Async Load
    • 이 외의 방법으로는 Async Load Package, BulkData가 있다.
  • 게임 전역으로 유일한 Singleton Object에 생성하는 것이 좋다.
    • DefaultEngine.ini나 자체적인 SingletonClass 등
      • 더보기

         

        [/Script/Engine.StreamableManager]
        ; 스트리밍 풀의 최대 크기를 메가바이트 단위로 설정합니다.
        s.Streaming.PoolSize=2000
        
        ; 애셋을 스트리밍할 때 사용할 수 있는 동시 스트리밍 요청의 최대 개수를 설정합니다.
        s.AsyncLoadingThreadCount=8
        
        ; 스트리밍 대기 시간을 초 단위로 설정합니다. 이는 에셋이 스트리밍 될 때까지의 지연 시간을 조정합니다.
        s.Streaming.HLODStrategy=2
        
        ; 스트리밍 세그먼트 크기를 설정합니다. 더 큰 크기는 스트리밍 프로세스를 최적화할 수 있지만 메모리 사용량이 증가할 수 있습니다.
        s.Streaming.SegmentedStreamingThreshold=20​

         

        #include "Engine/StreamableManager.h"
        #include "Engine/AssetManager.h"
        
        void MyFunction()
        {
            FStreamableManager& StreamableManager = UAssetManager::GetStreamableManager();
        
            // 비동기 로드를 위한 에셋 경로
            FString AssetPath = TEXT("Path/To/Your/Asset");
        
            // 에셋 로드
            StreamableManager.RequestAsyncLoad(AssetPath, FStreamableDelegate::CreateLambda([]()
            {
                // 에셋 로드 완료 후 수행할 작업
                UE_LOG(LogTemp, Log, TEXT("Asset Loaded Successfully!"));
            }));
        }​
  • FSoftObjPath를 전달하면 Load가 시작 함.
    • 작은 Object는 SynchronousLoad로도 충분 함.
    • 하지만 큰 Object는 Main Thread를 오래 점유하므로 RequestAsyncLoad를 사용해서 호출해야 한다.
      • RequestAsyncLoad는 완료 시 Delegate를 호출하도록 세팅 할 수 있다.
    • 더보기
      		void UGameCheatManager::GrantItems()
      	{
      		   TArray<FSoftObjectPath> ItemsToStream;
      		   FStreamableManager& Streamable = UGameGlobals::Get().StreamableManager;
      		   for(int32 i = 0; i < ItemList.Num(); ++i)
      		   {
      				  ItemsToStream.AddUnique(ItemList[i].ToStringReference());
      		   }
      		   Streamable.RequestAsyncLoad(ItemsToStream, FStreamableDelegate::CreateUObject(this, &UGameCheatManager::GrantItemsDeferred));
      	}
       
      	void UGameCheatManager::GrantItemsDeferred()
      	{
      		   for(int32 i = 0; i < ItemList.Num(); ++i)
      		   {
      				  UGameItemData* ItemData = ItemList[i].Get();
      				  if(ItemData)
      				  {
      						 MyPC->GrantItem(ItemData);
      				  }
      		   }
      	}
    • 예시에서 ItemList는 TArray<TSoftObjectPtr<UGameItem>>이며, Editor에서 Designer에 의해 수정되었다.
      • 위 코드는 ItemList를 순회하여 StringReferences로 변환시키고, 다음 Load를 위해 대기열에 등록한다.
      • ItemList가 모두 Load되거나 Load에 실패하면 전달 된 Delegate를 호출한다.
      • 그러면 Delegate는보통 Load 된 ItemList를 다시 순회하여 역참조를 구하고, 플레이어에게 전달해준다.
  • StreamableManager는 Dlegate가 호출될 때까지 Load하는 Asset에 대한 Reference를 유지한다.
    • 이는 Delegate 호출 전에 Garbage Collecting이 되는 일 없도록 방지하기 위함이다.
    • Delegate가 호출된 후에는 Reference가 해제되므로 반드시 어딘가에 Hard Reference를 해줘야 한다.

*Async Load 방식들에 대한 비교

StreamableManager

  • Runtime 중 Dynamic Asset Loading이나 Memory Resource가 제한된 환경에 적합하다.
    • 모바일이나 VR 환경
    • 주로 스킨, 무기, 추가 레벨 등에서 사용.
  • 반대로 초기 Game/Level Loading 등 한번만 Load하면 되지만 그 용량이 큰 경우에는 부적합하다.

Async Load Package

  • 전체 Game Level이나 World를 Async Load 할 때 적합
    • 보통 Map 전환 시 Loading Screen과 함께 사용하면 Load 지연을 인지하지 못하게 할 수 있다.
  • 또는 Game이나 Level 시작 시 필요한 Asset들을 Load할 때에도 적합하다.
  • 반대로 소규모, 또는 개별로 발생하는 Load에는 부적합하다.
    • Animation Clip, Texture 등

BulkData

  • 큰 Texture, Sound, Video와 같은 대용량 Resource 사용에 적합하다.
    • GamePlay 중 Memory에 직접 Load되어 성능 최적화에 기여 한다.
  • Media Player 또는 Video Streaming Service에서 async로 대용량 미디어 파일을 Load하고 재생할 때 유효
  • 모든 경우의 Synchronous Data 처리에 부적합하다.
    • Load 속도가 비교적 느리기 때문

 

 

 

 

'UE5 > Architecture' 카테고리의 다른 글

[Architecture] String  (0) 2024.05.06
[Architecture] Asset Registry  (1) 2024.05.06
[Architecture] Asset Reference  (0) 2024.05.06
[Architecture] Module(+ Package)  (0) 2024.05.03
[Architecture] DataAsset과 Asset Manager  (0) 2024.05.03

원래는 ULevel이나 ClientTravel 부분부터 상세히 찾아들어가보려 했는데 그러기에 앞서 Level 정보를 담고 있는 FWorldContext의 데이터나 대략적인 변수 용처를 먼저 알고 들어가는 것이 개념 정리에 좋을 것 같아 FWorldContext를 먼저 공부 해보았다.

 

/** FWorldContext
 *	A context for dealing with UWorlds at the engine level. As the engine brings up and destroys world, we need a way to keep straight
 *	what world belongs to what.
 *
 *	WorldContexts can be thought of as a track. By default we have 1 track that we load and unload levels on. Adding a second context is adding
 *	a second track; another track of progression for worlds to live on. 
 *
 *	For the GameEngine, there will be one WorldContext until we decide to support multiple simultaneous worlds.
 *	For the EditorEngine, there may be one WorldContext for the EditorWorld and one for the PIE World.
 *
 *	FWorldContext provides both a way to manage 'the current PIE UWorld*' as well as state that goes along with connecting/travelling to 
 *  new worlds.
 *
 *	FWorldContext should remain internal to the UEngine classes. Outside code should not keep pointers or try to manage FWorldContexts directly.
 *	Outside code can still deal with UWorld*, and pass UWorld*s into Engine level functions. The Engine code can look up the relevant context 
 *	for a given UWorld*.
 *
 *  For convenience, FWorldContext can maintain outside pointers to UWorld*s. For example, PIE can tie UWorld* UEditorEngine::PlayWorld to the PIE
 *	world context. If the PIE UWorld changes, the UEditorEngine::PlayWorld pointer will be automatically updated. This is done with AddRef() and
 *  SetCurrentWorld().
 *
 */

FWorldContext 구조체 선언 위에 적혀 있는 설명 주석입니다.

대략 번역해보면 다음과 같다.

  • 엔진 단위에서 UWorlds를 관리하기 위한 Context
  • 기본적으로 1개를 소지 하고 있다.
  • World간의 이동 뿐만 아니라 현재 PIE(Play In Editor, 에디터에서 실행하는 경우)의 UWorld까지 관리하는 방안을 제공
  • 반드시 UEngine 내부에서만 접근되어야 하며 외부에서 직접적으로 관리되어서는 안됨.
  • 외부에서는 UWorld*를 사용할 수 있으며 이를 엔진 단위 함수로 전달할 수 있다.
    이때 엔진 코드는 주어진 UWorld*와 연관된 WorldContext를 탐색할 수 있다.
  • 편의를 위해 UWorld*의 외부 포인터를 유지할 수 있다.
    예를 들어, PIE는 UEditorEngine::PlayWorld 포인터를 PIE WorldContext에 의존시킬 수 있다.
    만약 PIE UWorld가 바뀌면, PlayWorld 포인터는 AddRef()와 SeetCurrentWorld()함수로 인해 자동으로 갱신이 된다.

 

정확히 이해 못하겠지만 여기서 확실한 것이 하나 있습니다.

우리는 ULevel을 공부하기 전에 UWorld를 먼저 알아야 합니다.

이에 대해 매우 잘 정리된 블로그 포스트를 공유드립니다.

https://www.zhihu.com/column/insideue4

 

InsideUE5

深入UE5剖析源码,浅出游戏引擎架构理念

www.zhihu.com

중국에서 작성된 포스트인데 구글 번역으로 보면 내용 설명과 더불어 구조적인 고민이나 설명들이 여타 튜토리얼이나 문서에서는 볼 수 없는 것들이 많습니다.

원래는 출처 넣고 번역을 하려고 했으나 펌을 금지하기 때문에 링크로 공유 하겠습니다.

 

UWORLD

https://docs.unrealengine.com/5.0/en-US/API/Runtime/Engine/Engine/UWorld/

 

UWorld

The World is the top level object representing a map or a sandbox in which Actors and Components will exist and be rendered.

docs.unrealengine.com

 

UWorld는 일종의 ULevel의 대표 오브젝트다.

하나의 Persistent Level과 함께 월드 볼륨상에서 로드되거나 취소될 수 있는 Streaming Level을 사용할 수 있고, 여러가지 Level들을 WorldComposition으로 정리해 사용할 수 있게 해준다.

Stand Alone Game에서는 일반적으로 하나의 World만 존재하나, 에디터 안에서는 많은 World들이 수정될 수 있다.

 

ULEVEL

https://docs.unrealengine.com/4.27/en-US/API/Runtime/Engine/Engine/ULevel/

 

ULevel

The level object.

docs.unrealengine.com

말 그대로 Level 오브젝트이다.

Level 내에 존재하는 Actor 리스트나 BSP 정보, Brush List들을 들고 있다.

모든 Level은 Outer로 World을 가지고 있고, Persistent Level로 지정될 수 있으며, 여러 Sub Level들을 가질 수 있다.

 

몇가지 용어 설명을 추가하자면

Persistent Level: 아무것도 없을 때에 호출되는 기본 Level.

Sub Level: Persistent Level에서 일정부분을 담당하는 Level. 지역 뿐 아니라 사운드나 레이아웃 등 기능 별로도 분리가 가능하다. 이는 동시 작업이 불가한 Binary 파일인 레벨 작업에서 최소한의 동시작업을 지원한다.

 

즉 UWorld와 ULevel들의 관계는 대략 이런 식인 것이다.

이에 대한 부분은 차후 UWorld를 공부 할 때 자세히 다뤄보겠다.

 

본론으로 돌아가, FWorldContext는 위에서 우리가 길게 핥아본 UWorld를 관리하는 구조체이다.

이제 코드 부분을 다시 살펴보자.

더보기
USTRUCT()
struct FWorldContext
{
	GENERATED_USTRUCT_BODY()

	/**************************************************************/
	
	TEnumAsByte<EWorldType::Type>	WorldType;

	FSeamlessTravelHandler SeamlessTravelHandler;

	FName ContextHandle;

	/** URL to travel to for pending client connect */
	FString TravelURL;

	/** TravelType for pending client connects */
	uint8 TravelType;

	/** URL the last time we traveled */
	UPROPERTY()
	struct FURL LastURL;

	/** last server we connected to (for "reconnect" command) */
	UPROPERTY()
	struct FURL LastRemoteURL;

	UPROPERTY()
	TObjectPtr<UPendingNetGame>  PendingNetGame;

	/** A list of tag/array pairs that is used at LoadMap time to fully load packages that may be needed for the map/game with DLC, but we can't use DynamicLoadObject to load from the packages */
	UPROPERTY()
	TArray<struct FFullyLoadedPackagesInfo> PackagesToFullyLoad;

	/**
	 * Array of package/ level names that need to be loaded for the pending map change. First level in that array is
	 * going to be made a fake persistent one by using ULevelStreamingPersistent.
	 */
	TArray<FName> LevelsToLoadForPendingMapChange;

	/** Array of already loaded levels. The ordering is arbitrary and depends on what is already loaded and such.	*/
	UPROPERTY()
	TArray<TObjectPtr<class ULevel>> LoadedLevelsForPendingMapChange;

	/** Human readable error string for any failure during a map change request. Empty if there were no failures.	*/
	FString PendingMapChangeFailureDescription;

	/** If true, commit map change the next frame.																	*/
	uint32 bShouldCommitPendingMapChange:1;

	/** Handles to object references; used by the engine to e.g. the prevent objects from being garbage collected.	*/
	UPROPERTY()
	TArray<TObjectPtr<class UObjectReferencer>> ObjectReferencers;

	UPROPERTY()
	TArray<struct FLevelStreamingStatus> PendingLevelStreamingStatusUpdates;

	UPROPERTY()
	TObjectPtr<class UGameViewportClient> GameViewport;

	UPROPERTY()
	TObjectPtr<class UGameInstance> OwningGameInstance;

	/** A list of active net drivers */
	UPROPERTY(transient)
	TArray<FNamedNetDriver> ActiveNetDrivers;

	/** The PIE instance of this world, -1 is default */
	int32	PIEInstance;

	/** The Prefix in front of PIE level names, empty is default */
	FString	PIEPrefix;

	/** The feature level that PIE world should use */
	ERHIFeatureLevel::Type PIEWorldFeatureLevel;

	/** Is this running as a dedicated server */
	bool	RunAsDedicated;

	/** Is this world context waiting for an online login to complete (for PIE) */
	bool	bWaitingOnOnlineSubsystem;

	/** Handle to this world context's audio device.*/
	uint32 AudioDeviceID;

	/** Custom description to be display in blueprint debugger UI */
	FString CustomDescription;

	// If > 0, tick this world at a fixed rate in PIE
	float PIEFixedTickSeconds  = 0.f;
	float PIEAccumulatedTickSeconds = 0.f;

	/**************************************************************/

	/** Outside pointers to CurrentWorld that should be kept in sync if current world changes  */
	TArray<UWorld**> ExternalReferences;

	/** Adds an external reference */
	void AddRef(UWorld*& WorldPtr)
	{
		WorldPtr = ThisCurrentWorld;
		ExternalReferences.AddUnique(&WorldPtr);
	}

	/** Removes an external reference */
	void RemoveRef(UWorld*& WorldPtr)
	{
		ExternalReferences.Remove(&WorldPtr);
		WorldPtr = nullptr;
	}

	/** Set CurrentWorld and update external reference pointers to reflect this*/
	ENGINE_API void SetCurrentWorld(UWorld *World);

	/** Collect FWorldContext references for garbage collection */
	void AddReferencedObjects(FReferenceCollector& Collector, const UObject* ReferencingObject);

	FORCEINLINE UWorld* World() const
	{
		return ThisCurrentWorld;
	}

	FWorldContext()
		: WorldType(EWorldType::None)
		, ContextHandle(NAME_None)
		, TravelURL()
		, TravelType(0)
		, PendingNetGame(nullptr)
		, bShouldCommitPendingMapChange(0)
		, GameViewport(nullptr)
		, OwningGameInstance(nullptr)
		, PIEInstance(INDEX_NONE)
		, PIEWorldFeatureLevel(ERHIFeatureLevel::Num)
		, RunAsDedicated(false)
		, bWaitingOnOnlineSubsystem(false)
		, AudioDeviceID(INDEX_NONE)
		, ThisCurrentWorld(nullptr)
	{ }

private:

	UWorld*	ThisCurrentWorld;
};

 

WorldType

WorldType은 현재 World가 어떤 케이스인지 지칭한다.

타입인 EWorldType은 다음과 같다.

/** Specifies the goal/source of a UWorld object */
namespace EWorldType
{
	enum Type
	{
		/** An untyped world, in most cases this will be the vestigial worlds of streamed in sub-levels */
		None,

		/** The game world */
		Game,

		/** A world being edited in the editor */
		Editor,

		/** A Play In Editor world */
		PIE,

		/** A preview world for an editor tool */
		EditorPreview,

		/** A preview world for a game */
		GamePreview,

		/** A minimal RPC world for a game */
		GameRPC,

		/** An editor world that was loaded but not currently being edited in the level editor */
		Inactive
	};
}

여기서 잘 모르는 것이 몇 있다.

Preview가 붙은 것은 오브젝트나 머테리얼에서 미리보기를 하는 World들을 지칭한다.

 

SeamlessTravelHandler

Seamless World Travel을 관리하는 구조체의 변수이다.

https://docs.unrealengine.com/5.0/ko/travelling-in-multiplayer-in-unreal-engine/

 

멀티플레이어에서의 이동(Travel)

멀티플레이어에서의 travel, 이동 작동방식에 대한 개요입니다.

docs.unrealengine.com

구조체인만큼 추가적으로 확인해야 할 사항이 많기 때문에 차후 추가로 공부하는 기회를 갖도록 하겠다.

 

ContextHandle

FWorldContext를 구분짓는 일종의 Key이다.

이 값으로 WorldContext를 검색한다.

 

TravelURL

아래 포스트에서 어느정도 설명을 했으나 한번 더 짚어보겠다.

https://redchiken.tistory.com/310

 

UGameplayStatics::OpenLevel 분석 - 1

UGamePlayStatics::OpenLevel은 C++에서 레벨을 변경하고자 할 때 사용하는 함수이다. /** * Travel to another level * * @param LevelName the level to open * @param bAbsolute if true options are reset, if..

redchiken.tistory.com

TravelURL은 client connect를 할 Level의 URL이다.

형식은 [LevelName]?[Option]으로 구성되어 있다.

 

TravelType

TravelURL과 마찬가지로 어떤 형태로 Travel을 할지에 대한 정보이다.

enum ETravelType
{
    TRAVEL_Absolute,
    TRAVEL_Partial,
    TRAVEL_Relative,
    TRAVEL_MAX,
}

Absolute와 Relative는 각각 절대경로, 상대경로로 이해하면 된다.

하지만 Partial은 어떤 경로인지 감이 잘 안잡힐 수 있다.

TRAVEL_Partial의 의미를 유추할 수 있는 코드 부분은 UEngine::HandleDisconnect이다.

void UEngine::HandleDisconnect( UWorld *InWorld, UNetDriver *NetDriver )
{
	// There must be some context for this disconnect
	check(InWorld || NetDriver);

	// If the NetDriver that failed was a pending netgame driver, cancel the PendingNetGame
	CancelPending(NetDriver);

	// InWorld might be null. It might also not map to any valid world context (for example, a pending net game disconnect)
	// If there is a context for this world, setup client travel.
	if (FWorldContext* WorldContext = GetWorldContextFromWorld(InWorld))
	{
		// Remove ?Listen parameter, if it exists
		WorldContext->LastURL.RemoveOption( TEXT("Listen") );
		WorldContext->LastURL.RemoveOption( TEXT("LAN") );

		// Net driver destruction will occur during LoadMap (prevents GetNetMode from changing output for the remainder of the frame)
		SetClientTravel( InWorld, TEXT("?closed"), TRAVEL_Absolute );
	}
	else if (NetDriver)
	{
		// Shut down any existing game connections
		if (InWorld)
		{
			// Call this to remove the NetDriver from the world context's ActiveNetDriver list
			DestroyNamedNetDriver(InWorld, NetDriver->NetDriverName);
		}
		else
		{
			NetDriver->Shutdown();
			NetDriver->LowLevelDestroy();

			// In this case, the world is null and something went wrong, so we should travel back to the default world so that we
			// can get back to a good state.
			for (FWorldContext& PotentialWorldContext : WorldList)
			{
				if (PotentialWorldContext.WorldType == EWorldType::Game)
				{
					FURL DefaultURL;
					DefaultURL.LoadURLConfig(TEXT("DefaultPlayer"), GGameIni);
					const UGameMapsSettings* GameMapsSettings = GetDefault<UGameMapsSettings>();
					if (GameMapsSettings)
					{
						PotentialWorldContext.TravelURL = FURL(&DefaultURL, *(GameMapsSettings->GetGameDefaultMap() + GameMapsSettings->LocalMapOptions), TRAVEL_Partial).ToString();
						PotentialWorldContext.TravelType = TRAVEL_Partial;
					}
				}
			}
		}
	}
}

마지막 부분을 보면 주석으로 "무언가 잘못 되었을 때 이전 World로 되돌린다"는 설명이 되어있다.

그리고 이 때 TravelType은 TRAVEL_Partial로 지정되어 있다.

이를 통해 TRAVEL_Partial은 일부 정보만 리셋하는 것으로 예상해볼 수 있다.

실제 도큐먼트에서도 carry name, reset server로 기재가 되어있기도 하구 말이다.

 

LastURL

가장 마지막에 접근했던 Level의 URL이다.

 

LastRemoteURL

가장 마지막에 connect 했던 Level의 URL이다.

일반적으로는 사용하지 않지만 reconnect를 할 때에는 이 값을 저장한다.

 

PendingNetGame

이 변수에 대해서는 이렇다 할 정보를 찾지 못했다.

우선 UPendingNetGame 타입이라 별도 클래스이므로 나중에 추가로 확인해보도록 하겠다.

 

PackagesToFullyLoad

 

'UE5 > Level' 카테고리의 다른 글

[Level] UGameplayStatics::OpenLevel 분석 - 1  (0) 2022.07.02

+ Recent posts