淘先锋技术网

首页 1 2 3 4 5 6 7

UE4资源加载方式

https://m.sohu.com/a/203578475_667928/

引用资产

ue4 提供了许多种机制来控制引用资产的方式并通过扩展将其装入内存。这些引用分为两种方式:硬性引用,即对象 A 引用对象 B,并导致对象 B 在对象 A 加载时加载;软性引用,即对象 A 通过间接机制(例如字符串形式的对象路径)来引用对象 B。下面的前两个小节阐述硬性引用,而其余小节探讨软性引用。

直接属性引用

这是最常见的资产引用情况,并通过 UPROPERTY 宏公开。面板会公开一个UPROPERTY。

UPROPERTY(EditDefaultsOnly, Category=Building)

USoundCue* ConstructionStartStinger;

构造时引用

使用特殊类 ConstructorHelpers 加载,这个类在构造阶段查找某个对象的对象和类。

在以上构造函数中,ConstructorHelpers 类将尝试在内存中查找该资产,如果找不到,则进行加载。请注意,使用资产的完整路径来指定要加载的内容。如果该资产不存在或者由于出错而无法加载,那么该属性将设置为 nullptr。UPROPERTY 的声明与前面的硬性引用示例相同。它们的工作方式相同,只不过是最初的设置方式有所差别。有关硬性引用的一个注意事项是,当对象加载并实例化时,以硬性方式引用的资产会自动加载,可能导致内存使用量迅速增加。

间接属性引用

间接引用有两种方式FStringAssetReferences和TAssetPtr。FStringAssetReference 是一个简单的结构体,包含了一个具有资源完整名称的字符串。如果在类中创建一个那种类型的属性,那么它将会显示在编辑器中,在editor中,它的表现就是个 允许异步加载的UObject * 。它还可以正确地处理烘焙和重定向。TAssetPtr基本上就是一个封装了 FStringAssetReference 的 TWeakObjectPtr ,它使用一个特定的类作为模板,以便限制编辑器用户界面,使策划仅能选择特定的类。如果所引用的资源存在于内存中,那么 TAssetPtr.Get() 将返回该资源。使用 IsPending() 方法可检查资产是否已准备好可供访问。如果该资源不在内存中,那么可以调用 ToStringReference() 来查找它引用的资源,并可使用模板化 LoadObject<>() 方法、StaticLoadObject() 或 FStreamingManager 来加载对象。前两个方法以同步方式加载资产,这可能会导致帧速率突增。然后再次调用 TAssetPtr.Get() 解除对该资源的引用

以上代码使用 UStaticMesh 的 TAssetPtr 将网格的加载推迟到运行时进行。将检查资产,以确定对象是否已加载。如果尚未加载,那么将使用 FStreamingManager 执行同步加载。否则,将返回 TAssetPtr 内的 UStaticMesh 指针给调用者。

如果希望推迟加载 Uclass,请使用与 TAssetPtr 相同的方法,并替换类特定版本 TAssetSubclassOf 模版类型。其工作方式与引用特定的资产相同,但改为引用资产的 Uclass,而不是引用接口。

查找/加载对象

FindObject<>()可以在 UObject 已加载或已创建时获得它。LoadObject<>()可以在对象未加载时将其加载。请注意,LoadObject<>() 在内部执行的操作与 FindObject 相同,因此不必先尝试查找对象再进行加载。以下是各个函数的一些用法示例。

加载 UClass 时,可以使用 LoadObject 的一种特殊形式。这无非是加载类的一种较简单方法,它会自动验证类型。以下代码片段对此作了说明。

等同于:

资源注册表和对象库插:DataAsset和AssetData的区别

DataAsset是所有Data类(如UPrimaryDataAsset)的基类,是一个Class

而AssetData是存储具体Data实例的信息的Struct

资源注册表是一个存储资源的元数据的系统,允许您搜索及查询这些资源。编辑器使用此资源注册表来显示内容浏览器中的信息,但是您也可以从游戏性代码使用该注册表,来查找当前没有加载的游戏资源的相关元数据。要想使得一个资源的数据是可搜索的,需要给该属性添加"AssetRegistrySearchable"标签。查询资源注册表会返回FAssetData类型的对象,它包含了关于该对象的信息和一个 键-值 对映射表,该映射表包含了标记为可搜索的属性。

处理成组的未加载的资源的最简单方法是使用 ObjectLibrary 。ObjectLibrary 是一个对象,包含了一系列继承共享基类的未加载对象或者未加载对象的FAssetData 。您可以通过提供一个搜索路径来加载一个对象库,它将加载那个路径中的所有资源。这是非常有用的,因为您可以为不同类型指定您的部分内容文件夹,且 美术师/设计师 不需要手动编辑清单表就可以添加新的资源。这里是一个示例,展示了如何使用一个对象库来从磁盘加载AssetData:

