DH DaydreamerH Game Dev Training Desk

Game Development

Asset Management In UE

资产管理

UE 会自动处理资产的加载与卸载,这为开发者提供了一种告知引擎合适需要某个资产的方法。

在某些情况下,可能需要精确控制资产的发现、加载和审计的时机与方式;针对这些场景,资产管理器可以提供帮助。

资产管理器是一个独特的全局对象,存在于编辑器和已打包的游戏中;它可以被项目重写和自定义,并提供一个管理资产的框架,能够将内容划分为你项目语境中有意义的块,同时保留 UE 的松散包结构的优点。

  • 块:根据项目需求把资产分成逻辑分组,每组在运行时或打包时可作为一个独立单元处理;最终优化运行时内存/加载时间,控制首包大小、便于差分更新与按需下载。
  • 松散包结构:引擎中每个资产通常以独立包存在,包之间可通过引用连接,但不会被强制合并成单一大文件。

资产管理器还提供了一组工具,用于审计磁盘和内存使用情况,能够提供优化资产组织所需的信息。

主资产和次级资产

UE 的资产管理系统将所有资产分为两类:主资产和次级资产。

资产管理器可以通过主资产 ID 直接操作主资产,该 ID 可通过调用 GetPrimaryAssetId 获取;若要将某个继承自特定 UObject 类的资产指定为主资产,可重写该类的 GetPrimaryAssetId 函数,使其返回有效的 FPrimaryAssetId 结构;主资产 ID 由两部份组成:

  • 用于标识一组资产的唯一主资产类型。
  • 具体主资产的名称,名称默认为在 Content Browser 中显示的资产名。

次级资产不会被资产管理器直接处理,而是在被主资产引用或使用时由引擎自动加载。

默认情况下,只有 UWorld 资产被视为主资产,其他所有资产均为次级资产。

蓝图类资产和数据资产

资产管理器处理两类不同的资产:蓝图类和非蓝图资产。

蓝图类

要创建新的蓝图主资产,需要在内容浏览器中新建一个继承自重写了 GetPrimaryAssetId 函数的类的蓝图。这个基类可以是 Primary Data Asset 或其子类,也可以是重写了 GetPrimaryAssetId 的 Actor 子类。

要访问蓝图主资产,可以在 C++ 中调用如 GetPrimaryAssetObjectClass 之类的函数。或使用名称中带有 Class 的蓝图资产管理器函数。

得到类之后,你可以像使用其他蓝图类那样用它生成新实例,或者使用 Get Defaults 函数从与该蓝图关联的类默认对象读取只读数据。

对于那些不需要实例化的蓝图类,可以将数据存放在继承自 UPrimaryDataAsset 的数据专用蓝图中。

非蓝图资产

当你的主资产类型不需要存储蓝图数据时,可以使用非蓝图资产;非蓝图资产在代码中访问更简单,并且更节省内存。

要在编辑器中创建新的非蓝图主资产,可以在高级内容浏览器窗口中新建一个数据资产,这种方式创建的资产并不是创建一个类,而是某个类的实例。

要访问其类,需要用 GetPrimaryAssetObject 等函数加载;随后可以访问它们直接读取数据。

Asset Manager

Asset Manager 对象是一个单例,负责主资产的发现与加载。

引擎中包含的基础资产管理器提供了基本的管理功能,但可以拓展以满足项目的特定需求。

对象的异步加载是通过资产管理器内部的 Streamable Manager 负责实际的异步对象加载工作,同时使用 Streamable Handle 将对象保持在内存中,直到它们不再需要并可以被卸载。

与单例的 Asset Manager 不同,引擎在不同部分和不同使用场景中存在多个 Streamable Manager.

资产包(Asset Bundle)

一个资产包是与某个主资产关联的一组特定资产列表。资产包通过在某个 UObjectTSoftObjectPtrFStringAssetReference 成员的 UPROPERTY 部分使用 AssetBundles 元标签来创建。

该标签的值表示次级资产应存放到的包的名称。例如,下面这个静态网格资产,存放在名为 MeshPtr 的成员变量中,当该 UObject 保存时会被加入名为 TestBundle 的资产包:

/** Mesh */	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = Display, AssetRegistrySearchable, meta = (AssetBundles = "TestBundle"))	TSoftObjectPtr MeshPtr;

另一种使用资产包的方法是在运行时由项目的 Asset Manager 类注册它们:需要编写代码填充一个 FAssetBundleData 结构体,然后将该结构体传递给资产管理器。可以通过重写 UpdateAssetBundleData 函数,或使用与包中次级资产关联的主资产 ID 调用 AddDynamicAsset 来完成此操作。

Coach

基于本文开始提问

跳转到 AI Coach,并将当前文章作为测试上下文。

开始测试