https://dev.epicgames.com/documentation/ko-kr/unreal-engine/versioning-of-assets-and-packages-in-unreal-engine

 

언리얼 엔진의 에셋 및 패키지 버전 관리 | 언리얼 엔진 5.4 문서 | Epic Developer Community

언리얼 엔진에서 커스터마이징된 시리얼라이제이션 코드 및 버전 관리를 사용하여 에셋과 패키지에서 오브젝트를 로드하는 방법을 제어합니다.

dev.epicgames.com

  • Unreal Engine에는 Engine과 Engine Code, Data 사이의 호환성을 정의하거나 보증하는
    다양한 Version Control System을 제공한다.
    • 이를 이용하면 호환 불가능한 Code나 Data 감지가 가능한다.
    • 여기에 더불어, Custom Serialization 및 Convert Data를 관리할 수 있다.

Engine Version

  • Unreal Engine에서 가장 수준 높고 사용자 가시성이 뛰어난 Version Control
  • Editor 내 Help 메뉴 -> About Unreal Editor 섹션에서 확인 가능
  • 엔진 버전은 다음과 같이 구성되어 있으며, FEngineVersion/FEngineVersionBase 클래스에서 확인할 수 있다.
    • 더보기
      [MajorVersion].[MinorVersion].[PatchVersion].[ChangeListNumber][BranchName]
      5.0.3.20979098+++UE5+Release-5.0
    • Engine Version을 구성하는 각 Component는 상위 값이 증가하지 않는 한 값이 감소하지 않는다.
      • 달리 말하면, 두 Engine Version의 Component별로 비교하면 어느 Version이 최신인지 파악할 수 있다.
    • Major, Minor, Patch Version이 같은 경우,
      Epic의 Changelist보다 이용자가 생성한 Changelist를 항상 최근 버전으로 간주한다.
    • BranchName은 호환성 검사에 사용하지 않는다.
      • 주로 외부에 표시하는 용도
  • Engine Version이 호환되지 않으면 Engine에서 Asset을 Load 할 수 없다.
    • 최신 Engine Version에서 저장된 Asset은 Contents Browser에 나타나지 않는다.
    • 해당 Asset에 Reference는 null로 취급된다.
      • 이 때 Asset을 저장하면 인지하지 못한 Reference가 오염되기 때문에 저장하지 않는 것이 좋다.
  • Changelist 0이 포함된 Engine Version은 다른 모든 Engine Version과 호환되는 것으로 간주한다.
    • Engineer 입장에서는 Engine 개발이 간편해지는 측면이 있지만, Disk에 저장하는 관점에서는 부적합하다.
    • 대신 Contents Creator는 배포된 Editor의 Binary Build를 사용해야 한다.
      • 그래야 정확한 Engine Version Data를 지닌 Asset으로 작업을 하게 된다.
      • 또한 Asset으로 인한 Data 손실을 방지할 수 있다.

Asset and Package

  • Asset은 저장에 사용한 Editor의 Engine Version 정보가 포함되어 있다.
  • Engine은 Load 시 이 값을 확인하고,
    Code 변경을 통해 Serialize 된 Data에 추가되거나 제거된 Property를 적절히 처리할 수 있다.
  • Serialize를 해제하는 중(ex. Asset을 Load 하는 경우)
    • Field를 제거하는 경우에는 해당 Field가 인식되지 않고 무시된다.
    • Field를 추가하는 경우에는 Data에서는 없어지지만, 기본값을 유지한다.
  • Asset의 Data를 Disk에 저장하기 위해 Serialize 하는 경우
    • 삭제된 Field의 Data는 바로 폐기 된다.
    • 새로 추가된 Field의 Data는 유지되어 다른 Property와 함꼐 Serialize된다.
  • 이 결과 Data 구조는 서서히 변경되며,
    해당 Data 구조의 Serialized Expression은 새 Field를 추가하는 동안 필요 없는 Field는 자연스럽게 폐기가 된다.
  • 따라서 Serialize 된 Data는 Engine Version과 무관하게 동일하거나 그 이상의 Version에서만 인식할 수 있다.
  • 이 규칙을 통해 이전 Engine Version의 Data 손실 Error를 방지할 수 있다.
    • 최근 Version의 Property를 폐기하고 Data를 다시 Disk상에 Serialize 하면서 발생함.

