【UE4 C++ 基础知识】<11>资源的同步加载与异步加载
同步加载
同步加载会造成进程阻塞。
FObjectFinder / FClassFinder
在构造函数加载
- ConstructorHelpers::FObjectFinder
 - ConstructorHelpers::FClassFinder
参考 【UE4 C++ 基础知识】<10>资源的引用 
LoadObject
- 一般用来加载资源对象
 
UMaterial* M_Cube = LoadObject<UMaterial>(nullptr, TEXT("Material'/Game/Geometry/Meshes/CubeMaterial.CubeMaterial'"));
if (M_Cube)
{
	UE_LOG(LogTemp, Warning, TEXT("Material name:%s"), *M_Cube->GetName());
}
- 早期版本 
StaticLoadObject(),本处只作为记录,推荐使用 LoadObject 
soundCue = Cast<USoundCue>(StaticLoadObject(USoundCue::StaticClass(), nullptr, TEXT("SoundCue'/Game/Demo_Drone/Sound/explore_Cue.explore_Cue'")));
UGameplayStatics::PlaySoundAtLocation(this, soundCue,SweepResult.Location);
LoadClass
- 一般用来加载蓝图类, UClass*
 - 蓝图类的路径末尾加上
_C 
UClass* pClass = LoadClass<AActor>(nullptr, TEXT("Blueprint'/Game/CPPFunction/Load/BP_LoadActor.BP_LoadActor_C'"));
if (pClass)
{
	UE_LOG(LogTemp, Warning, TEXT("pClass name:%s"), *pClass->GetName());
}
TSubclassOf<AActor> BPClass = LoadClass<AActor>(nullptr, TEXT("Blueprint'/Game/CPPFunction/Load/BP_MyActor'"));
if (BPClass)
{
	UE_LOG(LogTemp, Warning, TEXT("BPClass name:%s"), *BPClass->GetName());
}
TryLoad
- 配合 FSoftObjectPath 使用
 - TryLoad 中调用 LoadObject,加载时需要调用Cast转换一下
 
FSoftObjectPath SoftObjectPaths_Mesh = FSoftObjectPath(TEXT("StaticMesh'/Game/CPPFunction/Load/StaticMesh.StaticMesh'"));
UStaticMesh* Mesh1 = Cast<UStaticMesh>(SoftObjectPaths_Mesh.TryLoad());
if (Mesh1)
{
	UE_LOG(LogTemp, Warning, TEXT("Mesh1 name:%s"), *Mesh1->GetName());
}
TryLoadClass
- 搭配 FSoftClassPath 使用
 - TryLoadClass 中调用了 LoadClass
 
FSoftClassPath SoftClassPath_Actor = FSoftClassPath(TEXT("Blueprint'/Game/CPPFunction/Load/BP_MyActor_SoftRef.BP_MyActor_SoftRef_C'"));
UClass* pClass_Actor = SoftClassPath_Actor.TryLoadClass<AActor>();
if (pClass_Actor)
{
	UE_LOG(LogTemp, Warning, TEXT("pClass_Actor name:%s"), *pClass_Actor->GetName());
}
FStreamableManager::LoadSynchronous
- FStreamableManager::内部调用 RequestSyncLoad
 - 参数中返回一个FStreamableHandle类型的指针
 
可加载非蓝图资源类
- 配合FStreamableManager、FSoftObjectPath 使用
 - 配合FStreamableManager、TSoftObjectPtr使用
 