在这个示例中,它创建了一个新的对象库,关联了一个基类,然后加载了给定路径中的所有资源数据。接着选择性地加载真正的资源。如果资源很小,或者如果您正在进行烘焙且需要确保烘焙所有内容,那么您则需要完全地加载这些资源。只要您在烘焙过程中执行一次资源注册表查询,并加载返回的资源,您的对象库就可以正常地在烘焙数据中发挥作用,就像在开发过程中的处理一样。一旦把资源数据放入到了一个 ObjectLibrary 中,您可以进行查询并选择性地加载特定的资源。这里是示例,展示了如何进行查询:

在这个示例中,它搜索对象库并查找任何具有 TypeName 文本域且该文本域中包含 "FooType" 的对象,然后返回第一次查找到的对象。一旦有了此 AssetData ,您可以调用 ToStringReference() 来把它转换成一个 FStringAssetReference ,然后您可以使用下一个系统进行异步加载。

StreamableManager(动态加载管理器)和异步加载

现在,您具有一个引用了磁盘上一个资源的 FStringAssetReference ,那么怎样真正地异步加载它哪? FStreamableManager是完成这个处理的最简单方法。首先,您需要创建一个 FStreamableManager ,我建议你把它放到某个全局游戏单例对象中,比如在 DefaultEngine.ini 中使用 GameSingletonClassName 指定的对象。然后,您可以把 StringAssetReference 传给该对象,并启动进行加载。SynchronousLoad 将会进行简单的、阻碍加载,并返回该对象。这种方法对于较小的对象可能很好,但是它可能会使您的主线程停顿时间过长。出现那种情况,您将需要使用 RequestAsyncLoad ,它将会异步地加载一组资源,并在完成后调用一个代理。

在这个示例中,ItemList 是一个 TArray< TAssetPtr<UGameItem> > ,由设计人员在编辑器中进行修改。这段代码迭代那个列表,将列表项转换为 StringReferences ,并将它们进行排队。当所有列表项都加载后(或者由于缺失列表项而不能加载),它将在代理中调用已通过的相关处理。该代理然后迭代同样的列表项,区分它们,并把它们提供给一个玩家。StreamableManager 在调用该代理之前,将保持到任何它所加载的对象的强引用,所以在调用该代理之前,您可以确保您想异步加载的任何对象都没有被垃圾回收。当调用该代理后, StreamableManager 释放这些引用,所以您想确保它们一直存在,您需要在其他地方对它们进行强引用。

您可以使用同样的方法来异步加载 FAssetDatas ,仅需在它们上面调用 ToStringReference ,把它们添加到一个数组中,然后使用一个代理调用RequestAsyncLoad 。该代理可以是您想操作的任何内容,所以如果需要,你可以传入负载信息。通过把结合使用上述的方法,您应该可以设置一个高效加载游戏中的任何资源的系统。转换直接访问内存的游戏代码来处理异步加载将会花费一些时间,但是之后您的游戏停顿将变得非常少,并且内存使用量会更低。

新的资源管理系统:AssetManager

AssetManager帮助我们对资源进行更好的控制,比如何时加载,何时释放。无论实在editor模式还是在发布版中,AssetManager都是一个唯一的,全局的对象。我们可以根据自己的需求去重写它。

UE的Asset管理系统把资源分成了两类:主要资源和次要资源。主要资源可以通过GetPrimaryAssetId函数来生成一个PrimaryAssetID。AssetManager能够通过这个ID对其进行控制。如果想把一个特定的UObject转为主要资源,重写GetPrimaryAssetId函数使其返回一个有效的FPrimaryAssetId结构即可。次要资源即为被主要资源引用的资源,它们不被AssetManager直接管理,当引用他们的主要资源被加载时,他们会自动加载。默认只有UWorld对象(Level)是主要资源,其他所有资源都是次要资源。为了让次要资源变为主要资源,GetPrimaryAssetId函数必须被重写并返回一个有效的FPrimaryAssetId结构。

最简单的创建一个主要资源类的方法就是继承UPrimaryDataAsset。

之后可创建一个这个类的实例

使用AssetManager的方法LoadPrimaryAssets,LoadPrimaryAsset, 和LoadPrimaryAssetsWithType进行加载。

Asset Bundles

UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = Display, AssetRegistrySearchable, meta = (AssetBundles = "TestBundle"))TAssetPtr<UStaticMesh> MeshPtr;

当热更新完毕时,可以把下载的bundle动态和主要资源进行关联