[UE4 C++入门到进阶] 8.同步or异步资源加载
埃罗芒阿Sensal
编辑于 2021年02月25日 13:01
收录于文集
共15篇

一.常用资源加载方式

1.静态资源加载(同步)

静态资源加载只能在构造函数中进行.

(1)FObjectFinder

静态对象加载,可以用来在C++中加载资源(贴图,模型等等)

格式:ConstructorHelpers::FObjectFinder<资源类型>对象名(资源路径)

资源路径获取方式:

在资源上右键选择Copy Reference

粘贴出来如下:

StaticMesh'/Engine/VREditor/BasicMeshes/SM_Cube_01.SM_Cube_01'

我们只要 ' &#​39;(单引号)里面的路径/Engine/VREditor/BasicMeshes/SM_Cube_01.SM_Cube_01,然后将 资源名.资源名 中的.资源名去掉,最后得到/Engine/VREditor/BasicMeshes/SM_Cube_01

代码块
C++
自动换行
复制代码
ConstructorHelpers::FObjectFinder<UStaticMesh>Obj(TEXT("/Engine/VREditor/BasicMeshes/SM_Cube_01"));
复制成功

②判断资源加载结果

代码块
C++
自动换行
复制代码
//Succeeded()返回一个bool值,true则加载成功;false则加载失败
if(Obj.Succeeded())
{
  //取出被加载的资源对象
  UStaticMesh* LoadedObj=Obj.Object;
}
复制成功

(2)FClassFinder

静态类型加载,用于获得蓝图类的类型

格式:ConstructorHelpers::FClassFinder<C++基类类型>类型名(路径)

注意:C++中无法直接识别纯蓝图类,如果想要加载纯蓝图类,C++基类类型要设为其在C++中的基类类型,比如下面的NewBlueprint基类是Actor

代码块
C++
自动换行
复制代码
ConstructorHelpers::FClassFinder<AActor>Class(TEXT("/Game/NewBlueprint"));
复制成功

判断加载结果

代码块
C++
自动换行
复制代码
//判断是否加载成功
if(Class.Succeeded())
{
  //获得被加载的类型
  UClass* LoadedClass = Class.Class.Get();
  UE_LOG(LogTemp, Warning, TEXT("class name:%s"), *LoadedClass->GetName());
}
复制成功

2.动态加载(同步)

(1)LoadObject(常用来加载单个资源对象)

LoadObject作用与FObjectFinder类似,用于加载非蓝图类的资源对象,如模型,贴图等,不同之处在于LoadObject不像FObjectFinder一样只能在构造函数中使用,比较灵活

格式:LoadObject<基类类型>(NULL,资源路径)

代码块
C++
自动换行
复制代码
UMaterial* MatObj = LoadObject<UMaterial>(NULL,TEXT("/Game/NewMaterial"));
if (MatObj)
{
  UE_LOG(LogTemp, Warning, TEXT("mat name:%s"), *MatObj->GetName());
}
复制成功

可以看到材质资源被正常加载

(2)LoadClass(常用来在C++实例化Actor蓝图类)

LoadClass用于加载蓝图类型,最常用的用途就是加载一个基类为Actor的蓝图类的类型,然后配合SpawnActor将这个蓝图类实例化

注意事项:加载蓝图类型,需要在路径后面加一个"_C",比如原本蓝图类NewBlueprint 的资源路径为Blueprint'/Game/NewBlueprint.NewBlueprint' 我们需要先获得单引号内的路径,然后在最后加上"_C",最终路径为/Game/NewBlueprint.NewBlueprint_C

代码块
C++
自动换行
复制代码
UClass* Class = LoadClass<UObject>(NULL, TEXT("/Game/NewBlueprint.NewBlueprint_C"));
if (Class)
{
  UE_LOG(LogTemp, Warning, TEXT("class name:%s"), *Class->GetName());
}
复制成功

类型被正确加载出来,可以使用这个类型去生成类实例

二.资源加载进阶内容

1.资源软引用和硬引用

软引用:

软引用通常仅存储资源对象的string reference(资源路径),实际上并没有与资源产生耦合关系,即使软引用被加载到内存中,其引用的资源对象并不会随之被加载到内存.

本质上软引用就是一个 拥有存储资源对象string reference(资源路径)的成员变量结构体

比如下面的代码,只不过是在这基础上新增了很多功能性的api,方便进行开发而已.

代码块
C++
自动换行
复制代码
struct SoftRef
{
  //用于存储资源对象路径(如 /Game/Developers/Administrator/NewMap)
	FString AssetPath;
}
复制成功

软引用例子:

本例中我们持有一个静态网格资源的软引用,当工程运行时,该静态网格资源不会被加载到内存中

硬引用:

硬引用直接与资源对象耦合,假如硬引用被加载到内存中,那被引用的资源对象也会被加入到内存中(这是很不好的现象,会导致内存占用高)