// 配合 FSoftObjectPath 使用 方法一
FSoftObjectPath SoftObjectPaths_Mesh1 = FSoftObjectPath(TEXT("StaticMesh'/Game/CPPFunction/Load/StaticMesh.StaticMesh'"));
UStaticMesh* StaticMesh1 = UAssetManager::GetStreamableManager().LoadSynchronous<UStaticMesh>(SoftObjectPaths_Mesh1);
if (StaticMesh1)
{
	UE_LOG(LogTemp, Warning, TEXT("Mesh1 name:%s"), *StaticMesh1->GetName());
}
// 配合 FSoftObjectPath 使用 方法二
FStreamableManager& streamableManager = UAssetManager::GetStreamableManager();
FSoftObjectPath SoftObjectPaths_Mesh2 = FSoftObjectPath(TEXT("StaticMesh'/Game/CPPFunction/Load/StaticMesh_Soft.StaticMesh_Soft'"));
UStaticMesh* StaticMesh2 = streamableManager.LoadSynchronous<UStaticMesh>(SoftObjectPaths_Mesh2);
if (StaticMesh2)
{
	UE_LOG(LogTemp, Warning, TEXT("Mesh2 name:%s"), *StaticMesh2->GetName());
}
// 配合 TSoftObjectPtr<T> 使用
FSoftObjectPath SoftObjectPaths_Mesh3 = FSoftObjectPath(TEXT("StaticMesh'/Game/CPPFunction/Load/StaticMesh_Soft2.StaticMesh_Soft2'"));
TSoftObjectPtr<UStaticMesh> SoftObjectPtr_Mesh = TSoftObjectPtr<UStaticMesh>(SoftObjectPaths_Mesh3);
UStaticMesh* StaticMesh3 = streamableManager.LoadSynchronous(SoftObjectPtr_Mesh);//保持良好习惯<UStaticMesh>
if (StaticMesh3)
{
	UE_LOG(LogTemp, Warning, TEXT("Mesh3 name:%s"), *StaticMesh3->GetName());
}
也可加载蓝图类为 UClass*
- 配合FStreamableManager、TSoftObjectPtr使用
 - 配合FStreamableManager、TSoftClassPtr使用
 
FSoftObjectPath SoftObjectPaths_Actor1 = FSoftObjectPath(TEXT("Blueprint'/Game/CPPFunction/Load/BP_MyActor.BP_MyActor_C'"));
UClass* BPClass1 = UAssetManager::GetStreamableManager().LoadSynchronous<UClass>(SoftObjectPaths_Actor1);
if (BPClass1)
{
	UE_LOG(LogTemp, Warning, TEXT("BPClass1 name:%s"), *BPClass1->GetName());
}
// 配合 FSoftObjectPath 使用 方法二
FStreamableManager& streamableManager = UAssetManager::GetStreamableManager();
FSoftObjectPath SoftObjectPaths_Actor2 = FSoftObjectPath(TEXT("Blueprint'/Game/CPPFunction/Load/BP_MyActor_SoftRef.BP_MyActor_SoftRef_C'"));
UClass* BPClass2 = streamableManager.LoadSynchronous<UClass>(SoftObjectPaths_Actor2);
if (BPClass2)
{
	UE_LOG(LogTemp, Warning, TEXT("BPClass2 name:%s"), *BPClass2->GetName());
}
// 配合 TSoftObjectPtr<T> 使用
FSoftObjectPath SoftObjectPaths_Actor3 = FSoftObjectPath(TEXT("Blueprint'/Game/CPPFunction/Load/BP_MyActor_SoftRef2.BP_MyActor_SoftRef2_C'"));
TSoftObjectPtr<UClass> SoftObjectPtr_Actor = TSoftObjectPtr<UClass>(SoftObjectPaths_Actor3);
UClass* BPClass3 = streamableManager.LoadSynchronous(SoftObjectPtr_Actor); //保持良好习惯可添加<UClass>
if (BPClass3)
{
	UE_LOG(LogTemp, Warning, TEXT("BPClass3 name:%s"), *BPClass3->GetName());
}
FStreamableManager::RequestSyncLoad
- 配合 FStreamableManager、FSoftObjectPath 使用
 - 返回一个FStreamableHandle类型的指针
 - TSharedPtr 通过 GetLoadedAsset() 获取单个资源
 - TSharedPtr 通过 GetLoadedAssets() 获取多个资源
 
// 获取单个资源 方法一
FSoftObjectPath SoftObjectPaths_Mesh1 = FSoftObjectPath(TEXT("StaticMesh'/Game/CPPFunction/Load/StaticMesh.StaticMesh'"));
TSharedPtr<FStreamableHandle> Handle1  = UAssetManager::GetStreamableManager().RequestSyncLoad(SoftObjectPaths_Mesh1);
if (Handle1.IsValid())
{
	UStaticMesh* StaticMesh1 = Cast<UStaticMesh>(Handle1->GetLoadedAsset());
	UE_LOG(LogTemp, Warning, TEXT("Mesh1 name:%s"), *StaticMesh1->GetName());
}
// 获取单个资源 方法二
FStreamableManager& streamableManager = UAssetManager::GetStreamableManager();
FSoftObjectPath SoftObjectPaths_Mesh2 = FSoftObjectPath(TEXT("StaticMesh'/Game/CPPFunction/Load/StaticMesh_Soft.StaticMesh_Soft'"));
TSharedPtr<FStreamableHandle> Handle2 = streamableManager.RequestSyncLoad(SoftObjectPaths_Mesh2);
if (Handle2.IsValid())
{
	UStaticMesh* StaticMesh2 = Cast<UStaticMesh>(Handle2->GetLoadedAsset());
	UE_LOG(LogTemp, Warning, TEXT("Mesh1 name:%s"), *StaticMesh2->GetName());
}
// 获取多个资源
TArray<FSoftObjectPath> SoftObjectPaths;
SoftObjectPaths.Add(SoftObjectPaths_Mesh1);
SoftObjectPaths.Add(SoftObjectPaths_Mesh2);
TSharedPtr<FStreamableHandle> Handle3 = streamableManager.RequestSyncLoad(SoftObjectPaths);
{
	TArray<UObject*> Objects;
	Handle3->GetLoadedAssets(Objects);
	for (UObject* item : Objects)
	{
		UStaticMesh* StaticMesh3 = Cast<UStaticMesh>(item);
		UE_LOG(LogTemp, Warning, TEXT("GetLoadedAssets(), item name:%s"), *StaticMesh3->GetName());
	}
}
异步加载
- 为了避免阻塞主线程,可以使用异步加载的方式来加载资源
 - 异步加载完成后,可以设置回调函数
 - 创建 
