【UE4】加載資源的方式(六)使用StreamableManager進行加載
參考資料&原文鏈接
AssetManager系列之TAssetPtr與FStreamableManager
(UE4 4.20)UE4同步加載和異步加載UObject ----------LoadObject,LoadClass,FStreamableManager
介紹
/** A native class for managing streaming assets in and keeping them in memory. AssetManager is the global singleton version of this with blueprint access */
struct ENGINE_API FStreamableManager : public FGCObject
用於管理流資產並將其保存在內存中的本機類。AssetManager是具有藍圖訪問權的全局單例版本。
首先,需要創建FStreamableManager
,我建議將它放在某類全局游戲單件對象中,如使用GameSingletonClassName
在DefaultEngine.ini
中指定的對象。
PS:如果你還有其他的單例要使用,那么我建議你把單例都弄到GameInstance里面,因為引擎里面只能指定一個單例類。除了把它在引擎里面指定為單例以外,還有一種單例的實現方法,就是在GameInstance里面寫。
先繼承GameInstance,然后在里面搞一個static的FStreamableManager的指針,在CPP里面引入頭文件之后將FStreamableManager初始化為nullptr,然后在重寫GameInstance的Init方法,在里面把FStreamableManager給new出來然后再賦值給FStreamableManager指針變量,在需要的時候Get一下GameInstance在直接點GameInstance就能用了。其他要搞成單例的類也都通用,有機會找篇文章聊一下,這里不多贅述。
PS:在4.26版本中發現已經是全局單例了。這種寫法比較特殊,因為最新的AssetManager里面就帶着有一個StreamableManager,我們可以直接用。也不知道是哪個大佬發現的,還能這么寫- -:
UAssetManager::GetStreamableManager()
建議都用這種寫法,方便快捷。
拿到StreamableManager對象以后就好辦了,可以將FSoftObjectPath
傳遞給它並開始加載。SynchronousLoad
將進行一次簡單的塊加載並返回對象。
同步加載單個資源
/**
* Synchronously load the referred asset and return the loaded object, or nullptr if it can't be found. This can be very slow and may stall the game thread for several seconds.
*
* @param Target Specific asset to load off disk
* @param bManageActiveHandle If true, the manager will keep the streamable handle active until explicitly released
* @param RequestHandlePointer If non-null, this will set the handle to the handle used to make this request. This useful for later releasing the handle
*/
UObject* LoadSynchronous(const FSoftObjectPath& Target, bool bManageActiveHandle = false, TSharedPtr<FStreamableHandle>* RequestHandlePointer = nullptr);
參數解釋:
- 同步加載引用的資源並返回加載的對象,如果找不到,則返回nullptr。這可能會非常慢,並可能會暫停游戲線程數秒。
Target
要加載磁盤的特定資產。bManageActiveHandle
如果為true,管理器將保持流句柄活動,直到顯式釋放。RequestHandlePointer
如果非空,這將把句柄設置為發出此請求的句柄。這對以后釋放句柄很有用。
句柄介紹:
/** A handle to a synchronous or async load. As long as the handle is Active, loaded assets will stay in memory*/
/**同步或異步加載的句柄。只要句柄是Active的,加載的資源就會留在內存中。*/
你可以控制句柄的顯示釋放來控制資產的釋放。
它有幾個包裝的加載函數:
/** Typed wrappers */
template< typename T >
T* LoadSynchronous(const FSoftObjectPath& Target, bool bManageActiveHandle = false, TSharedPtr<FStreamableHandle>* RequestHandlePointer = nullptr);
template< typename T >
T* LoadSynchronous(const TSoftObjectPtr<T>& Target, bool bManageActiveHandle = false, TSharedPtr<FStreamableHandle>* RequestHandlePointer = nullptr)
template< typename T >
TSubclassOf<T> LoadSynchronous(const TSoftClassPtr<T>& Target, bool bManageActiveHandle = false, TSharedPtr<FStreamableHandle>* RequestHandlePointer = nullptr)
示例代碼:
void UWC_TestUI::OnBtnClickCommonBtn_SmSyncOne()
{
FSoftObjectPath Path = FString(TEXT("Texture2D'/Game/UI/Images/InterestingUE4.InterestingUE4'"));
UTexture2D* Img = UAssetManager::GetStreamableManager().LoadSynchronous<UTexture2D>(Path,false,nullptr);
if (Img_SmSync)
{
Img_SmSync->SetBrushFromTexture(Img);
}
}
該方法或許適用於較小對象,但可能會導致主線程長時間停滯。在這種情況下,您將需要使用RequestAsyncLoad
,它將異步加載一組資源並在完成后調用委托。
實際上,FStreamableManager提供了RequestSyncLoad (同步)和 RequestAsyncLoad(異步)的兩個接口。它們的區別是同步方法是立即加載,因此沒有加載完成的回調函數,但是會返回一個TSharedPtr<FStreamableHandle>
,異步方法除了返回TSharedPtr<FStreamableHandle>
還會有一個加載完畢的回調函數,可以使用Lambda表達式在調用異步方法的時候一並傳入。
除了LoadSynchronous
這種方式以外,你還可以用RequestSyncLoad
:
void UWC_TestUI::OnBtnClickCommonBtn_SmSyncOne()
{
FSoftObjectPath Path = FString(TEXT("Texture2D'/Game/UI/Images/InterestingUE4.InterestingUE4'"));
//注意:在資源未完成加載之前代碼會在這一行暫停運行以等待資源加載完成。
TSharedPtr<FStreamableHandle> SyncStreamableHandle = UAssetManager::GetStreamableManager().RequestSyncLoad(Path);
if (SyncStreamableHandle)
{
UTexture2D * UImg2D = Cast<UTexture2D>(SyncStreamableHandle->GetLoadedAsset());
if (UImg2D)
{
Img_SmSync->SetBrushFromTexture(UImg2D);
}
}
}
注意,使用這個方法加載單個資源的時候要用GetLoadedAsset來獲得FStreamableHandle中返回的資源。
同步加載資源組
同步加載一組資源,並返回一個句柄。這可能會非常慢,並可能會暫停游戲線程數秒。
先看聲明:
/**
* Synchronously load a set of assets, and return a handle. This can be very slow and may stall the game thread for several seconds.
*
* @param TargetsToStream Assets to load off disk
* @param bManageActiveHandle If true, the manager will keep the streamable handle active until explicitly released
* @param DebugName Name of this handle, will be reported in debug tools
*/
TSharedPtr<FStreamableHandle> RequestSyncLoad(TArray<FSoftObjectPath> TargetsToStream, bool bManageActiveHandle = false, FString DebugName = TEXT("RequestSyncLoad Array"));
TSharedPtr<FStreamableHandle> RequestSyncLoad(const FSoftObjectPath& TargetToStream, bool bManageActiveHandle = false, FString DebugName = TEXT("RequestSyncLoad Single"));
參數解釋:
TargetsToStream
要加載的資產磁盤。bManageActiveHandle
如果為true,管理器將保持流句柄活動,直到顯式釋放。DebugName
此句柄的名稱,將在調試工具中報告。
示例代碼:
void UWC_TestUI::OnBtnClickCommonBtn_SmSyncGroup()
{
TArray<FSoftObjectPath> Paths;
Paths.AddUnique(FString(TEXT("Texture2D'/Game/UI/Images/InterestingUE4.InterestingUE4'")));
Paths.AddUnique(FString(TEXT("Texture2D'/Game/UI/Images/VS.VS'")));
//注意:在資源未完成加載之前代碼會在這一行暫停運行以等待資源加載完成。
TSharedPtr<FStreamableHandle> SyncStreamableHandle = UAssetManager::GetStreamableManager().RequestSyncLoad(Paths);
if (SyncStreamableHandle)
{
TArray<UObject *>LoadedAssets;
SyncStreamableHandle->GetLoadedAssets(LoadedAssets);
if (LoadedAssets.Num() > 0)
{
UTexture2D * UImg2D;
for (int32 i = 0 ; i < LoadedAssets.Num() ; i ++)
{
UImg2D = Cast<UTexture2D>(LoadedAssets[i]);
if (UImg2D)
{
//Img的名字
FName ImgName = FName( *FString(TEXT("Img_") + FString::FromInt(i)));
//動態創建一個Image
UImage* Image = WidgetTree->ConstructWidget<UImage>(UImage::StaticClass(),ImgName);
Image->SetBrushFromTexture(UImg2D);
HB_SmAsyncGroup->AddChild(Image);
}
}
}
}
}
注意,使用這個方法加載多個資源的時候要用GetLoadedAssets來獲得FStreamableHandle中返回的資源,里面傳入要返回的數組。
異步加載單個資源
示例代碼:
void UWC_TestUI::OnBtnClickCommonBtn_SmAsyncOne()
{
FSoftObjectPath SmSyncGroupTexturePath = TEXT("Texture2D'/Game/UI/Images/VS.VS'");
TSharedPtr<FStreamableHandle> OneHandle = UAssetManager::GetStreamableManager().RequestAsyncLoad(SmSyncGroupTexturePath);
if (OneHandle)
{
UTexture2D * UImg2D = Cast<UTexture2D>(OneHandle->GetLoadedAsset());
if (UImg2D)
{
Img_SmAsync->SetBrushFromTexture(UImg2D);
}
}
}
異步加載資源組
這是主要的可流操作。一個或多個目標對象的請求流。當完成時,將調用委托函數。返回可流句柄。
老規矩,還是直接上代碼:
/**
* This is the primary streamable operation. Requests streaming of one or more target objects. When complete, a delegate function is called. Returns a Streamable Handle.
*
* @param TargetsToStream Assets to load off disk
* @param DelegateToCall Delegate to call when load finishes. Will be called on the next tick if asset is already loaded, or many seconds later
* @param Priority Priority to pass to the streaming system, higher priority will be loaded first
* @param bManageActiveHandle If true, the manager will keep the streamable handle active until explicitly released
* @param bStartStalled If true, the handle will start in a stalled state and will not attempt to actually async load until StartStalledHandle is called on it
* @param DebugName Name of this handle, will be reported in debug tools
*/
TSharedPtr<FStreamableHandle> RequestAsyncLoad(TArray<FSoftObjectPath> TargetsToStream, FStreamableDelegate DelegateToCall = FStreamableDelegate(), TAsyncLoadPriority Priority = DefaultAsyncLoadPriority, bool bManageActiveHandle = false, bool bStartStalled = false, FString DebugName = TEXT("RequestAsyncLoad ArrayDelegate"));
TSharedPtr<FStreamableHandle> RequestAsyncLoad(const FSoftObjectPath& TargetToStream, FStreamableDelegate DelegateToCall = FStreamableDelegate(), TAsyncLoadPriority Priority = DefaultAsyncLoadPriority, bool bManageActiveHandle = false, bool bStartStalled = false, FString DebugName = TEXT("RequestAsyncLoad SingleDelegate"));
參數解釋:
TargetsToStream
要加載的資產磁盤。DelegateToCall
委托在加載完成時調用。將被調用在下一個Tick,如果資產已加載,或許多秒后。Priority
優先級傳遞給流系統,優先級高的將首先加載。bManageActiveHandle
如果為true,管理器將保持流句柄活動,直到顯式釋放。bStartStalled
如果為true,句柄將以停滯狀態啟動,並且在調用StartStalledHandle之前不會嘗試實際異步加載。DebugName
此句柄的名稱,將在調試工具中報告。
同樣的,他也有包裝,不過是使用的Lambda來包裝的:
/** Lambda Wrappers. Be aware that Callback may go off multiple seconds in the future. */
TSharedPtr<FStreamableHandle> RequestAsyncLoad(TArray<FSoftObjectPath> TargetsToStream, TFunction<void()>&& Callback, TAsyncLoadPriority Priority = DefaultAsyncLoadPriority, bool bManageActiveHandle = false, bool bStartStalled = false, FString DebugName = TEXT("RequestAsyncLoad ArrayLambda"));
TSharedPtr<FStreamableHandle> RequestAsyncLoad(const FSoftObjectPath& TargetToStream, TFunction<void()>&& Callback, TAsyncLoadPriority Priority = DefaultAsyncLoadPriority, bool bManageActiveHandle = false, bool bStartStalled = false, FString DebugName = TEXT("RequestAsyncLoad SingleLambda"));
注意:資源加載完畢了要用Get取,第二次調用Get則會取消引用:
/**
* Dereference the soft pointer.
*
* @return nullptr if this object is gone or the lazy pointer was null, otherwise a valid UObject pointer
*/
FORCEINLINE T* Get() const
{
return dynamic_cast<T*>(SoftObjectPtr.Get());
}
示例代碼:
//.h
public:
UPROPERTY(EditAnywhere,BlueprintReadWrite, Category = "Test")
TArray<TSoftObjectPtr<UTexture2D>> ObjectPtrs;
//.cpp
void UWC_TestUI::OnBtnClickCommonBtn_SmAsyncGroup()
{
if (ObjectPtrs.Num() <= 0)
{
return;
}
TArray<FSoftObjectPath> SmSyncGroupTexturePaths;
for (auto item:ObjectPtrs)
{
SmSyncGroupTexturePaths.AddUnique(item.ToSoftObjectPath());
}
UAssetManager::GetStreamableManager().RequestAsyncLoad(SmSyncGroupTexturePaths,FStreamableDelegate::CreateUObject(this,&UWC_TestUI::OnStreamableManagerAsyncLoadCompleted));
}
void UWC_TestUI::OnStreamableManagerAsyncLoadCompleted()
{
UTexture2D * UImg2D;
for (int32 i = 0 ; i < ObjectPtrs.Num() ; i ++)
{
//前面指定了類型,這里就不用強轉了
//UTexture2D * UImg2D = Cast<UTexture2D>(item.Get());
//注意要用Get來取得資源的引用
UImg2D = ObjectPtrs[i].Get();
if (UImg2D)
{
//Img的名字
FName ImgName = FName( *FString(TEXT("Img_") + FString::FromInt(i)));
//動態創建一個Image
UImage* Image = WidgetTree->ConstructWidget<UImage>(UImage::StaticClass(),ImgName);
Image->SetBrushFromTexture(UImg2D);
HB_SmSyncGroup->AddChild(Image);
}
}
}
ObjectPtrs是暴露給外部的,由自己手動指定資產路徑。
特點
這種加載方式的特點是:
- 同時支持同步和異步加載,選擇和控制靈活多變。
- 代碼量比前面幾種方式稍大,不過理解起來也不是很難,結構較為清晰。
- 同步加載是用返回的
TSharedPtr<FStreamableHandle>
句柄並用GetLoadedAsset
來獲得同步加載的資源。 - 異步加載則是通過回調函數加上
Get()
的形式來獲取加載好的資源。
本文標簽
游戲開發
、游戲開發基礎
、Unreal Engine
、UE資源加載
。