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

NIKKE를 재밌게 하다가 비슷한 작업이 Unreal에서 가능한지 확인하기 위해 해본 RnD입니다.

제가 알기로 NIKKE는 Spine을 쓰고, Spine은 뼈대에 2D 파츠 이미지를 붙이고 뼈대의 Animation을 통해 동작하는 방식으로 알고 있습니다.

흡시 언리얼 엔진에서 MeshMerge로 각 파츠 Mesh를 하나의 Mesh로 만들고, 안에 Skeletal을 넣은 뒤 Animation이 Skeletal에 동작하는 것과 같이요.

목표는 이와 같이 파츠 별 이미지를 따로 관리하는 것.

그리고 각 애니메이션 작업을 뼈대를 기반으로 하는 것입니다.

 

RnD 해봤던 플러그인은 총 3개입니다.

PaperZD

알아볼 때 가장 그럴듯해 보이던 플러그인이었습니다.

신규 버전에 대한 지원도 잘 되고 기존 언리얼의 Paper2D 기능을 확장하였기에 가장 기대가 컸습니다.

실제로 UE5 5.3 버전에서 잘 동작 하였습니다만 한가지 문제가 발생했습니다.

Paper가 붙었듯, Flip Book 형식으로 2D Animation이 재생된다는 점이었습니다.

메탈슬러그의 프레임 별 작화

이런 식으로 말이죠.

이 말은 각 프레임마다 이미지가 여러 장이 필요하고, 각 이미지는 대상의 모든 파츠를 다 담고 있어야 한다는 의미입니다.

아님 파츠별로 FlipBook을 따로 만들고 이걸 같은 좌표에 중첩해서 출력하거나.

혹은, PaperZD 기능을 확장하여 각 파츠 이미지에 해당하는 Sprite를 여러개 들고 있으며, 각 프레임마다 그 위치와 각도를 컨트롤 할 수 있는 Custom Paper Flip을 제작하는 것입니다.

 

앞의 2가지 방법은 코스트가 너무 높아져서 부적합하고, 세번째는 당장은 할 수 없는 작업이라 판단하였습니다.

렌더링 코드를 잘 아는 것도 아니고....

Creature2D Skeletal and Mesh Tool

Creature2D는 조사했을 때 제가 원하는 기능에 가장 근접한 Plugin이었습니다.

퀄리티도 꽤 높아서 기대를 많이 했는데, 지원 버전이 4.24에 멈춰있었습니다.

자체적으로 5.0 지원을 준비 하는 것 같기는 해서 설치 후 빌드 에러를 수정하고 작업하려 했으나,

지원 기능이 많은 만큼 렌더링 코드에서 많은 크래시가 발생하여 RnD를 중지하였습니다.

Spine Plugin

Spine과 같은 기능을 UE5에서 할 수 있는지 알아보다가 "Spine은 UE5에서 못쓰나?"라는 의문으로 시도했습니다.

알아본 결과 상당히 지원이 잘 되어 있는 것을 확인할 수 있었습니다.

 

https://ko.esotericsoftware.com/spine-ue4

 

spine-ue4 Runtime Documentation

Licensing Please see the Spine Runtimes License before integrating the Spine Runtimes into your applications. To use the spine-ue4 runtime in your Unreal Engine project: Download and install Unreal Engine. *Currently compatible with UE +4.27. Please see th

ko.esotericsoftware.com

이렇게 주의사항도 문서 갱신이 잘 되어 있기도 하구요.

확장자가 다르더라도 파일명이 같으면 인식을 못하니 파일명을 다 다르게 바꿔주라는 주의사항

이 부분만 주의를 한다면 테스트케이스 작업 자체는 매우 간단하게 마무리가 됩니다.

모듈 선언 해주고 값 넣고 세팅만 하면 되더군요.

 

다만 몇가지 주의할 점이 있습니다.

 

첫째, Spine 정보를 .skel과 .json 2가지로 전달할 수 있는데 .skel은 인식은 하지만 동작하지 않습니다.

그러니 Unreal Engine 5에서 Spine Plugin을 쓸 때 정보는 .json을 사용하는 편이 좋습니다.

 

둘째, 위 사진에 언급 한 것처럼 한 Spine에 대한 파일들이 확장자와 무관하게 이름이 모두 달라야 합니다.

조사해보니 5.3으로 올라가면서 json, uasset, spine 등의 이름 구분이 안정적으로 지원되지 않게 되었다고 하네요.

 

 

결과적으로 사실상 PaperZD, Creature2D, Spine 중에서는 Spine이 압도적인 승리를 거둔 셈입니다.

그냥 Spine을 쓰면 되니까요.

 

다만 애로사항도 있습니다.

첫번째로 Spine은 별도의 에디터가 있어서 그 에디터로 작업을 해야 한다는 점.

Unreal Engine은 조립만 해야 합니다.

 

두번째로, 이 때문에 애니메이션 쪽 작업의 디버깅이 불가하다는 점.

순전히 아트의 작업으로 넘어가게 됩니다.

어떻게 보면 좋은건가?

 

일단 튜토리얼 문서에 아직 못 본 항목도 있어서 조금 더 봐야 알 것 같습니다.

이름은 좀 다르겠지만 2편이 생기겠네요

+ Recent posts