UE4 异步资源加载

概述

虚幻引擎4中有几个新系统,使得可以更加轻松地异步加载资源数据,这些系统代替了虚幻引擎3中的免搜索内容包中存在的很多功能。这些新方法既可以在开发数据应用也可以在设备上的烘焙数据上进行应用,所以您不必保留两个根据需要加载数据的代码路径。有两种通用的方法可以供您根据需要来引用及加载数据。

FStringAssetReferences 和TAssetPtr

美术师或设计师引用资源的最简单的方法是,创建一个UProperty 强指针,并赋予其一个类目。在虚幻引擎4中,如果您使用强指针UObject 属性引用一个资源,那么当加载包含该属性的对象时将会加载那个资源(通过把对象放置在地图中,或者通过从类似于游戏信息这样的东西中进行引用该对象)。如果您不关心是否在游戏启动时能够100%地加载您的资源。如果您想让 美术师/设计师 使用和强指针一样的用户界面来引用特定的资源,而不是总是加载引用的资源,请使用 FStringAssetReference 或 TAssetPtr 。

FStringAssetReference 是一个简单的结构体,包含了一个具有资源完整名称的字符串。如果您在类中创建一个那种类型的属性,那么它将会显示在编辑器中,就像是个 UObject * 属性一样。它还可以正确地处理烘焙和重定向,所以如果您有一个StringAssetReference,那么它一定可以在设备上正常工作。TAssetPtr基本上就是一个封装了 FStringAssetReference 的 TWeakObjectPtr ,它使用一个特定的类作为模板,以便您能限制编辑器用户界面,使其仅允许选择特定的类。如果所引用的资源存在于内存中,那么 TAssetPtr.Get() 将返回该资源。如果该资源不在内存中,那么您那可以调用 ToStringReference() 来查找它引用的资源,并使用下面介绍的方法加载该资源,然后再次调用TAssetPtr.Get() 解除对该资源的引用。

如果美术师或设计师正在手动地设置引用,那么使用 TAssetPtrs 和 StringAssetReferences是非常适合的,但如果您想进行类似于查询这样的处理,查找满足特定要求的资源,而不是加载所有资源,那么您应该使用资源注册表及对象库。

资源注册表和对象库

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

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

