BlueRose
文章97
标签28
分类7
AssetManager系列之TAssetPtr与FStreamableManager

AssetManager系列之TAssetPtr与FStreamableManager

TAssetPtr

因为在wiki上已经有介绍TAssetPtr的内容了,所以我就直接翻译了,并且在末尾给予一定的补充。以下是翻译自wiki的内容:

一般情况下,我们无需将这个场景中所有Asset都加载了再进入游戏,我们可以先加载必要Asset,在进入场景后,异步加载剩余的非必要资源。而TAssetPtr可以解决这个问题。TAssetPtr类似于标准指针,其区别在于TAssetPtr指向的资产可能已经加载,也可能还没有加载,如果资产没有加载,则它包含加载该资产所需的信息。(TAssetPtr属于弱指针)

一个简单的引用Asset的方法就是创建带有UProperty标记的TAssetPtr成员变量,并且在编辑器中指定想要加载的Asset。本质上是TSoftClassPtr,包含了FSoftObjectPtr对象。可以通过FSoftObjectPath构造,并用其监视Asset的状态

类型

变量 描述
TAssetPtr 指向尚未加载但可以根据请求加载的资产的指针
TAssetSubclassOf 指向已定义的基类的子类的指针,该子类尚未加载,但可以根据请求加载。用于指向蓝图而不是基本component。(大概是因为蓝图是Asset)

关键功能

.h

/** 定义Asset指针. 别忘记添加UPROPERTY标记 */
UPROPERTY(EditAnywhere)
TAssetPtr<MyClass> MyAssetPointer;

/** 定义子类版本. */
UPROPERTY(EditAnywhere)
TAssetSubclassOf<MyBaseClass> MyAssetSubclassOfPointer;

.cpp

// 调用IsValid()去测试这个AssetPtr是不是指向一个有效的UObject
MyAssetPointer.IsValid();

//调用Get()来返回其指向的UObject(在UObject存在的情况下)
MyAssetPointer.Get();

/** 特别注意TAssetSubclassOf的Get(),它返回的是UClass的指针!! */
MyAssetSubclassOfPointer.Get()
/** 要正确使用UClass指针,必须使用GetDefaultObject<T>()来获得指向UObject或派生类的指针 */
MyAssetSubclassOfPointer.Get()->GetDefaultObject<MyBaseClass>()

// 调用ToStringReference()返回希望加载的Asset的FStringAssetReference
MyAssetPointer.ToStringReference();

如何使用

变量 描述
FStreamableManager 运行时的Asset流控制管理器,这是用户定义的对象,应该被定义在类似GameInstance之类的方便访问的对象中。
FStringAssetReference 一个包含Asset应用字符串的结构体,能对Asset进行弱引用。

Asset载入器

FStreamableManager是异步资源加载器,最好定义在类似GameInstance之类持久性对象中,原因有下:

  1. 访问方便,这使得在需要时加载资产变得很容易。
  2. 具备持久性,因为你永远不想在加载对象时丢失或销毁对FStreamableManager的引用。

使用方法

简单异步载入

允许您加载单个资产并获得它的强引用。这意味着在您使用unload手动卸载它之前,它永远不会被垃圾回收。(这个 方法已经被废弃,请使用RequestAsyncLoad,并设置bManageActiveHandle为true)

// the .h
TAssetPtr<ABaseItem> MyItem;

// the .cpp
FStringAssetReference AssetToLoad
AssetToLoad = MyItem.ToStringReference();
AssetLoader.SimpleAsyncLoad(AssetToLoad);
请求式异步载入
//the .h
TArray< TAssetPtr<ABaseItem> > MyItems;

// the .cpp
TArray<FStringAssetReference> AssetsToLoad
for(TAssetPtr<ABaseItem>& AssetPtr : MyItems) // C++11 ranged loop
{
     AssetsToLoad.AddUnique(AssetPtr.ToStringReference());
}
AssetLoader.RequestAsyncLoad(AssetsToLoad, FStreamableDelegate::CreateUObject(this, &MyClass::MyFunctionToBeCalledAfterAssetsAreLoaded));

PS.实际看过代码之后发现这个RequestAsyncLoad还有一个回调版本的。

