UE4运用C++和框架开发坦克大战教程笔记(十四)(第43~45集)

UE4运用C++和框架开发坦克大战教程笔记(十四)(第43~45集)

  • 43. 单个加载 UObject 功能
    • 获取资源 URL 链接
    • 实现异步加载单个 UObject 类型资源
  • 44. 批量加载 UObject 功能
    • 测试加载单个 UObject 资源
    • 批量加载多个同类的 UObject 资源
  • 45. 单个加载 UClass 功能
    • 测试加载多个 UObject 资源
    • 异步加载单个 UClass 类型的资源

43. 单个加载 UObject 功能

获取资源 URL 链接

继续来补充根据资源类型生成资源的逻辑,在 DDWealth 里添加获取 URL 的方法。

DDWealth.h

public:// 外部方法单纯获取资源链接// 返回单个 URLFWealthURL* GainWealthURL(FName WealthName);// 返回一个种类的资源的 URLvoid GainWealthURL(FName WealthKind, TArray<FWealthURL*>& OutURL);

DDWealth.cpp

FWealthURL* UDDWealth::GainWealthURL(FName WealthName)
{// 从 DataAsset 里遍历获取对应名字的资源的 URLfor (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 里遍历获取对应种类名字的全部资源的 URLfor (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()) {// 设置对应资源赋值给 WealthObjectObjectSingleLoadStack[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);// 如果数量为 0if (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]);elseUnLoadWealthEntry.Push(WealthEntryGroup[i]);}// 如果未加载的资源为 0if (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 类型资源的逻辑写好了。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/601740.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

c# OpenCvSharp透视矫正参数调整器

透视矫正不够智能化&#xff0c;每次都要进行局部参数调整&#xff0c;不便于程序使用&#xff0c;程序流程还是那几个步骤&#xff1b; 1、读取图像、灰度化 2、高斯滤波 3、二值化 4、边缘检测 灰度化图 上个图看看经过调整透视矫正边缘检测结果我还是挺满意的 发现一个…

MongoDB数据类型详解

BSON 协议与数据类型 MongoDB 为什么会使用 BSON&#xff1f; JSON 是当今非常通用的一种跨语言 Web 数据交互格式&#xff0c;属 ECMAScript 标准规范的一个子集。JSON &#xff08;JavaScript Object Notation&#xff0c;JS 对象简谱&#xff09;即 JavaScript 对象表示法…

开源项目 | 完整部署流程、一款开源人人可用的开源数据可视化分析工具

&#x1f4da; 项目介绍 在互联网数据大爆炸的这几年&#xff0c;各类数据处理、数据可视化的需求使得 GitHub 上诞生了一大批高质量的 BI 工具。 借助这些 BI 工具&#xff0c;我们能够大幅提升数据分析效率、生成更高质量的项目报告&#xff0c;让用户通过直观的数据看到结…

微服务实战系列之API加密

前言 随着一阵阵凛冽寒风的呼啸&#xff0c;新的年轮不知不觉滚滚而来。故事随着2023的远去&#xff0c;尘封于案底&#xff1b;希望迎着新年&#xff0c;绽放于枝头。在2024新岁启航&#xff0c;扬帆破浪之时&#xff0c;让烦恼抛洒于九霄&#xff0c;让生机蓬勃于朝朝暮暮。 …

如何充值GPT会员账号?

详情点击链接&#xff1a;如何充值GPT会员账号&#xff1f; 一OpenAI 1.最新大模型GPT-4 Turbo 2.最新发布的高级数据分析&#xff0c;AI画图&#xff0c;图像识别&#xff0c;文档API 3.GPT Store 4.从0到1创建自己的GPT应用 5. 模型Gemini以及大模型Claude2二定制自己的…

基于多反应堆的高并发服务器【C/C++/Reactor】(中)创建并初始化TcpServer实例 以及 启动

对于一个TcpServer来说&#xff0c;它的灵魂是什么&#xff1f;就是需要提供一个事件循环EventLop(EventLoop)&#xff0c;不停地去检测有没有客户端的连接到达&#xff0c;有没有客户端给服务器发送数据&#xff0c;描述的这些动作&#xff0c;反应堆模型能够胜任。当服务器和…

【Docker】容器的相关命令

上一篇&#xff1a;创建&#xff0c;查看&#xff0c;进入容器 https://blog.csdn.net/m0_67930426/article/details/135430093?spm1001.2014.3001.5502 目录 1. 关闭容器 2.启动容器 3.删除容器 4.查看容器的信息 查看容器 1. 关闭容器 从图上来看&#xff0c;容器 aa…

机器学习-基于attention机制来实现对Image Caption图像描述实验

机器学习-基于attention机制来实现对Image Caption图像描述实验 实验目的 基于attention机制来实现对Image Caption图像描述 实验内容 1.了解一下RNN的Encoder-Decoder结构 在最原始的RNN结构中&#xff0c;输入序列和输出序列必须是严格等长的。但在机器翻译等任务中&…

idea中使用Lombok 失效,@Slf4j 找不到符号的解决办法

文章目录 一、前言二、问题排查和解决方案三、 其他解决方案3.1 另一种解决方案3.2 参考文章 一、前言 今天在一个多module工程中&#xff0c;新增了一个 springboot&#xff08;版本 2.2.4.RELEASE&#xff09; module&#xff0c;像往常一样&#xff0c;我引入了lombok依赖&…

selenium 用webdriver.Chrome 访问网页闪退解决方案

1.1.1. 解决方案&#xff1a; 1.1.1.1. 移动插件到谷歌的安装目录下 1.1.1.2. 设置环境变量 1.1.1.3. 重启电脑检查成功 解决时间&#xff1a;5min

58.网游逆向分析与插件开发-游戏增加自动化助手接口-游戏菜单文字资源读取的逆向分析

内容来源于&#xff1a;易道云信息技术研究院VIP课 之前的内容&#xff1a;接管游戏的自动药水设定功能-CSDN博客 码云地址&#xff08;master分支&#xff09;&#xff1a;https://gitee.com/dye_your_fingers/sro_-ex.git 码云版本号&#xff1a;34b9c1d43b512d0b4a3c395b…

Springboot支付宝沙箱支付(完整详细步骤)

Springboot支付宝沙箱支付&#xff08;完整详细步骤&#xff09; 网页操作步骤1.进入支付宝开发平台—沙箱环境2.点击沙箱进入沙箱环境3.进入沙箱&#xff0c;配置接口加签方式4.配置应用网关5.生成自己的密钥 IntelliJ IDEA 操作步骤1.导入依赖2.在 application.yml 里面进行配…

java基于SSM的毕业生就业管理系统+vue论文

摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本毕业生就业管理系统就是在这样的大环境下诞生&#xff0c;其可以帮助管理者在短时间内处理完毕庞大的数据信…

TypeScript接口、对象

目录 1、TypeScript 接口 1.1、实例 1.2、联合类型和接口 1.3、接口和数组 1.4、接口和继承 1.5、单继承实例 1.6、多继承实例 2、TypeScript 对象 2.2、对象实例 2.3、TypeScript类型模板 2.4、鸭子类型&#xff08;Duck typing&#xff09; 1、TypeScript 接口 接口…

Mac启动时候出现禁止符号

Mac启动时候出现禁止符号 启动时候出现禁止符号,意味着 选定的启动磁盘 包含 Mac 操作系统&#xff0c;但它不是 您的 Mac 可以使用的 macOS 。您应该在这个磁盘上 重新安装 macOS 。 可以尝试以下苹果提供的方法&#xff1a; Mac启动时候出现禁止符号 不要轻易抹除磁盘&am…

idea将本地编译好的代码上传到hub镜像仓库

第一步&#xff1a;编译打包本地的文件 package 第二步&#xff1a;执行docker bulid打包命令 docker build -t sunyuhua/algo-ability:1.0.0 .sunyuhuasunyuhua-HKF-WXX:~/workspace/shbgit/algo-ability$ docker build -t sunyuhua/algo-ability:1.0.0 . [] Building 141.…

C语言编译器(C语言编程软件)完全攻略

介绍常用C语言编译器的安装、配置和使用。 常用的C语言编译器&#xff08;编程软件&#xff09;介绍&#xff0c;同时附带下载地址、详细的安装教程和使用教程。我们还对比了不同C语言编译器&#xff08;C语言编程软件&#xff09;的优缺点&#xff0c;让初学者知道该如何选择…

差分电路原理以及为什么输出电压要偏移

我们在使用放大器芯片的时候&#xff0c;除了对放大器芯片本身应用外&#xff0c;通常还需要搭建一些外围电路来满足放大器芯片的使用条件&#xff0c;最终满足应用的功能&#xff0c;下面通过一个差分电路来熟悉这些应用。 差分运算放大电路&#xff0c;对共模信号得到有效抑…

C# Image Caption

目录 介绍 效果 模型 decoder_fc_nsc.onnx encoder.onnx 项目 代码 下载 C# Image Caption 介绍 地址&#xff1a;https://github.com/ruotianluo/ImageCaptioning.pytorch I decide to sync up this repo and self-critical.pytorch. (The old master is in old ma…

实战演练 | Navicat 中编辑器设置的配置

Navicat 是一款功能强大的数据库管理工具&#xff0c;为开发人员和数据库管理员提供稳健的环境。其中&#xff0c;一个重要功能是 SQL 编辑器&#xff0c;用户可以在 SQL 编辑器中编写和执行 SQL 查询。Navicat 的编辑器设置可让用户自定义编辑器环境&#xff0c;以满足特定的团…