ue的资源对象有四种引用方式,Object Reference最常用,这就是硬引用

硬引用例子:

如果包含这个硬引用的类被加载到内存中,当工程运行时,该静态网格也会被加载到内存中,即使我们没有用到它

下面说下硬引用软引用的区别:

硬引用过多,会导致运行时很多我们暂时用不到的资源也被一股脑加载到内存中,直接表现为:

程序启动时间长,启动时会卡顿很久(因为加载大量资源导致进程阻塞);

程序常驻内存占用多(因为很多我们用不到的资源也被加载到内存中).

软引用的好处体现在:

程序启动时间短(启动时加载的资源少);

程序常驻内存少(资源用到再加载,用完再释放)

更推荐使用软引用来引用资源对象

2.软引用介绍

本篇将介绍 FSoftObjectPath,FSoftClassPath,TSoftObjectPtr这三个软引用类型.

(1)FSoftObjectPath

①介绍

FSoftObjectPath的前身是FStringAssetReference(已被废弃),如果看到教程中使用的是FStringAssetReference,说明教程已经过时了.

FSoftObjectPath是一个包含了对象的资源路径(如/Engine/BasicShapes/Cone.Cone)的结构体,他保持对资源的软引用(并不会直接将资源加载到内存),可以在需要的时候才将其引用的资源加载.

②用法

代码块
C++
自动换行
复制代码
//FSoftObjectPath可以存储虚幻的资源对象软引用,比如模型,贴图,音频等等
UPROPERTY(BlueprintReadWrite,EditAnywhere)
  FSoftObjectPath AssetPath;
复制成功

FSoftObjectPath可以存储几乎所有ue资源对象的软引用

进阶用法:AllowedClasses

代码块
C++
自动换行
复制代码
/**进阶用法
 *元数据说明符AllowedClasses配合FSoftObjectPath可以起到filter(筛选过滤)作用
 *当指定目标类型之后,仅会显示目标类型及其子类的对象,比如下面代码目标类型设为
 *StaticMesh(静态网格资源),那么在蓝图中仅会显示该类型资源
 */
UPROPERTY(BlueprintReadWrite,EditAnywhere,meta=(AllowedClasses="StaticMesh"))
	FSoftObjectPath MeshAssetPath;
复制成功

可以看到,添加了筛选类型之后,MeshAssetPath的颜色也变成了与目标类型(静态网格)一致的蓝色

③例子

在这里以存储一个静态网格资源的FSoftObjectPath进行举例:

距离存储一个静态网格资源的软引用

可以通过FSoftObjectPath拿到被引用资源的路径,资源名等信息

可以通过FSoftObjectPath获得资源名称和资源路径

④常用用法还有:

ToString():效果与GetAssetPathString()一样

Reset():释放所持有资源的引用

更多用法转至官方API:

https://docs.unrealengine.com/en-US/API/Runtime/CoreUObject/UObject/FSoftObjectPath/index.html

(2)FSoftClassPath

①介绍

FSoftClassPathFSoftObjectPath的派生类,用于存储一个类型的软引用.

二者之间的关系有点类似LoadClass和LoadObject的关系,一个用于加载类型,一个用于加载资源,地位平等,分工不同

②用法

代码块
C++
自动换行
复制代码
UPROPERTY(BlueprintReadWrite, EditAnywhere)
	FSoftClassPath ClassPath;
复制成功

不添加过滤类型的话,FSoftClassPath几乎可以存储所有类型的软引用

可以存储Actor类软引用

可以存储Pawn类软引用

进阶用法:MetaClass

代码块
C++
自动换行
复制代码
/**
 *FSoftClassPath可以搭配元数据说明符(meta specifier)MetaClass一起使用
 *MetaClass起到filter(筛选,过滤)作用,指定筛选类型之后,在蓝图中将只会显示
 *指定的类型以及其派生类型
 *PawnClassPath是将过滤类型指定为Pawn,那么在蓝图中,备选项只会显示Pawn及其子类
 *
 */
UPROPERTY(BlueprintReadWrite,EditAnywhere,meta=(MetaClass="Pawn"))
	FSoftClassPath PawnClassPath;
复制成功

如果指定了筛选类型,比如上面代码中指定为Pawn类,那么会将Pawn及其派生类显示出来,其他类型不显示

可以看到经过筛选之后只显式Pawn及其子类

③例子

因为FSoftClassPathFSoftObjectPath的派生类,所以也具备获取被引用资源的资源名,路径等功能,在此不再举例.

(3)TSoftObjectPtr

TSoftObjectPtr本质是将FSoftObjectPtr(无法在蓝图中使用)包装并模板化而来,可以在蓝图中进行使用,它的前身是FAssetPtr,同样,如果发现教程讲解的是FAssetPtr,说明教程该更新了.

TSoftObjectPtr与蓝图中的SoftObjectReference是一回事

