KoreanFoodie's Study

언리얼 비동기 애셋 로딩 (Asynchronous Loading) 본문

Game Dev/Unreal C++ : Study

언리얼 비동기 애셋 로딩 (Asynchronous Loading)

GoldGiver 2022. 4. 4. 13:30

FSoftObjectPath 와 TSoftObjectPtr

사용할 때마다 로드하는 것이 아니라, 애셋을 전부 로딩하지 않으면서 로딩하고 싶은 애셋을 미리 준비시켜 놓고 싶다면 어떨까? 그럴 경우, FSoftObjectPath 와 TSofrObjectPtr 를 이용하면 된다.

FSoftObjectPath 는 실제로 애셋의 경로를 FName 으로 저장하고 있는 구조체이며, TSoftObjectPtr 는 FSoftObjectPath 를 wrapping 한 TWeakObjectPtr 이다. TSoftObjectPtr.Get( ) 으로 참조된 애셋을 가져올 수 있으며, FSoftObjectPath 로 애셋을 로딩할 수 있다.

 

The Asset Registry and Object Libraries

애셋 레지스트리는 콘텐츠 브라우저에 표시되는 애셋을 관리할 수 있는 시스템이다. 로딩되지 않은 애셋을 메타데이터를 참조하고 싶을 때는, property 에 "AssetRegistrySearchable" 태그를 추가하면 된다.

ObejctLibrary 를 사용하면 로드된 애셋 혹은 로드되지 않은 애셋의 FAssetData 를 참조할 수 있는데,
일일히 애셋을 추가하지 않고서도, 해당 경로에 있는 애셋을 전부 추가할 수 있다!

if (!ObjectLibrary)
{
       ObjectLibrary = UObjectLibrary::CreateLibrary(BaseClass, false, GIsEditor);
       ObjectLibrary->AddToRoot();
}
ObjectLibrary->LoadAssetDataFromPath(TEXT("/Game/PathWithAllObjectsOfSameType");
if (bFullyLoad)
{
       ObjectLibrary->LoadAssetsFromAssetData();
}

위의 코드에서는 LoadAssetDataFromPath 의 인자값 디렉토리에 있는 애셋들을 LoadAssetsFromAssetData 를 통해 로딩하고 있다.

애셋 데이터를 불러들인 이후에 실제 전체 애셋을 로딩할지를 qeury 를 통해 선택할 수 있다.

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;
       }
}

위의 코드에서는 FooType 을 가진 오브젝트 라이브러리를 찾고 있다.

 

 

StreamableManager and Asynchronous Loading

이제 로딩된 애셋을 통해 비동기적으로 애셋을 로드해 보자.

먼저, FStreamableManager 를 선언해야 하는데, 이는 싱글톤 클래스로 지정된 오브젝트에 넣는 것이 좋다. 물론 동기적으로 애셋을 로드할 수도 있겠지만, Synchromous 를 이용해 동기적으로 애셋을 로드하면 메인 쓰레드가 오랫동안 다른 작업을 하지 못할 수 있다. Asynchronous 하게 애셋을 로딩하는 예시 코드를 보자.

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);
              }
       }
}

UGameGlobals 의 싱글톤 클래스에 선언된 StreamableManager 를 불러와 RequestAsyncLoad 함수를 호출하고, 델리게이트를 생성해 바인딩해 주었다. RequestAsyncLoad 함수에서 첫 번째 인자의 ItemToStream 애셋의 로딩이 완료되면 바인딩된 델리게이트가 호출될 것이다.

StreamableManager 는 델리게이트가 호출될 때까지 애셋을 하드 참조하며, 델리게이트가 호출된 이후에는 레퍼런스를 해제한다. 따라서 해당 애셋이 사라지지 않게 하려면 다른 곳에서 하드 참조를 해 주어야 한다.

참고문서

Comments