Programming by Engine Version

 

FEngineVersion

Utility functions.

docs.unrealengine.com

  • Unreal Game Sync를 통해 동기화되지 않은 Code-based Engine Build가 있는 경우,
    이 Build의 ChangeList를 0으로 표시하면 Version Check와 관련해 훨씬 유연한 환경을 만들 수 있다.
  • 하지만 Cooking 과정에서는 해당 Asset에서 Warning이 발생하므로 사용을 지양하는 것이 좋다.
    • 만약 이 Warning을 비활성화 하려면,
      DefaultEngine.in 파일의 [Core.System] Section에 ZeroEngineVersionWarning=0을 추가하면 된다.

Engine Version Update

  • Build의 Engine Version은 Engine 내 /Build/Build.version 파일에서 정의한 값으로 제어할 수 있다.
  • 이 파일을 직접 수정한 뒤, 다음 2가지 방법으로 Version을 Update 할 수 있다.
    • UpdateLocalVersion Commandlet 실행
    • UGC(Unreal Game Sync)로 Sychronize
  • 더보기
    {
    	"MajorVersion": 4,
    	"MinorVersion": 26,
    	"PatchVersion": 2,
    	"Changelist": 0,
    	"CompatibleChangelist": 14830424,
    	"IsLicenseeVersion": 0,
    	"IsPromotedBuild": 0,
    	"BranchName": "++UE4+Release-4.26"
    }
    • Changelist
      • 수동으로 정의
      • 수동으로 업데이트 하거나, UpdateLocalVersion Commandlet으로 Update
      • Update 시 디포 Directory Name을 사용하고 /를 +로 교체
    • CompatibleChangelist
      • 해당 Engine Version과 호환 가능한 것으로 간주할 수 있는 가장 낮은 Changelist
        • 가장 처음 만들어진 Changelist
      • Engine의 Local Build에서처럼 0으로 설정할 때도 있다.
        • 하지만 정식 출시된 Epic의 Build나 내부적으로 배포된 Build가 0이 아니므로
          Local Version과 호환되지는 않는다.
    • IsLicenseeVersion
      • Source Control에서 Synchronize 된 Local Build에 사용할 경우 0으로 지정
      • UpdateLocalVersion Commandlet을 통해 System Build를 할 경우 1
      • Promoted Build에서 사용할 때에는 1이 더 적절하다.
      • Version Check가 더 까다로워지는 대신 Hot Reload가 비활성화 된다.
    • IsPromotedBuild
      • 해당 Build가 Promoted Build인지 여부
      • 기본 값은 1
    • BuildId
      • 위 예시에는 없지만 제공되는 정보
      • Compile 할 때 Engine 및 Module에 전부 이 값을 Tag 한다.
        • Engine에서는 이 값이 없는 모든 Module은 호환 불가능한 대상으로 간주
      • 보통은 공백으로 두는 편이 좋다.
        • 공백으로 둘 경우 Compile 할 때마다 고유한 새 값이 생성된다.

UpdateLocalVersion Commandlet

  • 전용 Build Machine에서 Compile할 때처럼 Build/Build/version 파일을 수동으로 편집 할 필요가 없는 경우에 유용
  • 더보기
    RunUAT.bat UpdateLocalVersion [-CL=INT] [-CompatibleCL=INT] [-Build=STRING] [-SkipHeader] [-Licensee] [-Promoted=BOOL] [-Branch=STRING]
  • CL
    • Build.Version의 Changelist
  • CompatibleCL
    • Build.Version의 CompatibleChangelist
  • Build
    • Source Code의 BUILD_VERSION 매크로를 Update해 Engine의 Build Version String을 변경
  • SkipHeader
    • 이 Parameter가 있으면 Engine의 Header File이 Update 되지 않는다.
  • License
    • Build.version의 IsLicense
    • 이 Parameter가 있는 경우 true로 취급
  • Promoted
    • Build.version의 IsPromotedBuild
    • 0 이외의 정수를 입력하면 true로 간주하고 Field에 1이 입력
  • Branch
    • Build.Version의 BranchName