/** 
 * 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(const TArray<FSoftObjectPath>& TargetsToStream, FStreamableDelegate DelegateToCall = FStreamableDelegate(), TAsyncLoadPriority Priority = DefaultAsyncLoadPriority, bool bManageActiveHandle = false, bool bStartStalled = false, const FString& DebugName = TEXT("RequestAsyncLoad ArrayDelegate"));
TSharedPtr<FStreamableHandle> RequestAsyncLoad(const FSoftObjectPath& TargetToStream, FStreamableDelegate DelegateToCall = FStreamableDelegate(), TAsyncLoadPriority Priority = DefaultAsyncLoadPriority, bool bManageActiveHandle = false, bool bStartStalled = false, const FString& DebugName = TEXT("RequestAsyncLoad SingleDelegate"));

/** Lambda Wrappers. Be aware that Callback may go off multiple seconds in the future. */
TSharedPtr<FStreamableHandle> RequestAsyncLoad(const TArray<FSoftObjectPath>& TargetsToStream, TFunction<void()>&& Callback, TAsyncLoadPriority Priority = DefaultAsyncLoadPriority, bool bManageActiveHandle = false, bool bStartStalled = false, const FString& DebugName = TEXT("RequestAsyncLoad ArrayLambda"));
TSharedPtr<FStreamableHandle> RequestAsyncLoad(const FSoftObjectPath& TargetToStream, TFunction<void()>&& Callback, TAsyncLoadPriority Priority = DefaultAsyncLoadPriority, bool bManageActiveHandle = false, bool bStartStalled = false, const FString& DebugName = TEXT("RequestAsyncLoad SingleLambda"));
使用Asset

当你的Asset加载完成,别忘记调用Get()来取得它。

MyItem.Get(); // returns a pointer to the LIVE UObject

本人额外添加的内容(一些有用的东西)

FStreamableManager

/** 
 * 同步版本的载入函数,用于载入多个(一组)资源,返回一个handle。
 * 
 * @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(const TArray<FSoftObjectPath>& TargetsToStream, bool bManageActiveHandle = false, const FString& DebugName = TEXT("RequestSyncLoad Array"));
TSharedPtr<FStreamableHandle> RequestSyncLoad(const FSoftObjectPath& TargetToStream, bool bManageActiveHandle = false, const FString& DebugName = TEXT("RequestSyncLoad Single"));

/** 
 * 同步版本的载入函数,用于载入单个资源,返回UObject的指针。如果没有找到则返回空指针。
 * 
 * @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);

FStreamableHandle

同步或者异步加载的句柄,只要句柄处于激活状态,那么Asset就不会被回收。你可以句柄来控制Asset,以及绑定相应的委托。

/** Bind delegate that is called when load completes, only works if loading is in progress. This will overwrite any already bound delegate! */
bool BindCompleteDelegate(FStreamableDelegate NewDelegate);

/** Bind delegate that is called if handle is canceled, only works if loading is in progress. This will overwrite any already bound delegate! */
bool BindCancelDelegate(FStreamableDelegate NewDelegate);

/** Bind delegate that is called periodically as delegate updates, only works if loading is in progress. This will overwrite any already bound delegate! */
bool BindUpdateDelegate(FStreamableUpdateDelegate NewDelegate);

/**
 * Blocks until the requested assets have loaded. This pushes the requested asset to the top of the priority list,
 * but does not flush all async loading, usually resulting in faster completion than a LoadObject call
 *
 * @param Timeout                Maximum time to wait, if this is 0 it will wait forever
 * @param StartStalledHandles    If true it will force all handles waiting on external resources to try and load right now
 */
EAsyncPackageState::Type WaitUntilComplete(float Timeout = 0.0f, bool bStartStalledHandles = true);

/** Gets list of assets references this load was started with. This will be the paths before redirectors, and not all of these are guaranteed to be loaded */
void GetRequestedAssets(TArray<FSoftObjectPath>& AssetList) const;

/** Adds all loaded assets if load has succeeded. Some entries will be null if loading failed */
void GetLoadedAssets(TArray<UObject *>& LoadedAssets) const;

/** Returns first asset in requested asset list, if it's been successfully loaded. This will fail if the asset failed to load */
UObject* GetLoadedAsset() const;

/** Returns number of assets that have completed loading out of initial list, failed loads will count as loaded */
void GetLoadedCount(int32& LoadedCount, int32& RequestedCount) const;

/** Returns progress as a value between 0.0 and 1.0. */
float GetProgress() const;

FSoftObjectPath

一个包含对象的引用字符串的结构体。它可以对按需加载的资产进行弱引用。可以使用UProperties进行标记,使得它可以在编辑器中显示并且可以指定资源。

可以使用TryLoad进行Asset载入,返回UObject指针,为空则表示载入失败。