FStreamableManager,建议将它放在某类全局游戏单件对象中,如使用GameSingletonClassName在DefaultEngine.ini中指定的对象 
FStreamableManager::RequestAsyncLoad
- 返回一个 FStreamableHandle 类型的指针
 
异步加载非蓝图类资源 FSoftObjectPath
单文件加载
UPROPERTY(EditAnywhere, Category = "SoftObjectPath", meta = (AllowedClasses = "StaticMesh"))
FSoftObjectPath SingeleObject; UFUNCTION()
void OnSingleAssetLoadFinshed();
// 函数内部分语句
FStreamableManager& streamableManager = UAssetManager::GetStreamableManager();
FStreamableDelegate streamableDelegate = FStreamableDelegate::CreateUObject(this, &ALoadActor::OnSingleAssetLoadFinshed);
streamableManager.RequestAsyncLoad(SingeleObject, streamableDelegate); // 要回调的函数
void ALoadActor::OnSingleAssetLoadFinshed()
{
FSoftObjectPtr SoftObjPtr = FSoftObjectPtr(SingeleObject);
UStaticMesh* mesh = Cast<UStaticMesh>(SoftObjPtr.Get());
if (mesh)
{
UE_LOG(LogTemp, Warning, TEXT("mesh name:%s"), *mesh->GetName());
}
}

多文件加载
方法一 配合
FSoftObjectPtr- FSoftObjectPtr是一个结构体,是一种指向UObject的弱指针。无法在蓝图中使用
 - TSoftObjectPtr是一个模板类,是通用FSoftObjectPtr的模块化包装器
 
UPROPERTY(EditAnywhere, Category="SoftObjectPath", meta = (AllowedClasses = "StaticMesh"))
TArray<FSoftObjectPath> ObjectList1; UFUNCTION()
void OnMultiAssetsLoadFinshed1();
// 函数内部分语句
FStreamableManager& streamableManager = UAssetManager::GetStreamableManager();
FStreamableDelegate streamableDelegate = FStreamableDelegate::CreateUObject(this, &ALoadActor::OnMultiAssetsLoadFinshed1);
streamableManager.RequestAsyncLoad(ObjectList1, streamableDelegate); // 要回调的函数
void ALoadActor::OnMultiAssetsLoadFinshed1()
{
for (auto AssetItem : ObjectList1)
{
FSoftObjectPtr SoftObjPtr = FSoftObjectPtr(AssetItem); //此处也可用 TSoftObjectPtr<T>
UStaticMesh* mesh = Cast<UStaticMesh>(SoftObjPtr.Get());
if (mesh)
{
UE_LOG(LogTemp, Warning, TEXT("mesh name:%s"), *mesh->GetName());
}
}
}