if (!ObjectLibrary)
{
       ObjectLibrary = ConstructObject<UObjectLibrary>(UObjectLibrary::StaticClass());
       ObjectLibrary->ObjectBaseClass = BaseClass;
       ObjectLibrary->UseWeakReferences(GIsEditor);
       ObjectLibrary->AddToRoot();
}
ObjectLibrary->LoadAssetDataFromPath(TEXT("/Game/PathWithAllObjectsOfSameType");
if (bFullyLoad)
{
       ObjectLibrary->LoadAssetsFromAssetData();
}

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

TArray<FAssetData> AssetDatas;
ObjectLibrary->GetAssetDataList(AssetDatas);

for (int32 i = 0; i < AssetDatas.Num(); ++i)
{
       FAssetData& AssetData = AssetDatas[i];

       const FString* FoundTypeNameString = AssetData.TagsAndValues.Find(GET_MEMBER_NAME_CHECKED(UAssetObject,TypeName));

       if (FoundTypeNameString && FoundTypeNameString->Contains(TEXT("FooType")))
       {
              return AssetData;
       }
}

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

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

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

void UGameCheatManager::GrantItems()
{
       TArray<FStringAssetReference> ItemsToStream;
       FStreamableManager& Streamable = UGameGlobals::Get().StreamableManager;
       for(int32 i = 0; i < ItemList.Num(); ++i)
       {
              ItemsToStream.AddUnique(ItemList[i].ToStringReference());
       }
       Streamable.RequestAsyncLoad(ItemsToStream, FStreamableDelegate::CreateUObject(this, &UGameCheatManager::GrantItemsDeferred));
}

void UGameCheatManager::GrantItemsDeferred()
{
       for(int32 i = 0; i < ItemList.Num(); ++i)
       {
              UGameItemData* ItemData = ItemList[i].Get();
              if(ItemData)
              {
                     MyPC->GrantItem(ItemData);
              }
       }
}

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

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

<think>好的,我需要回答用户关于Unreal Engine中同步加载异步加载资源的区别及用法的问题。首先,我要确保自己正确理解这两个概念,并且能够用中文清晰地表达出来。根据用户提供的引用,特别是引用[4],里面提到了路径绑定和资源加载的不同方式,但具体细节可能还需要结合我的知识来补充。 首先,同步加载异步加载的基本区别在于资源加载时是否阻塞主线程。同步加载会立即加载资源,直到完成才继续执行后面的代码,这可能导致游戏卡顿,特别是在加载资源时。而异步加载是在后台进行,不会阻塞主线程,适合用于需要流畅体验的场景,比如游戏运行时动态加载资源。 接下来,我需要整理UE中具体的实现方法。同步加载通常使用`LoadObject`或`LoadClass`等函数,直接返回资源对象。而异步加载可能涉及`FStreamableManager`或`AsyncLoading`相关的类,比如使用`LoadPackageAsync`函数。需要提到具体的代码示例,比如如何调用这些函数,如何处理回调。 然后,区别方面,除了加载方式的不同,还需要提到性能影响、使用场景、资源管理等方面。比如同步加载适合小资源或初始化阶段,异步适合大资源或运行时动态加载异步加载可能需要更复杂的错误处理和资源管理,比如处理加载完成后的回调,确保资源正确使用,避免内存泄漏。 另外,根据用户提供的引用,引用[2]和引用[3]提到了Unreal Engine中C++编程和与蓝图交互的内容,可能需要在回答中提到如何在C++中实现这两种加载方式,以及如何与蓝图配合使用,比如将异步加载的结果传递给蓝图进行后续处理。 需要确保回答结构清晰,分点说明,可能分为同步加载的使用方法、异步加载的使用方法、两者的区别,以及使用建议。同时,用户要求生成相关问题,可能需要准备几个相关问题,比如如何优化资源加载性能,如何处理异步加载失败的情况,或者如何选择合适的加载方式等。 最后,检查是否符合用户的所有要求:使用正确的中文表达,数学公式用$...$,独立公式用$$...$$,但在这个问题中可能不涉及数学公式,所以可能不需要。同时,确保引用正确,用户提供的引用中有提到资源加载相关的部分,特别是引用[4],需要在适当的地方添加引用标识。例如,在提到异步加载优化性能时,可以引用[1]中提到的网络同步优化,但需要自然衔接,或者根据引用[4]中的路径绑定内容来支持资源加载的方法。 可能还需要注意术语的准确性,比如UE中的同步加载可能使用`SynchronousLoad`或`LoadObject`,而异步加载可能使用`FStreamableManager`的RequestAsyncLoad方法,或者`AsyncTask`等。需要确认这些方法的正确用法,并给出示例代码,比如C++中的代码片段,或者蓝图节点的使用。 总结步骤: 1. 解释同步加载异步加载的基本概念。 2. 分别说明两者的使用方法,包括代码示例。 3. 对比两者的区别,从性能、使用场景、资源管理等方面。 4. 提供使用建议,何时使用哪种方式。 5. 生成相关问题。 6. 确保引用正确,比如在提到优化性能时引用[1],或者在资源管理部分引用[3]或[4]。 现在需要将这些思路整合成一个结构清晰的中文回答,并确保覆盖所有用户的要求,包括正确使用引用和生成相关问题。</think>### Unreal Engine 同步加载异步加载资源的区别及用法 #### 一、同步加载(Synchronous Load) 同步加载会阻塞主线程直到资源完全加载完毕,适用于**初始化阶段或小资源**的加载。 **使用方法** 在C++中,常用`LoadObject`或`StaticLoadObject`函数: ```cpp // 同步加载纹理资源 UTexture2D* Texture = LoadObject<UTexture2D>(nullptr, TEXT("/Game/Assets/Texture_Example")); ``` 在蓝图中,可通过**直接引用资源节点**实现同步加载[^3]。 **特点**: - **即时性**:立即返回资源对象。 - **卡顿风险**:大资源加载会导致主线程阻塞,影响帧率。 --- #### 二、异步加载(Asynchronous Load) 异步加载在后台线程执行,不阻塞主线程,适合**动态加载大型资源**(如开放世界场景)。 **使用方法** 1. **C++实现**(使用`FStreamableManager`): ```cpp FStreamableManager StreamableManager; TSharedPtr<FStreamableHandle> Handle = StreamableManager.RequestAsyncLoad( TEXT("/Game/Assets/Mesh_Example"), FStreamableDelegate::CreateLambda([]() { // 加载完成后的回调逻辑 }) ); ``` 2. **蓝图实现**: 使用**Async Load Asset**节点,绑定完成事件(图2.2.1)[^4]。 **特点**: - **非阻塞**:后台加载,保持游戏流畅。 - **回调机制**:需处理加载完成后的资源绑定逻辑。 - **资源管理**:需手动管理加载句柄,避免内存泄漏[^2]。 --- #### 三、核心区别对比 | 特性 | 同步加载 | 异步加载 | |---------------------|----------------------------|-----------------------------------| | 线程阻塞 | 是 | 否 | | 适用场景 | 启动预加载、小资源 | 动态加载、大资源 | | 性能影响 | 可能导致卡顿 | 无显著卡顿 | | 实现复杂度 | 简单 | 需处理回调和资源管理 | | 典型函数/节点 | `LoadObject`、直接引用 | `FStreamableManager`、Async节点 | --- #### 四、使用建议 1. **同步加载**: - 用于必须立即使用的资源(如游戏启动时的核心材质)。 - 避免在游戏运行时频繁调用,防止帧率下降[^3]。 2. **异步加载**: - 开放世界动态加载地形或角色模型。 - 配合进度条或加载动画提升用户体验。 - 注意释放未使用的资源句柄,优化内存占用[^2]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值