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

+ Recent posts