方法二
配合TSoftObjectPtr<T>UPROPERTY(EditAnywhere, Category = "SoftObjectPath")
TArray<TSoftObjectPtr<UTexture2D>> ObjectList2; UFUNCTION()
void OnMultiAssetsLoadFinshed2();
// 函数内部分语句
FStreamableManager& streamableManager = UAssetManager::GetStreamableManager();
FStreamableDelegate streamableDelegate;
streamableDelegate.BindUObject(this, &ALoadActor::OnMultiAssetsLoadFinshed2); TArray<FSoftObjectPath> SoftPathList;
for (int32 i=0; i<ObjectList2.Num(); i++)
{
SoftPathList.Add(ObjectList2[i].ToSoftObjectPath());
}
streamableManager.RequestAsyncLoad(SoftPathList, streamableDelegate); // 要回调的函数
void ALoadActor::OnMultiAssetsLoadFinshed2()
{
for (auto AssetItem : ObjectList2)
{
UTexture2D* ItemTex = AssetItem.Get();
if (ItemTex)
{
UE_LOG(LogTemp, Warning, TEXT("Texture2D name:%s"), *ItemTex->GetName());
}
}
}
异步加载蓝图类
单文件加载 FSoftClassPath 、TSoftClassPtr
- 测试 
TArray<FSoftClassPath>加载多个蓝图类编译不通过 
UPROPERTY(EditAnywhere, Category = "SoftClassPath", meta = (MetaClass = "Actor"))
FSoftClassPath SingleClassPath; UFUNCTION()
void OnSingleClassLoadFinshed();
// 函数内部分语句
FStreamableManager& streamableManager = UAssetManager::GetStreamableManager();
FStreamableDelegate streamableDelegate = FStreamableDelegate::CreateUObject(this, &ALoadActor::OnSingleClassLoadFinshed);
streamableManager.RequestAsyncLoad(SingleClassPath, streamableDelegate); // 函数内部分语句
void ALoadActor::OnSingleClassLoadFinshed()
{
TSoftClassPtr<AActor> ItemPtr = TSoftClassPtr<AActor>(SingleClassPath);
UClass* ItemClass = ItemPtr.Get();
if (ItemClass)
{
UE_LOG(LogTemp, Warning, TEXT("Actor name:%s"), *ItemClass->GetName());
}
}

- 测试 
 
卸载资源
自动回收
- 当对象失去饮用后会被自动释放。
 - 在异步回调结束后,对象会被标记可回收,此时使用 
ForceGC可销毁对象 
手动回收
使用 FStreamableManager::Unload()
- LoadSynchronous()、RequestSyncLoad()、RequestAsyncLoad() 默认bManageActiveHandle 参数为false,表示自动管理内存;当设为true,表示常驻内存直到手动释放
 - FStreamableManager::Unload()会Release掉和当前资源相关的所有FStreamableHandle
 - 编辑器模式下会一直常驻内存,打包版本中才会生效
 
FStreamableManager& streamableManager = UAssetManager::GetStreamableManager();
streamableManager.Unload(FSoftObjectPath(AssetPath));
//需要立即回收的话
GEngine->ForceGarbageCollection(true);
//GetWorld()->ForceGarbageCollection(true);
使用 FStreamableHandle::ReleaseHandle()
- 异步加载时,如果资源还没加载完成就执行ReleaseHandle()(假设加载时bManageActiveHandle为true),比如在执行回调函数之前执行ReleaseHandle,那么当资源加载完成后(回调函数执行之后),会自动从内存中回收。不过该对象在回调函数中仍然有效,除非在回调函数内ForceGC。
 - 编辑器模式下会一直常驻内存,打包版本中才会生效
 
TSharedPtr<FStreamableHandle> Handle1  = UAssetManager::GetStreamableManager().RequestSyncLoad(SoftObjectPaths_Mesh1);
Handle1->ReleaseHandle();
使用 ConditionalBeginDestroy()
- 编辑器模式下卸载后,对象从内存中销毁,无法再次Load,需要重启编辑器
 
UMaterial* M_Cube = LoadObject<UMaterial>(nullptr, TEXT("Material'/Game/Geometry/Meshes/CubeMaterial.CubeMaterial'"));
if (M_Cube)
{
	M_Cube->ConditionalBeginDestroy();
	M_Cube = nullptr;
	GetWorld()->ForceGarbageCollection();
}
参考
【UE4 C++ 基础知识】<11>资源的同步加载与异步加载的更多相关文章
- 【UE4 C++ 基础知识】<10>资源的引用
		
2种引用方式 硬引用(Hard Reference) 即对象 A 引用对象 B,并导致对象 B 在对象 A 加载时加载 硬引用过多会导致运行时很多暂时用不到的资源也被加载到内存中 大量资源会导致进程阻 ...
 - 【UE4 C++ 基础知识】<3> 基本数据类型、字符串处理及转换
		
基本数据类型 TCHAR TCHAR就是UE4通过对char和wchar_t的封装 char ANSI编码 wchar_t 宽字符的Unicode编码 使用 TEXT() 宏包裹作为字面值 TCHAR ...
 - 【UE4 C++ 基础知识】<12> 多线程——FRunnable
		