代码块
C++
自动换行
复制代码
//TSoftObjectPtr,与蓝图中的SoftObjectReference一样
UPROPERTY(BlueprintReadWrite,EditAnywhere)
  TSoftObjectPtr<UStaticMesh> MeshAssetPtr;
复制成功

蓝图中用法是选择StaticMesh的SoftObjectReference

TSoftObjectPtr其实是智能指针的一种,本例仅用来展示资源加载

代码块
JavaScript
自动换行
复制代码
//解引用获得资源指针
UStaticMesh* Mesh=MeshAssetPtr.Get();
//转换成FSoftObjectPath
FSoftObjectPath SoftPath=MeshAssetPtr.ToSoftObjectPath();
复制成功

以上是三种常用软引用类型介绍

3.StreamableManager同步加载资源

(1)RequestSyncLoad

代码块
C++
自动换行
复制代码
//FSoftObjectPath 作为LoadSynchronous的参数同步加载资源
UPROPERTY(BlueprintReadWrite,EditAnywhere,meta=(AllowedClasses="StaticMesh"))
	FSoftObjectPath MeshAsset;

/**
 *说明:
 *1.UAssetManager::Get() 获得资源管理类实例
 *2.资源管理类通过GetStreamableManager() 获得StreamableManager
 *3.StreamableManager使用其成员函数RequestSyncLoad(FSoftObjectPath Path)获得handle
 *4.handle通过GetLoadedAsset()获得被加载的资源
 */
UObject* LoadedObj=nullptr;
TSharedPtr<FStreamableHandle> Handle;
Handle=UAssetManager::Get().GetStreamableManager().RequestSyncLoad(MeshAsset);
LoadedObj = Handle->GetLoadedAsset();
复制成功

(2)LoadSynchronous

LoadSynchronous 内部使用RequestSyncLoad实现资源加载,所以可以直接使用LoadSynchronous来加载资源

代码块
C++
自动换行
复制代码
//FSoftObjectPath 作为LoadSynchronous的参数同步加载资源
UPROPERTY(BlueprintReadWrite,EditAnywhere,meta=(AllowedClasses="StaticMesh"))
	FSoftObjectPath MeshAsset;

/**
 *说明:
 *1.UAssetManager::Get() 获得资源管理类实例
 *2.资源管理类通过GetStreamableManager() 获得StreamableManager
 *3.StreamableManager使用其成员函数LoadSynchronous(FSoftObjectPath Path)同步加载资源
 */
UObject* LoadedObj=nullptr;
LoadedObj=UAssetManager::Get().GetStreamableManager().LoadSynchronus(MeshAsset);
复制成功

三.异步资源加载

SynchronousLoad将进行一次简单的块加载并返回对象。该方法或许适用于较小对象,但可能会导致主线程长时间停滞。在这种情况下,您将需要使用RequestAsyncLoad,它将异步加载一组资源并在完成后调用委托。

使用异步资源加载方式加载资源目的是为了防止资源过大导致主线程阻塞,如果用来夹在单个资源,意义不大,因为单个资源(除非体积很大)一般很快能就完成加载.隐私异步资源加载一般用于加载多个资源,在多个资源加载完成之后以回调方式通知.

使用StreamableManager进行异步资源加载需要两个要素:

FSoftObjectPath:资源软引用,存储资源对象的路径,用于资源加载;

TBaseDelegate<void>:回调函数handle(句柄),用于绑定回调函数,当资源加载完成时,会触发回调.

以下为伪代码:

代码块
C++
自动换行
复制代码
//待加载的资源列表
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "AssetLoadActor")
	TArray<FSoftObjectPath> AssetList;
复制成功

代码块
C++
自动换行
复制代码
//异步资源加载需要绑定回调函数,当资源加载完成之后会触发回调函数
FStreamableDelegate handle;
handle=FStreamableDelegate::CreateUObject(this, &AAssetLoader::OnLoadFinish);


/**
 *RequestAsyncLoad(FSoftObjectPath path,FStreamableDelegate handle)
 *1.TArray<FSoftObjectPath> AssetList 用于加载资源
 *2.FStreamableDelegate 用于绑定回调函数,在资源加载完成之后,回调会触发
 */
UAssetManager::Get().GetStreamableManager().RequestAsyncLoad(AssetList, handle);

//回调函数示例代码
void AAssetLoader::OnLoadFinish()
{
  //遍历输出被加载的资源的名称
  	for (auto asset:AssetList)
	{
      //FSoftObjectPath转换成FSoftObjectPtr
		FSoftObjectPtr SoftObjPtr = FSoftObjectPtr(asset);
      //通过Get获得被加载的资源
		UObject* Obj = SoftObjPtr.Get();
		if (Obj)
		{
			UE_LOG(LogTemp,Warning,TEXT("AssetName:%s"),*Obj->GetName());
		}
	}
}
复制成功

可以看到资源被正常加载并输出资源名

以上就是常用的资源加载方式以及进阶内容,喜欢的话请关注一下!