虚幻学习笔记22—C++同步和异步加载

本文介绍了虚幻引擎中硬引用和软引用的区别,以及同步和异步加载资源的方法,强调了异步加载配合回调函数的重要性,以避免阻塞主线程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、前言

        之前提到的静态和动态加载都是同步的加载,同时其中的引用基本都是硬引用。如果资源比较大的话会出现卡顿的现象,下面将介绍一种异步加载的方式。同时,还将介绍一种区别与之前的Load的方法。

        在说明同步和异步加载之前需要先讲一下虚幻引擎的引用路径的问题,虚幻中的资源和类的引用包含软引用和硬引用,硬引用即对象A引用对象B,那么加载对象A的时候会自动加载对象B。软引用,即对象A通过间接机制(一般是字符串形式的路径)来引用对象B,不会在A加载时加载B。静态和动态加载

二、实现

2.1、硬性引用

        比如一个类A中定义一个变量为“输入映射”,在后续的蓝图中添加了,如图2.1.1所示,在细节

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "MyInput")
	class UInputMappingContext* MyMappingContext;
图2.1.1

面板中添加了该变量的具体资源,那么在该类A在加载的同时会自动加载该“输入映射”添加的资源到内存中。

还有一种硬引用的方式,这个函数通常在构造函数中使用。

static ConstructorHelpers::FClassFinder<UUserWidget>tempUICalss(TEXT("/Script/UMGEditor.WidgetBlueprint'/Game/BluePrint/UI3D.UI3D_C'"));
2.2、软引用路径

        而软引用则不同,如下多是在A类中定义以下几个软引用,如图2.2.1所示可以在类A的细节面

	/// <summary>
	/// class的是蓝图类,Object的可以是非类的资源如图片
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Path")
	FSoftObjectPath AssetObjectPath;
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Path")
	FSoftClassPath AssetClassPath;
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Path")
	TSoftObjectPtr<AActor> AssetObjectPtr;//场景中的物体
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Path")
	TSoftObjectPtr<AActor> AssetClassPtr;//场景中的物体

板中看到这定义的四个路径,这里的路径绑定的资源不再会A加载的同时加载到内存中,而要通过

图2.2.1

后续的代码再加载到内存中。这就是软引用和硬引用的区别。

2.3、同步加载

        通过复制引用资源的路径,然后添加到代码的的路径中,同步加载在资源比较大的情况下会让主程序都出现卡顿的情况。

	//同步加载
	FSoftObjectPath tempPath2 = TEXT("/Script/Engine.Texture2D'/Game/StarterContent/Textures/T_Wood_Oak_D.T_Wood_Oak_D'");
	TSharedPtr<FStreamableHandle> SynStreamhandle = UAssetManager::GetStreamableManager().RequestSyncLoad(tempPath2);
	if (SynStreamhandle)
	{
		UTexture2D* tempTex2 = Cast<UTexture2D>(SynStreamhandle->GetLoadedAsset());
		if (tempTex2)
		{
			GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Green, FString::Printf(TEXT("TexName:%s"), *tempTex2->GetName()));
		}
	}
2.4、异步加载

        异步加载就是在执行的时候会开启另外一个线程去后台加载资源,因此如果还是通过以下的方式去获取资源可能会不同步得到响应的结果。

	FSoftObjectPath tempPath1 = TEXT("/Script/Engine.Texture2D'/Game/StarterContent/Textures/T_Burst_M.T_Burst_M'");
	//异步加载1
	TSharedPtr<FStreamableHandle>AsyStreamhandle = UAssetManager::GetStreamableManager().RequestAsyncLoad(tempPath1);
	if (AsyStreamhandle)
	{
		UTexture2D* tempTex = Cast<UTexture2D>(AsyStreamhandle->GetLoadedAsset());
		if (tempTex)
		{
			GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Green, FString::Printf(TEXT("TexName:%s"), *tempTex->GetName()));
		}
	}

最好的方式是给异步加载后的结果一个回调函数,如下代码所示,这样在回调函数就可以异步

#include "Engine/StreamableManager.h"

TSharedPtr<FStreamableHandle>AsyStreamhandle;

AsyStreamhandle = UAssetManager::GetStreamableManager().RequestAsyncLoad(tempPath1, FStreamableDelegate::CreateUObject(this, &AMySoftRefActor::AysLoadCallBack));

