FSoftObjectPath 와 TSoftObjectPtr
모든 사용할 때마다 로드하는 것이 아니라, 애셋을 전부 로딩하지 않으면서 로딩하고 싶은 애셋을 미리 준비시켜 놓고 싶다면 어떨까? 그럴 경우, FSoftObjectPath 와 TSofrObjectPtr 를 이용하면 된다.
FSoftObjectPath 는 실제로 애셋의 경로를 FName 으로 저장하고 있는 구조체이며, TSoftObjectPtr 는 FSoftObjectPath 를 wrapping 한 TWeakObjectPtr 이다. TSoftObjectPtr.Get( ) 으로 참조된 애셋을 가져올 수 있으며, FSoftObjectPath 로 애셋을 로딩할 수 있다.
The Asset Registry and Object Libraries
애셋 레지스트리는 콘텐츠 브라우저에 표시되는 애셋을 관리할 수 있는 시스템이다. 로딩되지 않은 애셋을 메타데이터를 참조하고 싶을 때는, property 에 "AssetRegistrySearchable" 태그를 추가하면 된다.
ObejctLibrary 를 사용하면 로드된 애셋 혹은 로드되지 않은 애셋의 FAssetData 를 참조할 수 있는데,
일일히 애셋을 추가하지 않고서도, 해당 경로에 있는 애셋을 전부 추가할 수 있다.
LoadAssetDataFromPath 의 인자값 디렉토리에 있는 애셋들을 LoadAssetsFromAssetData 를 통해 로딩하고 있다.
if (!ObjectLibrary)
{
ObjectLibrary = UObjectLibrary::CreateLibrary(BaseClass, false, GIsEditor);
ObjectLibrary->AddToRoot();
}
ObjectLibrary->LoadAssetDataFromPath(TEXT("/Game/PathWithAllObjectsOfSameType");
if (bFullyLoad)
{
ObjectLibrary->LoadAssetsFromAssetData();
}
애셋 데이터를 불러들인 이후에 실제 전체 애셋을 로딩할지를 qeury 를 통해 선택할 수 있다.
아래 코드는 FooType 을 가진 오브젝트 라이브러리를 찾고 있다.
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;
}
}
StreamableManager and Asynchronous Loading
이제 로딩된 애셋을 통해 비동기적으로 애셋을 로드해 보자.
먼저, FStreamableManager 를 선언해야 하는데, 이는 싱글톤 클래스로 지정된 오브젝트에 넣는 것이 좋다. 물론 동기적으로 애셋을 로드할 수도 있겠지만, Synchromous 를 이용해 동기적으로 애셋을 로드하면 메인 쓰레드가 오랫동안 다른 작업을 하지 못할 수 있다. Asynchronous 하게 애셋을 로딩하는 예시 코드를 보자.
UGameGlobals 의 싱글톤 클래스에 선언된 StreamableManager 를 불러와 RequestAsyncLoad 함수를 호출하고, 델리게이트를 생성해 바인딩해 주었다. RequestAsyncLoad 함수에서 첫 번째 인자의 ItemToStream 애셋의 로딩이 완료되면 바인딩된 델리게이트가 호출될 것이다.
StreamableManager 는 델리게이트가 호출될 때까지 애셋을 하드 참조하며, 델리게이트가 호출된 이후에는 레퍼런스를 해제한다. 따라서 해당 애셋이 사라지지 않게 하려면 다른 곳에서 하드 참조를 해 주어야 한다.
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);
}
}
}
'게임엔진 > Unreal' 카테고리의 다른 글
[Unreal] Programming Subsystem (프로그래밍 서브시스템) (0) | 2023.11.14 |
---|---|
[Unreal] Level Blueprint 를 C++로 접근하기 (0) | 2023.11.06 |
[Unreal] Reflection (리플렉션) (0) | 2023.10.26 |
[Unreal] GetClass 와 StaticClass 의 차이 (0) | 2023.10.26 |
[Unreal] 업/다운캐스팅 (Cast 함수) 동작 원리 (0) | 2023.10.26 |