UE4运用C++和框架开发坦克大战教程笔记(十四)(第43~45集)
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 {
<