UE4运用C++和框架开发坦克大战教程笔记(十四)(第43~45集)
- 43. 单个加载 UObject 功能
- 获取资源 URL 链接
- 实现异步加载单个 UObject 类型资源
- 44. 批量加载 UObject 功能
- 测试加载单个 UObject 资源
- 批量加载多个同类的 UObject 资源
- 45. 单个加载 UClass 功能
- 测试加载多个 UObject 资源
- 异步加载单个 UClass 类型的资源
43. 单个加载 UObject 功能
获取资源 URL 链接
继续来补充根据资源类型生成资源的逻辑,在 DDWealth 里添加获取 URL 的方法。
DDWealth.h
public:
// 外部方法单纯获取资源链接
// 返回单个 URL
FWealthURL* GainWealthURL(FName WealthName);
// 返回一个种类的资源的 URL
void GainWealthURL(FName WealthKind, TArray<FWealthURL*>& OutURL);
DDWealth.cpp
FWealthURL* UDDWealth::GainWealthURL(FName WealthName)
{
// 从 DataAsset 里遍历获取对应名字的资源的 URL
for (int i = 0; i < WealthData.Num(); ++i) {
for (int j = 0; j < WealthData[i]->WealthURL.Num(); ++j) {
if (WealthData[i]->WealthURL[j].WealthName.IsEqual(WealthName))
return &WealthData[i]->WealthURL[j];
}
}
return NULL;
}
void UDDWealth::GainWealthURL(FName WealthKind, TArray<FWealthURL*>& OutURL)
{
// 从 DataAsset 里遍历获取对应种类名字的全部资源的 URL
for (int i = 0; i < WealthData.Num(); ++i) {
for (int j = 0; j < WealthData[i]->WealthURL.Num(); ++j) {
if (WealthData[i]->WealthURL[j].WealthKind.IsEqual(WealthKind))
OutURL.Push(&WealthData[i]->WealthURL[j]);
}
}
}
建立 DDWealth – DDModule – DDOO – 对象 的调用链。
DDModule.h
public:
// 外部方法单纯获取资源链接
FWealthURL* GainWealthURL(FName WealthName);
void GainWealthURL(FName WealthKind, TArray<FWealthURL*>& OutURL);
DDModule.cpp
FWealthURL* UDDModule::GainWealthURL(FName WealthName)
{
return Wealth->GainWealthURL(WealthName);
}
void UDDModule::GainWealthURL(FName WealthKind, TArray<FWealthURL*>& OutURL)
{
Wealth->GainWealthURL(WealthKind, OutURL);
}
DDOO.h
protected:
// 外部方法单纯获取资源链接
FWealthURL* GainWealthURL(FName WealthName);
void GainWealthURL(FName WealthKind, TArray<FWealthURL*>& OutURL);
DDOO.cpp
FWealthURL* IDDOO::GainWealthURL(FName WealthName)
{
return IModule->GainWealthURL(WealthName);
}
void IDDOO::GainWealthURL(FName WealthKind, TArray<FWealthURL*>& OutURL)
{
IModule->GainWealthURL(WealthKind, OutURL);
}
如果编译通过则说明写好了,现在所有的对象都可以通过这两个方法获取目标资源的 URL。刚刚写的这些代码结构也比较简单,此处就跳过验证环节。
实现异步加载单个 UObject 类型资源
异步加载需要用到引擎提供的 StreamableManager,所以我们声明一个存放 Object 资源数据的结构体,里面还包含着 FStreamableHandle 句柄,以便参与异步加载。
在 DDWealth 内的逻辑如下:结构体作为加载节点,并且声明一个它的数组作为加载节点队列。Tick()
内检测到队列内的节点是否已经加载完毕,是则将其从队列里删除。对象只需要调用 LoadObjectWealth()
就可以开始异步加载,并且生成目标 Object 资源的加载节点后将其放入队列。
DDWealth.h
#include "Engine/StreamableManager.h" // 引入头文件
#include "DDWealth.generated.h"
// 加载单个 Object 资源的节点
struct ObjectSingleLoadNode;
UCLASS()
class DATADRIVEN_API UDDWealth : public UObject, public IDDMM
{
GENERATED_BODY()
public:
// 加载 Object 类型资源接口
void LoadObjectWealth(FName WealthName, FName ObjectName, FName FunName);
// 加载 Object 类型的同种类的所有资源
void LoadObjectWealthKind(FName WealthKind, FName ObjectName, FName FunName);
protected:
// 获取 Object 资源结构体
FObjectWealthEntry* GetObjectSingleEntry(FName WealthName);
TArray<FObjectWealthEntry*> GetObjectKindEntry(FName WealthKind);
// 处理加载单个 Object 节点的方法,放在 Tick() 里
void DealObjectSingleLoadStack();
protected:
// 加载器,用于异步加载
FStreamableManager WealthLoader;
// 加载节点队列
TArray<ObjectSingleLoadNode*> ObjectSingleLoadStack;
protected:
// 加载 UObject 反射回调函数,方便返回已经生成的资源
DDOBJFUNC_TWO(BackObjectWealth, FName, BackName, UObject*, BackWealth);
DDOBJFUNC_TWO(BackObjectWealthKind, TArray<FName>, BackNames, TArray<UObject*>, BackWealths);
};
DDWealth.cpp
struct ObjectSingleLoadNode
{
// 加载句柄
TSharedPtr<FStreamableHandle> WealthHandle;
// 资源结构体
FObjectWealthEntry* WealthEntry;
// 请求对象名
FName ObjectName;
// 回调方法名
FName FunName;
// 构造函数
ObjectSingleLoadNode(TSharedPtr<FStreamableHandle> InWealthHandle, FObjectWealthEntry* InWealthEntry, FName InObjectName, FName InFunName)
{
WealthHandle = InWealthHandle;
WealthEntry = InWealthEntry;
ObjectName = InObjectName;
FunName = InFunName;
}
};
void UDDWealth::WealthTick(float DeltaSeconds)
{
// 在 Tick() 里检查队列中的加载节点是否完成
DealObjectSingleLoadStack();
}
void UDDWealth::LoadObjectWealth(FName WealthName, FName ObjectName, FName FunName)
{
// 获取资源结构体
FObjectWealthEntry* WealthEntry = GetObjectSingleEntry(WealthName);
// 如果没有这个名字对应的资源结构体
if (!WealthEntry) {
DDH::Debug() << ObjectName << " Get Null Wealth : " << WealthName << DDH::Endl();
return;
}
// 如果资源不可用
if (!WealthEntry->WealthPath.IsValid()) {
DDH::Debug() << ObjectName << " Get UnValid Wealth : " << WealthName << DDH::Endl();
return;
}
// 如果资源已经加载
if (WealthEntry->WealthObject) {
// 直接返回已经存在的资源给对象(整个 BackObjectWealth 方法已经由反射系统的宏生成)
BackObjectWealth(ModuleIndex, ObjectName, FunName, WealthName, WealthEntry->WealthObject);
}
else {
// 进行异步加载
TSharedPtr<FStreamableHandle> WealthHandle = WealthLoader.RequestAsyncLoad(WealthEntry->WealthPath);
// 添加新节点到加载序列
ObjectSingleLoadStack.Push(new ObjectSingleLoadNode(WealthHandle, WealthEntry, ObjectName, FunName));
}
}
// 批量加载同种类 UObject 暂时先不写,留到下一节课
void UDDWealth::LoadObjectWealthKind(FName WealthKind, FName ObjectName, FName FunName)
{
}
FObjectWealthEntry* UDDWealth::GetObjectSingleEntry(FName WealthName)
{
for (int i = 0; i < WealthData.Num(); ++i) {
for (int j = 0; j < WealthData[i]->ObjectWealthData.Num(); ++j) {
if (WealthData[i]->ObjectWealthData[j].WealthName.IsEqual(WealthName))
return &(WealthData[i]->ObjectWealthData[j]);
}
}
return NULL;
}
TArray<FObjectWealthEntry*> UDDWealth::GetObjectKindEntry(FName WealthKind)
{
TArray<FObjectWealthEntry*> WealthGroup;
for (int i = 0; i < WealthData.Num(); ++i) {
for (int j = 0; j < WealthData[i]->ObjectWealthData.Num(); ++j) {
if (WealthData[i]->ObjectWealthData[j].WealthKind.IsEqual(WealthKind))
WealthGroup.Push(&(WealthData[i]->ObjectWealthData[j]));
}
}
return WealthGroup;
}
void UDDWealth::DealObjectSingleLoadStack()
{
// 定义加载完成的序列
TArray<ObjectSingleLoadNode*> CompleteStack;
for (int i = 0; i < ObjectSingleLoadStack.Num(); ++i) {
// 判断是否已经加载完成
if (ObjectSingleLoadStack[i]->WealthHandle->HasLoadCompleted()) {
// 设置对应资源赋值给 WealthObject
ObjectSingleLoadStack[i]->WealthEntry->WealthObject = ObjectSingleLoadStack[i]->WealthEntry->WealthPath.ResolveObject();
// 返回资源给对象
BackObjectWealth(ModuleIndex, ObjectSingleLoadStack[i]->ObjectName, ObjectSingleLoadStack[i]->FunName, ObjectSingleLoadStack[i]->WealthEntry->WealthName, ObjectSingleLoadStack[i]->WealthEntry->WealthObject);
// 添加已经加载完成的节点到临时序列
CompleteStack.Push(ObjectSingleLoadStack[i]);
}
}
// 销毁已经完成的节点
for (int i = 0; i < CompleteStack.Num(); ++i) {
// 移除出节点序列
ObjectSingleLoadStack.Remove(CompleteStack[i]);
// 释放内存
delete CompleteStack[i];
}
}
剩余部分我们留到下一节课来实现。
44. 批量加载 UObject 功能
测试加载单个 UObject 资源
依旧是建立 DDWealth – DDModule – DDOO – 对象 的调用链。
虽然我们还没写批量加载同种类 UObject 资源的逻辑,但是也可以顺便补充上这个调用链。
DDModule.h
public:
// 加载 Object 类型资源接口
void LoadObjectWealth(FName WealthName, FName ObjectName, FName FunName);
void LoadObjectWealthKind(FName WealthKind, FName ObjectName, FName FunName);
DDModule.cpp
void UDDModule::LoadObjectWealth(FName WealthName, FName ObjectName, FName FunName)
{
Wealth->LoadObjectWealth(WealthName, ObjectName, FunName);
}
void UDDModule::LoadObjectWealthKind(FName WealthKind, FName ObjectName, FName FunName)
{
Wealth->LoadObjectWealthKind(WealthKind, ObjectName, FunName);
}
DDOO 不需要传 ObjectName,因为它本身保存着这个变量。
DDOO.h
protected:
// 加载 Object 类型资源接口
void LoadObjectWealth(FName WealthName, FName FunName);
void LoadObjectWealthKind(FName WealthKind, FName FunName);
DDOO.cpp
void IDDOO::LoadObjectWealth(FName WealthName, FName FunName)
{
IModule->LoadObjectWealth(WealthName, GetObjectName(), FunName);
}
void IDDOO::LoadObjectWealthKind(FName WealthKind, FName FunName)
{
IModule->LoadObjectWealthKind(WealthKind, GetObjectName(), FunName);
}
在项目的 .Bulid.cs 文件里添加对 UMG 的依赖。因为我们打算在 Widget 里放一个 Image 控件来让其显示加载的图片资源,作为功能的验证过程。
RaceCarFrame.Build.cs
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject",
"Engine", "InputCore",
"PhysXVehicles", "HeadMountedDisplay",
"DataDriven", "UMG" }); // 添加依赖
打开项目,准备测试上面写的异步加载单个 UObject 功能。我们打算异步加载一张图片资源,并将其应用到一个 Image 控件上。
创建一个以 DDUserWidget 为基类的 C++ 类,目标模组为项目,命名为 LoadWealthWidget。创建完毕后进行代码编译。
在 Blueprint 文件夹下,基于 LoadWealthWidget 创建一个蓝图,命名为 LoadWealthWidget_BP。
把 HUDData 里,之前配置的目标 Widget 换成 LoadWealthWidget_BP,Object Name 改成 LoadWealthWidget。
将 LoadWealthWidget_BP 修改成如下图:
随后在 LoadWealthWidget 里添加加载 UObject 资源的逻辑,并且将加载到的图片资源放进 Image 里。
LoadWealthWidget.h
class UImage;
UCLASS()
class RACECARFRAME_API ULoadWealthWidget : public UDDUserWidget
{
GENERATED_BODY()
public:
virtual void DDInit() override;
virtual void DDLoading() override;
UFUNCTION()
void LoadSingleTexture(FName BackName, UObject* BackWealth);
public:
UPROPERTY(Meta = (BindWidget))
UImage* ViewImage;
};
LoadWealthWidget.cpp
#include "Components/Image.h" // 引入头文件
void ULoadWealthWidget::DDInit()
{
Super::DDInit();
AddToViewport(0);
}
void ULoadWealthWidget::DDLoading()
{
Super::DDLoading();
// 调用加载资源的方法,并且回调函数会被调用
LoadObjectWealth("ViewImage1", "LoadSingleTexture");
}
void ULoadWealthWidget::LoadSingleTexture(FName BackName, UObject* BackWealth)
{
ViewImage->SetBrushFromTexture(Cast<UTexture2D>(BackWealth));
}
编译后运行,可看见左上角显示如下图:
这是因为我们没有设置这个 “ViewImage1” 名称对应的目标图片资源。再次打开 HUDData,设置如下:
再次运行,可见左上角的 Image 配置上了相应的图片。说明异步加载单个 UObject 资源的逻辑写好了。
批量加载多个同类的 UObject 资源
逻辑其实跟加载单个 UObject 资源差不多,只是读取和加载利用 for 循环执行多次。
新声明一个 UObject 种类加载节点,里面要声明存放多个资源结构体的数组。
DDWealth.h
struct ObjectKindLoadNode; // 添加结构体声明
UCLASS()
class DATADRIVEN_API UDDWealth : public UObject, public IDDMM
{
GENERATED_BODY()
protected:
// 处理批量加载 Object 节点的方法
void DealObjectKindLoadStack();
protected:
TArray<ObjectKindLoadNode*> ObjectKindLoadStack;
};
DDWealth.cpp
struct ObjectKindLoadNode
{
// 加载句柄
TSharedPtr<FStreamableHandle> WealthHandle;
// 没有加载的资源
TArray<FObjectWealthEntry*> UnLoadWealthEntry;
// 已经加载的资源的数组
TArray<FObjectWealthEntry*> LoadWealthEntry;
// 请求对象名
FName ObjectName;
// 回调方法名
FName FunName;
// 构造函数
ObjectKindLoadNode(TSharedPtr<FStreamableHandle> InWealthHandle, TArray<FObjectWealthEntry*> InUnLoadWealthEntry, TArray<FObjectWealthEntry*>& InLoadWealthEntry, FName InObjectName, FName InFunName)
{
WealthHandle = InWealthHandle;
UnLoadWealthEntry = InUnLoadWealthEntry;
LoadWealthEntry = InLoadWealthEntry;
ObjectName = InObjectName;
FunName = InFunName;
}
};
void UDDWealth::WealthTick(float DeltaSeconds)
{
DealObjectKindLoadStack(); // 添加到 Tick()
}
void UDDWealth::LoadObjectWealthKind(FName WealthKind, FName ObjectName, FName FunName)
{
TArray<FObjectWealthEntry*> WealthEntryGroup = GetObjectKindEntry(WealthKind);
// 如果数量为 0
if (WealthEntryGroup.Num() == 0) {
DDH::Debug() << ObjectName << " Get Null WealthKind : " << WealthKind << DDH::Endl();
return;
}
// 判断资源可用性
for (int i = 0; i < WealthEntryGroup.Num(); ++i) {
if (!WealthEntryGroup[i]->WealthPath.IsValid()) {
DDH::Debug() << ObjectName << " Get Not Valid in Kind : " << WealthKind << " For Name : " << WealthEntryGroup[i]->WealthName << DDH::Endl();
return;
}
}
// 还没有加载的资源
TArray<FObjectWealthEntry*> UnLoadWealthEntry;
// 已经加载的资源
TArray<FObjectWealthEntry*> LoadWealthEntry;
// 资源加载与否归类
for (int i = 0; i< WealthEntryGroup.Num(); ++i) {
if (WealthEntryGroup[i]->WealthObject)
LoadWealthEntry.Push(WealthEntryGroup[i]);
else
UnLoadWealthEntry.Push(WealthEntryGroup[i]);
}
// 如果未加载的资源为 0
if (UnLoadWealthEntry.Num() == 0) {
// 直接获取所有资源给请求对象
TArray<FName> NameGroup;
TArray<UObject*> WealthGroup;
for (int i = 0; i < LoadWealthEntry.Num(); ++i) {
NameGroup.Push(LoadWealthEntry[i]->WealthName);
WealthGroup.Push(LoadWealthEntry[i]->WealthObject);
}
BackObjectWealthKind(ModuleIndex, ObjectName, FunName, NameGroup, WealthGroup);
}
else {
// 获取资源路径
TArray<FSoftObjectPath> WealthPaths;
for (int i = 0; i < UnLoadWealthEntry.Num(); ++i)
WealthPaths.Push(UnLoadWealthEntry[i]->WealthPath);
// 进行异步加载获取句柄
TSharedPtr<FStreamableHandle> WealthHandle = WealthLoader.RequestAsyncLoad(WealthPaths);
// 生成加载节点
ObjectKindLoadStack.Push(new ObjectKindLoadNode(WealthHandle, UnLoadWealthEntry, LoadWealthEntry, ObjectName, FunName));
}
}
void UDDWealth::DealObjectKindLoadStack()
{
// 定义加载完成的序列
TArray<ObjectKindLoadNode*> CompleteStack;
for (int i = 0; i < ObjectKindLoadStack.Num(); ++i) {
// 判断是否已经加载完成
if (ObjectKindLoadStack[i]->WealthHandle->HasLoadCompleted()) {
// 返回资源参数
TArray<FName> NameGroup;
TArray<UObject*> WealthGroup;
// 填充已加载资源
for (int j = 0; j < ObjectKindLoadStack[i]->LoadWealthEntry.Num(); ++j) {
NameGroup.Push(ObjectKindLoadStack[i]->LoadWealthEntry[j]->WealthName);
WealthGroup.Push(ObjectKindLoadStack[i]->LoadWealthEntry[j]->WealthObject);
}
// 遍历设置所有未加载资源结构体为已加载状态
for (int j = 0; j < ObjectKindLoadStack[i]->UnLoadWealthEntry.Num(); ++j) {
ObjectKindLoadStack[i]->UnLoadWealthEntry[j]->WealthObject = ObjectKindLoadStack[i]->UnLoadWealthEntry[j]->WealthPath.ResolveObject();
// 填充已加载资源
NameGroup.Push(ObjectKindLoadStack[i]->UnLoadWealthEntry[j]->WealthName);
WealthGroup.Push(ObjectKindLoadStack[i]->UnLoadWealthEntry[j]->WealthObject);
}
// 返回数据给请求对象
BackObjectWealthKind(ModuleIndex, ObjectKindLoadStack[i]->ObjectName, ObjectKindLoadStack[i]->FunName, NameGroup, WealthGroup);
// 添加节点到已完成序列
CompleteStack.Push(ObjectKindLoadStack[i]);
}
}
// 销毁已经完成的节点
for (int i = 0; i < CompleteStack.Num(); ++i) {
// 移除出节点序列
ObjectKindLoadStack.Remove(CompleteStack[i]);
// 释放内存
delete CompleteStack[i];
}
}
验证部分我们留到下一节课。
45. 单个加载 UClass 功能
测试加载多个 UObject 资源
来到 LoadWealthWidget,我们打算异步加载多张图片资源,然后用之前的延时系统将图片每秒一张地换到 Image 控件上。
LoadWealthWidget.h
class UTexture2D;
UCLASS()
class RACECARFRAME_API ULoadWealthWidget : public UDDUserWidget
{
GENERATED_BODY()
public:
// 资源加载的回调函数
UFUNCTION()
void LoadKindTexture(TArray<FName> BackNames, TArray<UObject*> BackWealths);
// 供延时系统使用的切换图片方法
void ChangeImage();
public:
int32 ImageIndex;
TArray<UTexture2D*> TextureGroup;
};
LoadWealthWidget.cpp
void ULoadWealthWidget::DDLoading()
{
Super::DDLoading();
//LoadObjectWealth("ViewImage1", "LoadSingleTexture");
// 测试完记得注释掉
LoadObjectWealthKind("ViewImage", "LoadKindTexture");
}
void ULoadWealthWidget::LoadKindTexture(TArray<FName> BackNames, TArray<UObject*> BackWealths)
{
for (int i = 0; i < BackWealths.Num(); ++i) {
// 输出所有获取到的资源的名字
DDH::Debug() << BackNames[i] << DDH::Endl();
TextureGroup.Push(Cast<UTexture2D>(BackWealths[i]));
}
ImageIndex = 0;
InvokeRepeat("ChangeImage", 1.f, 1.f, this, &ULoadWealthWidget::ChangeImage);
}
void ULoadWealthWidget::ChangeImage()
{
ViewImage->SetBrushFromTexture(TextureGroup[ImageIndex]);
ImageIndex = ImageIndex + 1 >= TextureGroup.Num() ? 0 : ImageIndex + 1;
}
编译后,给 HUDData 中配置更多的图片,并且将它们的 WealthKind 设置成同名,老师配置了 11 张,此处就截图 4 张以作示例:
运行游戏,可以看见左上角正在每秒一张地轮播刚刚配置的图片,并且输出了 11 张图片的 Wealth Name。我们还可以发现它需要一定的时间(一开始 Image 控件为空白)来进行异步加载。
异步加载单个 UClass 类型的资源
加载 UClass 类型资源的逻辑跟加载 UObject 差不多,区别在于:
UObject 的资源链接用的是 FStringAssetReference
;UClass 的是 TSoftClassPtr
,它需要通过 ToSoftObjectPath()
转换成 FSoftObjectPath
才能参与到 UClass 类型的异步加载中。
DDWealth.h
// 加载单个 Class
struct ClassSingleLoadNode;
UCLASS()
class DATADRIVEN_API UDDWealth : public UObject, public IDDMM
{
GENERATED_BODY()
public:
// 加载 Class 类型资源接口
void LoadClassWealth(FName WealthName, FName ObjectName, FName FunName);
void LoadClassWealthKind(FName WealthKind, FName ObjectName, FName FunName);
protected:
// 获取 Class 资源结构体
FClassWealthEntry* GetClassSingleEntry(FName WealthName);
TArray<FClassWealthEntry*> GetClassKindEntry(FName WealthKind);
// 处理加载单个 Class 节点的方法
void DealClassSingleLoadStack();
protected:
TArray<ClassSingleLoadNode*> ClassSingleLoadStack;
protected:
// 加载 UClass 反射回调函数
DDOBJFUNC_TWO(BackClassWealth, FName, BackName, UClass*, BackWealth);
DDOBJFUNC_TWO(BackClassWealthKind, TArray<FName>, BackNames, TArray<UClass*>, BackWealths);
};
DDWealth.cpp
struct ClassSingleLoadNode
{
// 加载句柄
TSharedPtr<FStreamableHandle> WealthHandle;
// 资源结构体
FClassWealthEntry* WealthEntry;
// 请求对象名
FName ObjectName;
// 回调方法名
FName FunName;
// 构造函数
ClassSingleLoadNode(TSharedPtr<FStreamableHandle> InWealthHandle, FClassWealthEntry* InWealthEntry, FName InObjectName, FName InFunName)
{
WealthHandle = InWealthHandle;
WealthEntry = InWealthEntry;
ObjectName = InObjectName;
FunName = InFunName;
}
};
void UDDWealth::WealthTick(float DeltaSeconds)
{
DealObjectSingleLoadStack();
DealObjectKindLoadStack();
DealClassSingleLoadStack(); // 每帧调用
}
void UDDWealth::LoadClassWealth(FName WealthName, FName ObjectName, FName FunName)
{
// 获取资源结构体
FClassWealthEntry* WealthEntry = GetClassSingleEntry(WealthName);
// 如果为空
if (!WealthEntry) {
DDH::Debug() << ObjectName << " Get Null Wealth : " << WealthName << DDH::Endl();
return;
}
// 如果资源不可用
if (!WealthEntry->WealthPtr.ToSoftObjectPath().IsValid()) {
DDH::Debug() << ObjectName << " Get UnValid Wealth : " << WealthName << DDH::Endl();
return;
}
// 如果资源已经加载
if (WealthEntry->WealthClass) {
// 直接把资源返回给申请对象
BackClassWealth(ModuleIndex, ObjectName, FunName, WealthName, WealthEntry->WealthClass);
}
else {
// 进行异步加载
TSharedPtr<FStreamableHandle> WealthHandle = WealthLoader.RequestAsyncLoad(WealthEntry->WealthPtr.ToSoftObjectPath());
// 添加节点
ClassSingleLoadStack.Push(new ClassSingleLoadNode(WealthHandle, WealthEntry, ObjectName, FunName));
}
}
// 批量加载 UClass 资源的逻辑先不写
void UDDWealth::LoadClassWealthKind(FName WealthKind, FName ObjectName, FName FunName)
{
}
FClassWealthEntry* UDDWealth::GetClassSingleEntry(FName WealthName)
{
for (int i = 0; i < WealthData.Num(); ++i) {
for (int j = 0; j < WealthData[i]->ClassWealthData.Num(); ++j) {
if (WealthData[i]->ClassWealthData[j].WealthName.IsEqual(WealthName))
return &(WealthData[i]->ClassWealthData[j]);
}
}
return NULL;
}
TArray<FClassWealthEntry*> UDDWealth::GetClassKindEntry(FName WealthKind)
{
TArray<FClassWealthEntry*> WealthGroup;
for (int i = 0; i < WealthData.Num(); ++i) {
for (int j = 0; j < WealthData[i]->ClassWealthData.Num(); ++j) {
if (WealthData[i]->ClassWealthData[j].WealthKind.IsEqual(WealthKind))
WealthGroup.Push(&(WealthData[i]->ClassWealthData[j]));
}
}
return WealthGroup;
}
void UDDWealth::DealClassSingleLoadStack()
{
// 定义加载完成的序列
TArray<ClassSingleLoadNode*> CompleteStack;
for (int i = 0; i < ClassSingleLoadStack.Num(); ++i) {
// 判断是否已经加载完成
if (ClassSingleLoadStack[i]->WealthHandle->HasLoadCompleted()) {
// 设置对应资源完成
ClassSingleLoadStack[i]->WealthEntry->WealthClass = Cast<UClass>(ClassSingleLoadStack[i]->WealthEntry->WealthPtr.ToSoftObjectPath().ResolveObject());
// 返回资源给对象
BackClassWealth(ModuleIndex, ClassSingleLoadStack[i]->ObjectName, ClassSingleLoadStack[i]->FunName, ClassSingleLoadStack[i]->WealthEntry->WealthName, ClassSingleLoadStack[i]->WealthEntry->WealthClass);
// 添加已经加载完成的节点到临时序列
CompleteStack.Push(ClassSingleLoadStack[i]);
}
}
// 销毁已经完成的节点
for (int i = 0; i < CompleteStack.Num(); ++i) {
// 移除出节点序列
ClassSingleLoadStack.Remove(CompleteStack[i]);
// 释放内存
delete CompleteStack[i];
}
}
补充 DDWealth – DDModule – DDOO – 对象的调用链。
DDModule.h
public:
// 加载 Class 类型资源接口
void LoadClassWealth(FName WealthName, FName ObjectName, FName FunName);
void LoadClassWealthKind(FName WealthKind, FName ObjectName, FName FunName);
DDModule.cpp
void UDDModule::LoadClassWealth(FName WealthName, FName ObjectName, FName FunName)
{
Wealth->LoadClassWealth(WealthName, ObjectName, FunName);
}
void UDDModule::LoadClassWealthKind(FName WealthKind, FName ObjectName, FName FunName)
{
Wealth->LoadClassWealthKind(WealthKind, ObjectName, FunName);
}
DDOO.h
protected:
// 加载 Class 类型资源接口
void LoadClassWealth(FName WealthName, FName FunName);
void LoadClassWealthKind(FName WealthKind, FName FunName);
DDOO.cpp
void IDDOO::LoadClassWealth(FName WealthName, FName FunName)
{
IModule->LoadClassWealth(WealthName, GetObjectName(), FunName);
}
void IDDOO::LoadClassWealthKind(FName WealthKind, FName FunName)
{
IModule->LoadClassWealthKind(WealthKind, GetObjectName(), FunName);
}
接下来准备测试一下加载单个 UClass 类型资源的逻辑。我们打算让一个普通的 Actor 被加载到场景中。而调用异步加载 UClass 资源的方法就让 WealthCallObject(它在 42 集开头通过配置在 PlayerData 里来生成)来调用。
WealthCallObject.h
public:
virtual void DDLoading() override;
// 回调函数
UFUNCTION()
void LoadActorClass(FName BackName, UClass* BackWealth);
public:
// 生成位置
UPROPERTY(EditAnywhere)
FTransform ViewTrans;
WealthCallObject.cpp
void UWealthCallObject::DDLoading()
{
Super::DDLoading();
// 测试完后记得注释掉
LoadClassWealth("ViewActor1", "LoadActorClass");
}
void UWealthCallObject::LoadActorClass(FName BackName, UClass* BackWealth)
{
GetDDWorld()->SpawnActor<AActor>(BackWealth, ViewTrans);
}
编译后打开 WealthCallObject 蓝图,设置位置如下:
在 Blueprint 文件夹下创建一个基于 Actor 的蓝图,命名为 ViewActor1,给它添加一个网格体:
打开 PlayerData,配置如下:
运行游戏,可以看见目标地点生成了 ViewActor1。说明异步加载单个 UClass 类型资源的逻辑写好了。