Object Version

  • UObject 파생 Class는 Engine Version 외에 Object Version Control System을 별개로 운영한다.
    • Engine Level Version
    • Object Level Version
  • Object Serialization은 다음의 용도로 사용된다.
    • 대량의 Burk Data를 포함한 Asset Performance Optimize
    • 단위 변환 시 Data Format 변경을 구현
    • Object Data를 압축된 Format의 Disk에 저장할 때 공간 절약을 위한 Customize
  • Custom Serialization을 위해서는 다음 2가지 작업이 필요하다.
    • UObject::Serialize 함수를 Override하여 Project의 필요에 따라 Data Format 변경
    • 신규 Code의 Object Version에 기반한 검사
  • Serialization이 변경 되어도 하위 호환성을 보존하면서, Engine Version과 같은 방식으로 Data 손실을 방지할 수 있다.

Serialization and Version Control of Engine Level

  • Engine Level에서는 EUnrealEngineObjectUE5Version을 사용해 Serializer Function의 Version을 관리한다.
    • Custom Serializer가 변경될 때마다 새 항목이 EUnrealEngineObjectUE5Version에 추가된다.
  • Epic Games가 Public Unreal Engine Launch Version에서 이 Type을 수정하면,
    병렬 Global EnumType인 EUnrealEngineObjectLicenseeUEVersion은 사용자가 자체적으로
    Engine Level Version 관리를 추가할 수 있다.
  • 이후 Package를 저장하면 두 Enum값이 증가한 채로 저장이 된다.
    • Serializer, Deserializer Code는 이 값으로 Data Read/Write 방식을 결정한다.
  • Engine에서는 자동으로 이 Version의 값을 자동으로 자체검사한다.
    • Engine보다 높은 Version이 있는 Package는 Load 할 수 없다.
  • 이 방식은 Engine의 여러 분야를 별개의 팀이 작업하는 곳에서는 적합하지 않다.
    • 다수의 Version에서 Enum Type을 업데이트 할 수 없다.
    • Merge 시 상수를 다시 정렬하는 동안 해당 Version Number로 저장한 Asset이 손상되거나 무효화 된다.
    • 이 경우에는 Engine Level이 아닌 Object Level의 Serialization이 더 적절하다.

Serialization and Version Control of Object Level

  • Programmer들이 동시에 각자 다른 Branch로 개발하는 경우,
    Engine이 FGuid 기반 Custom Version을 통해 Object Level Version Control을 제공해야 한다.
  • FGuid 구조에는 다른 Version과 같이 증가하는 정수형 Version Number 뿐 아니라,
    팀의 요구에 따라 동시에 다양한 Custom Version을 보유할 수 있는 GUID(Global Unique Identifier)도 제공한다.
  • System이나 Branch별로 Custom Version을 유지하면 다른 Branch의 Code와 병합할 때 Conflict가 발생하지 않는다.
  • 더보기
    const FGuid FAnimationCustomVersion::GUID(0x2EB5FDBD, 0x01AC4D10, 0x8136F38F, 0x3393A5DA); 
    
    // 코어로 커스텀 버전 등록 
    FCustomVersionRegistration GRegisterAnimationCustomVersion(FAnimationCustomVersion::GUID, FAnimationCustomVersion::LatestVersion, TEXT("AnimGraphVer"));
 

FArchive

Base class for archives that can be used for loading, saving, and garbage collecting in a byte order neutral way.

docs.unrealengine.com

  • Custom Code에서는 등록된 FGuid에 있는 Version Number를 고려해 Data의 Read/Write 방식을 결정한다.
  • 이런 식으로 등록된 FGuid와 연결된 Version Number는 절대 감소하지 않는다.
    • 이를 통해 Engine은 신규 Version의 Asset을 Load하지 않도록 할 수 있다.

