AssetManager的使用

前言

AssetManager(下面简称AM)是一个全局单例类,用于管理各种primary assetsasset bundles

可以在运行时对Primary Assets进行加载和释放操作

补充官方文档对于AssetManager的介绍

虚幻引擎 4 自动处理 资源 加载与卸载,开发者无需编写系统代码告知引擎具体所需的资源。然而,某些情况下开发者可能需要更精确地掌控资源发现、加载与审核的时机与方法。在这些情况下,资源管理器(Asset Manager) 便能大显身手。资源管理器是存在于编辑器和打包游戏中的独特全局对象,可根据不同项目进行覆盖和自定义。它提供了一个管理资源的框架,可将内容划分为数据块,对应项目的上下文,而同时保证虚幻引擎 4 松散打包架构 的优势。它同时提供了一套工具,协助审核硬盘和内存使用,提供所需信息,以优化资源组织,在部署游戏时进行 烘焙和数据块划分

我们可以在项目设置中自定义AM

image-20201228110451282

或者修改DefaultEngine.ini文件

1
[/Script/Engine.Engine] AssetManagerClassName=/Script/Module.UClassName 其中"Module"代表项目的模块名,"UClassName"则代表希望使用的 UClass 名。

关于资源和注册

Asset Registry

Asset Registry是Asset注册表,位于Project Settings——AssetManager中(Primany Asset Types To Scan),其中存储了每个的asset的有用信息。这些信息会在asset被储存的时候进行更新。

之前的文章资源注册简单分析了IAssetRegistry模块的一些基础数据和功能;

Asset

我们在编辑器ContentBrowser内看到的都是Asset,初学者容易混淆场景中真正生成的对象与Asset的关系,我们一般的蓝图类都是类型BlueprintAsset

我们通过蓝图IAssetRegistry可以查找已经注册的Asset信息

image-20201228104718787

主资源和次资源

UE4将所有资源分为两类:主资源次资源, 即PrimaryAsset和SecondaryAsset

默认只有目录Content/Maps/内的 UWorld 资源(关卡)为主资源;

次资源不由资源管理器直接处理,但其被主资源引用或使用后引擎便会自动进行加载。

调用 GetPrimaryAssetId 即可获得资源的 ID。如需将次资源指定为主资源,需要覆盖 GetPrimaryAssetId 即可返回一个有效的 FPrimaryAssetId 结构。

1
2
3
4
FPrimaryAssetId USRItem::GetPrimaryAssetId() const
{
return FPrimaryAssetId(ItemType, GetFName());
}

注册主资源

我们的主资源不仅需要重写GetPrimaryAssetId()方法, 还需要把对应PrimaryAssetType的字符串填写到项目设置中, 即用资源管理器注册主资源,如下图

image-20201229172845276

这样引擎就会自动扫描对应目录下的对应类型的主资源

在蓝图中可以用如下节点查看是否已经拥有该类型的标签

image-20201229172959486

这样我们就可以对资源进行加载和卸载操作了

注意: 项目设置注册的类型名称与GetPrimaryAssetId()定义的名称必须一样, 不然找不到

PrimaryDataAsset

PrimaryDataAssetUPrimaryDataAsset 继承, 拥有加载和保存资源束的功能

接用Fortnite的话

大多数的Primary Asset由于可能会直接被美术或策划直接修改,因此通常都会有一个在磁盘上的表现形式。常用的手法是将其继承自UPrimaryDataAsset。通过这种手法就可以直接在引擎中添加新的Primary Data Asset

ActionRPG项目中, 项目用URPGItem继承自 PrimaryDataAsset, 管理了所有道具类的数据, 包括技能,武器, 消耗品等

Item基类里面可以设置一些通用的变量比如

1
2
3
4
5
6
7
8
9
10
11
12
//资源类型, 需要匹配项目设置对应的名称
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Item)
FPrimaryAssetType ItemType;
//名称
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Item)
FText ItemName;
//描述
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Item)
FText ItemDescription;
//Icon
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Item)
FSlateBrush ItemIcon;

资源束/AssetBundle