概述 UE4里,提供的多线程的方法: 继承 FRunnable 接口创建单个线程 创建 AsyncTask 调用线程池里面空闲的线程 通过 TaskGraph 系统来异步完成一些自定义任务 支持原生的 ...
 - javascript 同步加载与异步加载
		
HTML 4.01 的script属性 charset: 可选.指定src引入代码的字符集,大多数浏览器忽略该值. defer: boolean, 可选.延迟脚本执行,相当于将script标签放入页面 ...
 - Javascript 文件的同步加载与异步加载
		
HTML 4.01 的script属性 charset: 可选.指定src引入代码的字符集,大多数浏览器忽略该值.defer: boolean, 可选.延迟脚本执行,相当于将script标签放入页面b ...
 - 关于requireJS的同步加载和异步加载
		
这篇随笔主要记录require('name')和require(['name1','name2'])在同步和异步加载使用的区别 1.require('name')同步加载模块的形式 define(fu ...
 - AJAX中的同步加载与异步加载
		
AJAX是四个单词的简写,其中Asynchronous即异步的意思,异步的链接可以同时发起多个,并且不会阻止JS代码执行.与之对应的概念是同步,同步的链接在同一时刻只会有一个,并且会阻止后续JS代码的 ...
 - Jquery前端分页插件pagination同步加载和异步加载
		
上一篇文章介绍了Jquery前端分页插件pagination的基本使用方法和使用案例,大致原理就是一次性加载所有的数据再分页.https://www.jianshu.com/p/a1b8b1db025 ...
 - C# 篇基础知识11——泛型和集合
		
.NET提供了一级功能强大的集合类,实现了多种不同类型的集合,可以根据实际用途选择恰当的集合类型. 除了数组 Array 类定义在System 命名空间中外,其他的集合类都定义在System.Coll ...
 
随机推荐
- Python - 通过PyYaml库操作YAML文件
			
PyYaml简单介绍 Python的PyYAML模块是Python的YAML解析器和生成器 它有个版本分水岭,就是5.1 读取YAML5.1之前的读取方法 def read_yaml(self, pa ...
 - jooq使用示例
			
一.说明 最近使用的项目,采用了jooq. 通过学习api文档和自我调试,写了一些代码,在此处进行记录. 二.代码 一切尽在代码中-- 参考文档:http://www.jooq.org/doc/3.1 ...
 - Apache网页优化
			
目录: 一.Apache网页优化概述 二.网页压缩 三.网页缓存 四.隐藏版本信息 五.Apache防盗链 一.Apache网页优化概述 在企业中,部署Apache后只采用默认的配置参数,会引发网站很 ...
 - go语言游戏服务端开发(二)——网络通信
			
一.网络层 网络游戏客户端除了全局登录使用http请求外,一般通过socket长连接与服务端保持连接.go语言的net包提供网络socket长连接相关操作. 对于服务端,一般经历 Listen.Acc ...
 - 浅谈 Xamarin Community Toolkit 的未来发展
			
.NET MAUI会在今年晚些时候发布,我们也很高兴和大家一起分享我们对Xamarin Community Toolkit的计划! 这包括 .NET MAUI Community Toolkit.Xa ...
 - Android——菜单(Menu)
			
菜单的运用在Android中很常见,今天就两节体育课,闲下来也想认真的学一学,正好项目中也会有应用.我是跟着菜鸟教程进行学习的,我相应的粘了一些我自己认为比较重要的,以供方便记录学习. 本章给大家带来 ...
 - google插件网页播放mp4代码
			
<script src="http://html5media.googlecode.com/svn/trunk/src/html5media.min.js"></ ...
 - Jmeter系列(3) - 静默压测
			
前言 Windows环境 简述 静默 : 脱离UI运⾏JMeter压测,用命令行方式运行性能测试脚本好处:命令运⾏更容易"搞事情"命令格式: jmeter –n –t $jmx_f ...
 - js中date类型的格式转化为yyyy-MM-dd HH:mm:ss的String类型
			
在vue中或其他框架中可以在Date的原型链中添加Format的方法,如ruoyi可以写在main.js中更好,如果写在utils还需要去导入包. 正常的js直接放到utils.js就好 Date.p ...
 - SonarQube汉化
			
SonarQube安装后默认是英文,如果不习惯看英文,可以进行汉化,官方提供了汉化插件. 登录后,汉化步骤如下: 需要点击了解风险,不然查询到插件后没有install按钮 搜索Chinese,找到插件 ...