Custom Serialization Function

  • Object는 Serialize를 override 하여 Disk에 있는 정밀 Data 표현을 제어할 수 있다.
    • FArchive를 이용해 Read/Write를 수행하므로 Data Format을 한번만 배치하면 된다.
  • 아래는 FArchive에서 Serialization Code에서의 Version based logic을 작성할 때 유용한 함수들이다.
  • LicenceUEVer에 대한 UEVer 호출을 변경할 경우,
    Epic의 공식 Version Number가 아니라 사용자의 Version Number를 사용하도록 코드가 변경된다.
    • 자체 Version의 Unreal Engine을 따로 관리하는 Epic 외 사용자에게 사용을 권장한다.
  • https://dev.epicgames.com/documentation/en-us/unreal-engine/API/Runtime/Core/Serialization/FArchiveState?application_version=5.2
 

F Archive State | Unreal Engine 5.4 Documentation | Epic Developer Community

 

dev.epicgames.com

 

F Archive | Unreal Engine 5.4 Documentation | Epic Developer Community

Base class for archives that can be used for loading, saving, and garbage collecting in a byte order neutral way.

dev.epicgames.com

예시

  • PhysXVehicles Plugin의 WheeledVehicle은 Engine Object Version 및 Custom Version을 모두 사용하고,
    실제 코드를 변경하기 전에 만들어진 Asset과도 하위 호환성을 유지한다.
  • 더보기
    void UWheeledVehicleMovementComponent::Serialize(FArchive& Ar)
    {
    	Super::Serialize(Ar);
    	Ar.UsingCustomVersion(FFrameworkObjectVersion::GUID);
    	if (Ar.CustomVer(FFrameworkObjectVersion::GUID) < FFrameworkObjectVersion::WheelOffsetIsFromWheel)
    	{
    		bDeprecatedSpringOffsetMode = true;
    	}
    }
    • 여기서 Custom Version은 WheelSuspension을 표현하는 Physics Spring Object에 대한
      Offset의 계산 방식 변경 변수를 설정할 때 사용된다.
    • 사용된 Version 값은 Physics Code를 관리하는 팀에서 제작한 Enum Type을 가져온다.
  • 더보기
    void FConstraintInstance::PostSerialize(const FArchive& Ar)
    {
    #if WITH_EDITORONLY_DATA
    	// ... 
    	if (Ar.IsLoading() && Ar.UEVer() < VER_UE_FIXUP_MOTOR_UNITS)
    	{
    		AngularVelocityTarget = 1.f / (2.f PI);
    	}
    	// ...
    #endif
    }
    • Angular Velocity가 RPS 대신 RPM으로 발생하는 물리적 변화에 대응해 단위 전화을 수행한다.
    • Angular Velocity는 단위가 없는 부동소수점으로 저장되므로 낮은 Version에서는 Load 중 변환이 필요하다.
      • 이 경우 Asset Load 할 때 Angular Velocity만 수정하면 된다.
    • 이 Type으로 저장되는 모든 Asset은 이미 RPM으로 저장되며,
      다음에 Load 할 때 Code를 변환할 필요가 없도록 현재 Engine Version Number로 저장한다.

Binary Version Control

 

언리얼 엔진의 바이너리 버전 관리 방법 | 언리얼 엔진 5.4 문서 | Epic Developer Community

오래된 DLL 파일로 인해 발생할 수 있는 잠재적인 오류를 완화하는 언리얼 엔진의 빌드 ID 시스템 레퍼런스입니다.

dev.epicgames.com

 

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