AssetBundle是一个Asset的列表,用于将一堆Asset在runtime的时候载入。

  • FAssetBundleEntry
1
2
3
4
5
6
7
8
/** 保存了Asset的资源束信息 ,对于全局捆绑包,或者在保存的捆绑包信息中,这是空的 */
FPrimaryAssetId BundleScope;

/** 这个bundle的特定的唯一名称*/
FName BundleName;

/** 包含的资源软路径表 */
TArray<FSoftObjectPath> BundleAssets;
  • FAssetBundleData
1
2
/** FAssetBundleEntry 数组 */
TArray<FAssetBundleEntry> Bundles;

举例几个方法

1
2
3
4
5
	/** Returns pointer to an entry with given Scope/Name */
FAssetBundleEntry* FindEntry(const FPrimaryAssetId& SearchScope, FName SearchName);
/** Adds or updates an entry with the given BundleName -> Path. Scope is empty and will be filled in later */
void AddBundleAsset(FName BundleName, const FSoftObjectPath& AssetPath);
..................

常用API

在cpp中, 可以在AssetManager中使用如下API使用资源

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/** 
* Loads a list of Primary Assets. This will start an async load of those assets, calling callback on completion.
* These assets will stay in memory until explicitly unloaded.
* You can wait on the returned streamable request or poll as needed.
* If there is no work to do, returned handle will be null and delegate will get called before function returns.
*
* @param AssetsToLoad List of primary assets to load
* @param LoadBundles List of bundles to load for those assets
* @param DelegateToCall Delegate that will be called on completion, may be called before function returns if assets are already loaded
* @param Priority Async loading priority for this request
* @return Streamable Handle that can be used to poll or wait. You do not need to keep this handle to stop the assets from being unloaded
*/
virtual TSharedPtr<FStreamableHandle> LoadPrimaryAssets(const TArray<FPrimaryAssetId>& AssetsToLoad, const TArray<FName>& LoadBundles = TArray<FName>(), FStreamableDelegate DelegateToCall = FStreamableDelegate(), TAsyncLoadPriority Priority = FStreamableManager::DefaultAsyncLoadPriority);

/** Single asset wrapper */
virtual TSharedPtr<FStreamableHandle> LoadPrimaryAsset(const FPrimaryAssetId& AssetToLoad, const TArray<FName>& LoadBundles = TArray<FName>(), FStreamableDelegate DelegateToCall = FStreamableDelegate(), TAsyncLoadPriority Priority = FStreamableManager::DefaultAsyncLoadPriority);

/** Loads all assets of a given type, useful for cooking */
virtual TSharedPtr<FStreamableHandle> LoadPrimaryAssetsWithType(FPrimaryAssetType PrimaryAssetType, const TArray<FName>& LoadBundles = TArray<FName>(), FStreamableDelegate DelegateToCall = FStreamableDelegate(), TAsyncLoadPriority Priority = FStreamableManager::DefaultAsyncLoadPriority);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/** 
* Unloads a list of Primary Assets that were previously Loaded.
* If the only thing keeping these assets in memory was a prior Load call, they will be freed.
*
* @param AssetsToUnload List of primary assets to load
* @return Number of assets unloaded
*/
virtual int32 UnloadPrimaryAssets(const TArray<FPrimaryAssetId>& AssetsToUnload);

/** Single asset wrapper */
virtual int32 UnloadPrimaryAsset(const FPrimaryAssetId& AssetToUnload);

/** Loads all assets of a given type, useful for cooking */
virtual int32 UnloadPrimaryAssetsWithType(FPrimaryAssetType PrimaryAssetType);

在蓝图中, 使用下图所示方法

image-20201229174245630

FPrimaryAssetData

定义了资源的一般数据

1
2
3
4
5
6
7
8
9
10
11
/** Path used to look up cached asset data in the asset registry. This will be missing the _C for blueprint classes */
FName AssetDataPath;

/** Path to this asset on disk */
FSoftObjectPtr AssetPtr;

/** Current state of this asset */
FPrimaryAssetLoadState CurrentState;

/** Pending state of this asset, will be copied to CurrentState when load finishes */
FPrimaryAssetLoadState PendingState;