void AMySoftRefActor::AysLoadCallBack()
{
	if (AsyStreamhandle)
	{
		UTexture2D* tempTex = Cast<UTexture2D>(AsyStreamhandle->GetLoadedAsset());
		if (tempTex)
		{
			GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Green, FString::Printf(TEXT("TexName:%s"), *tempTex->GetName()));
		}
	}
}

处理加载的资源。

三、总结

3.1、软引用和硬引用的区别。

3.2、异步加载最好指定一个回调函数。

<think>我们正在讨论的是如何在Unreal Engine异步加载资源或对象。根据引用[2]引用[3],我们知道异步加载通常用于避免游戏卡顿,比如在过场动画或过渡场景中加载下一个关卡或资源。引用[4]提供了一些异步加载关卡的参考笔记链接。 在Unreal Engine中,异步加载资源主要使用`FStreamableManager`类,而异步加载关卡则使用`UAssetManager`或`LoadPackageAsync`等方法。下面将分别介绍这两种情况。 ### 一、异步加载资源(如纹理、网格等) 1. **使用FStreamableManager** `FStreamableManager`是异步加载单个资源或一组资源的主要工具。它允许你指定一个回调函数,当资源加载完成时被调用。 **步骤:** a. 创建`FStreamableManager`实例(通常在游戏实例或全局对象中)。 b. 定义资源路径(`FSoftObjectPath`或`TSoftObjectPtr`)。 c. 调用`RequestAsyncLoad`并指定回调。 **示例代码:** ```cpp // 在头文件中声明 FStreamableManager StreamableManager; // 异步加载资源 void AMyActor::LoadResourceAsync() { TArray<FSoftObjectPath> PathsToLoad; PathsToLoad.Add(TEXT("/Game/Path/To/Your/Asset.YourAsset")); // 使用回调函数 StreamableManager.RequestAsyncLoad(PathsToLoad, FStreamableDelegate::CreateUObject(this, &AMyActor::OnResourceLoaded)); } void AMyActor::OnResourceLoaded() { // 资源加载完成后的处理 UObject* LoadedResource = StreamableManager.GetLoadedAsset(PathsToLoad[0]); // 使用资源... } ``` 2. **使用TSoftObjectPtr** 如果你已经有一个`TSoftObjectPtr`,可以直接使用`LoadSynchronous()`同步加载,但异步加载需要结合`FStreamableManager`。 ```cpp TSoftObjectPtr<UTexture2D> SoftTexture; FStreamableHandle Handle = StreamableManager.RequestAsyncLoad(SoftTexture.ToSoftObjectPath(), FStreamableDelegate::CreateUObject(this, &AMyActor::OnTextureLoaded)); ``` ### 二、异步加载关卡 异步加载关卡通常使用`ULevelStreaming`相关的功能,通过`UGameplayStatics::LoadStreamLevel`或`ULevelStreamingDynamic`。 1. **使用LoadStreamLevel** ```cpp UGameplayStatics::LoadStreamLevel(GetWorld(), TEXT("YourLevelName"), true, false, FLatentActionInfo(0, 0, TEXT("OnLevelLoaded"), this)); ``` 这种方法需要配合蓝图节点(Latent Action)使用,适合在蓝图中调用。 2. **使用ULevelStreamingDynamic** 更灵活的方式是使用`ULevelStreamingDynamic`,它允许在运行时动态加载关卡。 **示例代码:** ```cpp // 动态加载关卡 ULevelStreamingDynamic* LevelLoader = ULevelStreamingDynamic::LoadLevelInstance(GetWorld(), TEXT("YourLevelName"), FVector(0,0,0), FRotator(0,0,0)); if (LevelLoader) { LevelLoader->OnLevelLoaded.AddDynamic(this, &AMyActor::OnLevelLoaded); } ``` ```cpp void AMyActor::OnLevelLoaded() { // 关卡加载完成 } ``` 3. **使用UAssetManager异步加载关卡(UE4.20+)** 在较新版本中,推荐使用`UAssetManager`进行异步加载,尤其是对于大型资源或关卡。 ```cpp // 获取AssetManager UAssetManager& AssetManager = UAssetManager::Get(); FStreamableManager& Streamable = AssetManager.GetStreamableManager(); // 加载关卡资产 TSoftObjectPtr<UWorld> LevelPtr(TEXT("/Game/Path/To/Your/Level.YourLevel")); Streamable.RequestAsyncLoad(LevelPtr.ToSoftObjectPath(), FStreamableDelegate::CreateUObject(this, &AMyActor::OnLevelAssetLoaded)); ``` ```cpp void AMyActor::OnLevelAssetLoaded() { UWorld* LoadedLevel = Cast<UWorld>(StreamableManager.GetLoadedAsset(LevelPtr.ToSoftObjectPath())); if (LoadedLevel) { // 然后使用UGameplayStatics::OpenLevel打开关卡,或者使用UWorld::ServerTravel等 } } ``` ### 三、注意事项 1. **内存管理**:异步加载资源需要手动管理卸载,避免内存泄漏。 2. **错误处理**:检查加载是否成功,处理失败情况。 3. **性能优化**:避免同时加载过多资源,合理设置加载优先级。 ### 四、蓝图中的异步加载 在蓝图中,可以使用以下节点: - **异步加载资源**:`Async Load Asset`节点(需要指定资源路径)。 - **异步加载关卡**:`Load Stream Level`节点(带Latent Action)。 ### 总结 异步加载Unreal Engine中优化性能的关键技术,主要使用`FStreamableManager``UAssetManager`实现。根据资源类型(单个资源、蓝图、关卡)选择合适的方法,并注意内存管理[^2][^3][^4]。 §§相关问题§§ 1. 如何卸载异步加载资源? 2. 异步加载资源时如何显示加载进度? 3. 在异步加载过程中如何取消加载? 4. 异步加载同步加载的性能差异有多大?</think>在虚幻引擎中,异步加载资源或对象是优化性能的关键技术,可避免游戏卡顿。以下是核心实现方法: ### 一、异步加载资源(纹理、网格等) 1. **使用 FStreamableManager** ```cpp // 声明流式管理器 FStreamableManager StreamableManager; // 异步加载资源 void LoadAssetAsync() { FSoftObjectPath AssetPath(TEXT("/Game/Path/To/Asset.Asset")); StreamableManager.RequestAsyncLoad( AssetPath, FStreamableDelegate::CreateUObject(this, &AMyClass::OnAssetLoaded) ); } // 加载完成回调 void OnAssetLoaded() { UObject* LoadedAsset = StreamableManager.GetLoadedAsset(AssetPath); // 使用资源... } ``` 2. **使用异步加载节点(蓝图)** - 在蓝图中使用 **Async Load Asset** 节点 - 连接 **On Loaded** 事件处理加载完成逻辑 ### 二、异步加载关卡 1. **使用 LoadStreamLevel** ```cpp UGameplayStatics::LoadStreamLevel( GetWorld(), TEXT("LevelName"), true, // 是否阻塞 false, // 是否可见 FLatentActionInfo(0, 0, TEXT("OnLevelLoaded"), this) // 回调 ); ``` 2. **UAssetManager 高级加载** ```cpp // 获取资源管理器 UAssetManager& Manager = UAssetManager::Get(); // 创建加载批次 TArray<FName> BundlesToLoad; BundlesToLoad.Add(FName("LevelBundle")); // 启动异步加载 FStreamableHandle Handle = Manager.LoadPrimaryAssetsWithType( FPrimaryAssetType("LevelType"), BundlesToLoad, FStreamableDelegate::CreateUObject(this, &AMyClass::OnLevelLoaded) ); ``` ### 三、关键注意事项 1. **内存管理** ```cpp // 显式释放资源 StreamableManager.Unload(AssetPath); // 或 Handle->ReleaseHandle(); ``` 2. **加载状态监控** ```cpp if(Handle.IsValid() && !Handle->HasLoadCompleted()) { float Progress = Handle->GetProgress(); } ``` 3. **错误处理** ```cpp StreamableManager.RequestAsyncLoad(..., FStreamableDelegate::CreateUObject(this, &AMyClass::OnSuccess), FStreamableDelegate::CreateUObject(this, &AMyClass::OnFail) // 失败回调 ); ``` ### 四、最佳实践 1. **预加载策略**:在过场动画或过渡区域预加载资源(引用[3]中战神/暗黑血统的推门场景) 2. **优先级控制**:使用`FStreamableManager`的`RequestAsyncLoad`重载设置优先级 3. **资源分组**:通过`FStreamableManager`批量加载相关资源 4. **进度显示**:监听`GetProgress()`实现加载进度条(引用[4]) ### 五、调试技巧 1. 控制台命令: ```bash obj list class=Texture2D # 查看已加载纹理 list levels # 查看已加载关卡 ``` 2. 使用性能分析工具(Stat Unit, Unreal Insights) 异步加载可显著提升游戏流畅度,特别适用于开放世界、大型场景切换等场景[^2][^3]。建议结合引用[4]的GitHub示例项目进行实践。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值