[Architecture] Command-line Arguments  (0) 2024.05.07
[Architecture] Config File  (0) 2024.05.06
[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/command-line-arguments-in-unreal-engine

 

언리얼 엔진의 명령줄 실행인자 | 언리얼 엔진 5.4 문서 | Epic Developer Community

명령줄 실행인자란 엔진 실행 파일에 전달하여 시작 시 엔진 실행 방식을 커스터마이징할 수 있는 추가 실행인자입니다.

dev.epicgames.com

  • Additional Launch Parameters라고도 불린다.
  • 시작 시 Engine의 실행 방식 Customize 한다.
  • Project를 Test 및 Optimize하기에 유용한 툴
  • 이러한 Setting은 전반적인 조작부터 상세한 옵션까지 그 범위가 다양하다.
    • Unreal Editor가 Full Editor Mode가 아닌 GameMode로 실행 되도록 장제
    • 특정 맵이 게임 내에서 특정 해상도 및 Frame Rate로 실행되도록 선택

Command로 Command-line Argument 전달

  • 더보기
    <EXECUTABLE> [URL_PARAMETERS] [ARGUMENTS]
    • EXECUTABLE
      • 실행파일 이름
    • URL_PARAMETERS
      • URL 파라미터
    • ARGUMENTS
      • Command Flag 또는 Key-Value Pair
  • 더보기
    UnrealEditor.exe MyGame.uproject /Game/Maps/BonusMaps/BonusMap.umap?game=MyGameMode -game -fullscreen

Unreal Editor로 Command-line Argument 전달

  • Unreal Editor는 Command-line Argument로 Standalone Game Customize를 지원한다.
    • Editor에서 Command-line Argument는 Additional Launch Parameter라 불린다.
    • 이 옵션은 Play in Standalone Game 모드에서만 지원된다.
  • 또한 Multiplay Game Test 목적으로 Dedicated Server로만 Command-line Argument를 전달하는 작업도 지원한다.
  • Dedicated Server를 위한 Command-line Argument는 다음과 같다.
    • Server Map Name Override
      • Map Name을 URL Parameter로 전달
    • Additional Server Game Options
      • 추가 URL Parameter 전달
    • Additional Server Launch Parameters
      • URL Parameter 외의 추가 Command-line Flag, Key-Value Pair를 전달
  • Edit -> Editor Preference을 통해 커스터마이즈 가능

Game Launch Parameter

  • Edit -> Editor Preferences -> Level Editor -> Play로 이동해 Play in Standalone Game 이름의 Section 탐색
  • 이 Section의 Additional Launch Parameter를 위한 TextBox에 Command-line Argument를 입력한다.

Server Launch Parameter

  1. Edit -> Editor Preferences -> Level Editor -> Play 로 이동해 Multiplayer Options 이름의 Section 탐색
  2. Launch Separate Server 항목을 활성화하고, Run Under One Process 항목을 비활성화
    • 위 조건을 충족해야 Dedicated Server에 대한 Command-line Argument를 지정할 수 있다.
    • Run Under One Process가 비활성화 된 경우,
      각 Client가 Editor의 개별 Instance를 Spawn하기 때문에 Client의 실행 속도가 느려진다.
  3. Multiplayer Options Section에서 Server로 이동
    • 이 Section에서 3 종류의 Command-line Argument를 지정할 수 있다.
      • Server Map Name Override
      • Additional Server Game Options
      • Additional Server Launch Parameters

Shortcut으로 Command-line Argument 전달

  1. 실행 파일에 대한 Shrotcut 생성
  2. Shortcut을 우클릭하여 Properties 선택
  3. Properties의 Shortcut 이름의 Section에서 Target 이름의 Field  끝에 Command-line Arrgument 추가
  4. 이후 해당 Shortcut을 실행할 경우, 원본 실행 파일로 Command-line Argument가 전달된다.

Custom Command-line Argument 생성

  • Comamnd-line에 원하는 Flag나 Key-Value Pair를 전달하여 자신만의 Argument를 생성할 수 있다.
    • 이를 위해서는 Code 내 Command-line에서 해당 Argument를 읽어야만 한다.
    • Project Code가 Custom Command-line Argument를 읽지 않고 파싱하는 경우,
      해당 Argument는 무시된다.

Flag

  • 더보기
    UnrealEditor.exe MyGame.uproject -game
    • 위 예시에서 -game가 Flag이다.
    • -game은 대상을 Game Mode로 실행하고 싶다는 의미이다.

Flag Parsing

  • 더보기
    UnrealEditor.exe MyGame.uproject -myflag

     

    bool bMyFlag = false;
    if (FParse::Param(FCommandLine::Get(), TEXT("myflag")))
    {
    	bMyFlag = true;
    }

Key-Value Pair

  • 더보기
    UnrealEditor.exe MyGame.uproject -game -windowed -ResX=1080 -ResY=1920
    • Key-Value Pair는 특정 값을 지정하는 Setting Switch이다.
      • Switch에는 현재 상태 뿐 아니라 setting도 동반되어야 한다.

Key-Value Pari Parsing

  • 더보기
    UnrealEditor.exe MyGame.uproject -mykey=42

     

    int32 myKeyValue;
    if (FParse::Value(FCommandLine::Get(), TEXT("mykey="), myKeyValue))
    {
    	// 프로그램이 이 'if' 명령문을 입력하는 경우 mykey가 명령줄에 존재했던 것입니다
    	// myKeyValue는 이제 명령줄을 통해 전달된 값을 포함합니다
    }
  • Engine/Source/Runtime/Core/Public/Misc/CommandLine.h에서
    Command-line과 상호작용 할 수 있는 함수들에 대한 더 자세한 정보를 확인할 수 있다.

Command-line Argument Reference

URL Parameter

  • URL Parameter는 Game이 시작될 때 특정 Map을 로드하도록 강제한다.
  • 비록 선택사항이지만, 제공될 경우에는 실행 파일 이름을 따르거나 존재하는 모든 Flag를 따른다.

Map Name

  • Map Directory 내에 위치한다면 어떤 Map Name이든 표현 가능
  • 선택적으로 .umap 확장자를 포함할 수 있음
  • Map Directory 경로 내에 없는 Map을  다음 두가지 조건을 만족해야 한다.
    • 절대 경로를 사용 혹은 Map Driectory 기준의 상대 경로 사용.
    • Map 뒤에 반드시 .umpa 파일 확장자 요구

Server IP Address

  • Server IP Address를 URL Parameter로 사용해 Game Client를 Dedicated Server에 연결 할 수 있다.

Additional Parameter

  • Map Name 혹은 Server IP Address에 덧붙이는 방식으로 Additional Parameter 지정 가능
  • 각 옵션은 ?로 시작하고 =로 값이 설정된다.
  • 옵션 앞에 -를 붙이면 캐시된 URL Option에서 해당 Option이 제거된다.

Example

  • Map Directory에 위치한 Map으로 게임 열기
  • Map Directory 외부에 위치한 Map으로 게임 열기
    • 더보기
      MyGame.exe /Game/Maps/BonusMaps/BonusMap.umap
  • Map Directory 외부에 위치한 맵으로 Unreal Editor에서 Game 열기
    • 더보기
      UnrealEditor.exe MyGame.uproject /Game/Maps/BonusMaps/BonusMap.umap?game=MyGameMode -game
  • Game Client를 Dedicated Server에 연결
    • 더보기
      UnrealEditor.exe MyGame.uproject /Game/Maps/BonusMaps/BonusMap.umap -server -port=7777 -log
      UnrealEditor.exe MyGame.uproject 127.0.0.1:7777 -game -log

Flag and Key-Value Reference

Read Command-line Argument from File

  • 다음 상황에서 매우 유용함
    • 매우 많은 수의 Command-line Argument를 사용하는 경우
    • 동일한 Argument들을 자주 재사용하는 경우
    • Windows Command line 길이 제한에 걸리는 경우
  • 더보기
    <EXECUTABLE> -CmdLineFile=ABSOLUTE\PATH\TO\FILE.txt

유용한 Command-line Argument

  • Common
  • Rendering 및 GPU
  • CPU 및 Memory
  • Debugging

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

[Architecture] Version Control of Asset and Package  (0) 2024.05.07
[Architecture] Config File  (0) 2024.05.06
[Architecture] Task System  (0) 2024.05.06
[Architecture] Programming Subsystem  (0) 2024.05.06
[Architecture] String  (0) 2024.05.06
  • 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

+ Recent posts