做设计有哪些免费网站/营销软文范文
UE4运用C++和框架开发坦克大战教程笔记(十五)(第46~48集)
- 46. 批量加载 UClass 功能
- 测试批量加载多个同类 UClass 资源
- 47. 创建单个资源对象
- 测试加载并创建单个 UClass 资源对象
- 48. 创建同类资源对象
46. 批量加载 UClass 功能
逻辑和批量加载同类 UObject 资源的逻辑差不多。区别在 DealClassKindLoadStack()
内,如果已经有资源率先加载完成了,那后续资源加载的途中我们想让已经加载好的资源执行额外的处理逻辑(比如让它每帧生成),我们就需要补充额外的判断条件,即判断其是否第一次生成完毕。
DDWealth.h
// 加载批量 Class
struct ClassKindLoadNode;UCLASS()
class DATADRIVEN_API UDDWealth : public UObject, public IDDMM
{GENERATED_BODY()protected:// 处理批量加载 Class 节点的方法void DealClassKindLoadStack();protected:TArray<ClassKindLoadNode*> ClassKindLoadStack;
};
DDWealth.cpp
struct ClassKindLoadNode
{// 加载句柄TSharedPtr<FStreamableHandle> WealthHandle;// 没有加载的资源TArray<FClassWealthEntry*> UnLoadWealthEntry;// 已经加载的资源的数组TArray<FClassWealthEntry*> LoadWealthEntry;// 请求对象名FName ObjectName;// 回调方法名FName FunName;// 构造函数ClassKindLoadNode(TSharedPtr<FStreamableHandle> InWealthHandle, TArray<FClassWealthEntry*> InUnLoadWealthEntry, TArray<FClassWealthEntry*>& InLoadWealthEntry, FName InObjectName, FName InFunName){WealthHandle = InWealthHandle;UnLoadWealthEntry = InUnLoadWealthEntry;LoadWealthEntry = InLoadWealthEntry;ObjectName = InObjectName;FunName = InFunName;}
};void UDDWealth::WealthTick(float DeltaSeconds)
{DealClassKindLoadStack();
}void UDDWealth::LoadClassWealthKind(FName WealthKind, FName ObjectName, FName FunName)
{TArray<FClassWealthEntry*> WealthEntryGroup = GetClassKindEntry(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]->WealthPtr.ToSoftObjectPath().IsValid()) {DDH::Debug() << ObjectName << " Get Not Valid in Kind : " << WealthKind << " For Name : " << WealthEntryGroup[i]->WealthName << DDH::Endl();return;}}// 未加载资源序列TArray<FClassWealthEntry*> UnLoadWealthEntry;// 已加载资源序列TArray<FClassWealthEntry*> LoadWealthEntry;// 分类保存for (int i = 0; i < WealthEntryGroup.Num(); ++i) {if (WealthEntryGroup[i]->WealthClass)LoadWealthEntry.Push(WealthEntryGroup[i]);elseUnLoadWealthEntry.Push(WealthEntryGroup[i]);}// 判断所有资源是否都已经加载if (UnLoadWealthEntry.Num() == 0) {// 填充参数TArray<FName> NameGroup;TArray<UClass*> WealthGroup;for (int i = 0; i < LoadWealthEntry.Num(); ++i) {NameGroup.Push(LoadWealthEntry[i]->WealthName);WealthGroup.Push(LoadWealthEntry[i]->WealthClass);}// 返回资源给请求对象BackClassWealthKind(ModuleIndex, ObjectName, FunName, NameGroup, WealthGroup);}else {// 获取未加载资源路径数组TArray<FSoftObjectPath> WealthPaths;for (int i = 0; i < UnLoadWealthEntry.Num(); ++i)WealthPaths.Push(UnLoadWealthEntry[i]->WealthPtr.ToSoftObjectPath());// 进行异步加载获取句柄TSharedPtr<FStreamableHandle> WealthHandle = WealthLoader.RequestAsyncLoad(WealthPaths);// 添加新的加载节点ClassKindLoadStack.Push(new ClassKindLoadNode(WealthHandle, UnLoadWealthEntry, LoadWealthEntry, ObjectName, FunName));}
}void UDDWealth::DealClassKindLoadStack()
{// 定义已完成加载节点序列TArray<ClassKindLoadNode*> CompleteStack;for (int i = 0; i < ClassKindLoadStack.Num(); ++i) {// 判断第一次加载完成,WealthHandle 已经加载完成,UnLoadWealthEntry 数量大于 0if (ClassKindLoadStack[i]->WealthHandle->HasLoadCompleted() && ClassKindLoadStack[i]->UnLoadWealthEntry.Num() > 0) {// 如果已经加载完成,设置未加载序列的资源指针for (int j = 0; j < ClassKindLoadStack[i]->UnLoadWealthEntry.Num(); ++j)ClassKindLoadStack[i]->UnLoadWealthEntry[j]->WealthClass = Cast<UClass>(ClassKindLoadStack[i]->UnLoadWealthEntry[j]->WealthPtr.ToSoftObjectPath().ResolveObject());// 将未加载完成序列里的资源填充到已加载资源序列ClassKindLoadStack[i]->LoadWealthEntry.Append(ClassKindLoadStack[i]->UnLoadWealthEntry);// 清空 UnLoadWealthEntryClassKindLoadStack[i]->UnLoadWealthEntry.Empty();}// 如果未加载序列为 0,说明已经加载完成if (ClassKindLoadStack[i]->UnLoadWealthEntry.Num() == 0) {// 加载 UClass 或者直接生成资源的情况来处理// 设置反射参数TArray<FName> NameGroup;TArray<UClass*> WealthGroup;for (int j = 0; j < ClassKindLoadStack[i]->LoadWealthEntry.Num(); ++j) {NameGroup.Push(ClassKindLoadStack[i]->LoadWealthEntry[j]->WealthName);WealthGroup.Push(ClassKindLoadStack[i]->LoadWealthEntry[j]->WealthClass);}// 返回资源给请求对象BackClassWealthKind(ModuleIndex, ClassKindLoadStack[i]->ObjectName, ClassKindLoadStack[i]->FunName, NameGroup, WealthGroup);// 添加该节点到已完成序列CompleteStack.Push(ClassKindLoadStack[i]);}}// 清空已完成节点for (int i = 0; i < CompleteStack.Num(); ++i) {ClassKindLoadStack.Remove(CompleteStack[i]);delete CompleteStack[i];}
}
测试批量加载多个同类 UClass 资源
接下来是验证环节,我们打算让多个 Actor 生成在场景中,并且为了让它们不会挤到一块,所以每次生成的时候将位置偏移一点。
WealthCallObject.h
public:// 回调函数UFUNCTION()void LoadKindClass(TArray<FName> BackNames, TArray<UClass*> BackWealths);public:// 生成的偏移量UPROPERTY(EditAnywhere)float OffsetValue;
WealthCallObject.cpp
void UWealthCallObject::DDLoading()
{// 测试完毕后记得注释掉LoadClassWealthKind("ViewActor", "LoadKindClass");
}void UWealthCallObject::LoadKindClass(TArray<FName> BackNames, TArray<UClass*> BackWealths)
{for (int i = 0; i < BackWealths.Num(); ++i) {DDH::Debug() << BackNames[i] << DDH::Endl();GetDDWorld()->SpawnActor<AActor>(BackWealths[i], ViewTrans.GetLocation() + FVector(OffsetValue * i, 0.f, 0.f), FQuat::Identity.Rotator());}
}
编译后,在 Blueprint 文件夹下创建一个 ViewActor 文件夹,将 ViewActor1 放进去,并复制两个,分别取名为 ViewActor2 和 ViewActor3。给它俩换一下网格体模型。
给 PlayerData 里再配置两个 Class 资源数据:
打开 WealthCallObject 的蓝图,设置 Offset Value 为 150。
运行游戏,可见左上角输出了 3 个 Class 资源的名字,场景内也出现了它们的实例。
在第一次运行后,在 Deal{xxx}LoadStack()
方法里会给加载节点里的 UObject*
/ UClass*
赋值,它们就不为空了,也就不会进行异步加载,为了让它每次都像第一次运行那样(为了方便测试异步加载无误),我们要修改一下代码,让它在编辑器运行时每次都清空 WealthObject 和 WealthClass。这样就以后打包完游戏就不会自动清空。
DDWealth.cpp
void UDDWealth::WealthBeginPlay()
{for (int i = 0; i < WealthData.Num(); ++i) {// ... 省略#if WITH_EDITOR // 循环设置 WealthObject 和 WealthClass 为空,目的在于每次从编辑器启动游戏时,资源 Asset 的状态都重置for (int j = 0; j < WealthData[i]->ObjectWealthData.Num(); ++j)WealthData[i]->ObjectWealthData[j].WealthObject = NULL;for (int j = 0; j < WealthData[i]->ClassWealthData.Num(); ++j)WealthData[i]->ClassWealthData[j].WealthClass = NULL;
#endif}
}
47. 创建单个资源对象
我们先前写的逻辑都是加载资源然后将其返回给请求者,接下来我们打算实现:加载 UClass 资源并创建对象后,将对象返回给请求者。
下图截取自梁迪老师的 DataDriven 文档:
创建多个对象的方法里,每帧都会创建一个对象,创建足够数量的对象后才会将所有的对象返回给请求对象。
这节课我们先实现创建单个资源对象的功能。
DDWealth.h
public:// 创建一个对象实例void BuildSingleClassWealth(EWealthType WealthType, FName WealthName, FName ObjectName, FName FunName, FTransform SpawnTransform);protected:// 生成三种资源对象的反射回调函数DDOBJFUNC_TWO(BackObject, FName, BackName, UObject*, BackObject);DDOBJFUNC_TWO(BackActor, FName, BackName, AActor*, BackActor);DDOBJFUNC_TWO(BackWidget, FName, BackName, UUserWidget*, BackWidget);
DDWealth.cpp
// 给 UClass 加载节点补充另外的内容,以便支持对象创建
struct ClassSingleLoadNode
{TSharedPtr<FStreamableHandle> WealthHandle;FClassWealthEntry* WealthEntry;FName ObjectName;FName FunName;// 生成位置FTransform SpawnTransform;// 是否只加载 UClass 资源bool IsLoadClass;ClassSingleLoadNode(TSharedPtr<FStreamableHandle> InWealthHandle, FClassWealthEntry* InWealthEntry, FName InObjectName, FName InFunName){WealthHandle = InWealthHandle;WealthEntry = InWealthEntry;ObjectName = InObjectName;FunName = InFunName;IsLoadClass = true; // 添加}// 另一个构造函数ClassSingleLoadNode(TSharedPtr<FStreamableHandle> InWealthHandle, FClassWealthEntry* InWealthEntry, FName InObjectName, FName InFunName, FTransform InSpawnTransform){WealthHandle = InWealthHandle;WealthEntry = InWealthEntry;ObjectName = InObjectName;FunName = InFunName;SpawnTransform = InSpawnTransform;IsLoadClass = false;}
};// 给处理方法增加判断,是仅仅加载资源还是创建对象
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());// 判断是否生成对象if (ClassSingleLoadStack[i]->IsLoadClass) {// 返回资源给对象BackClassWealth(ModuleIndex, ClassSingleLoadStack[i]->ObjectName, ClassSingleLoadStack[i]->FunName, ClassSingleLoadStack[i]->WealthEntry->WealthName, ClassSingleLoadStack[i]->WealthEntry->WealthClass);}else {// 生成相应类型的对象并且传递对象到请求者if (ClassSingleLoadStack[i]->WealthEntry->WealthType == EWealthType::Object) {UObject* InstObject = NewObject<UObject>(this, ClassSingleLoadStack[i]->WealthEntry->WealthClass);InstObject->AddToRoot();BackObject(ModuleIndex, ClassSingleLoadStack[i]->ObjectName, ClassSingleLoadStack[i]->FunName, ClassSingleLoadStack[i]->WealthEntry->WealthName, InstObject);}else if (ClassSingleLoadStack[i]->WealthEntry->WealthType == EWealthType::Actor) {AActor* InstActor = GetDDWorld()->SpawnActor<AActor>(ClassSingleLoadStack[i]->WealthEntry->WealthClass, ClassSingleLoadStack[i]->SpawnTransform);BackActor(ModuleIndex, ClassSingleLoadStack[i]->ObjectName, ClassSingleLoadStack[i]->FunName, ClassSingleLoadStack[i]->WealthEntry->WealthName, InstActor);}else if (ClassSingleLoadStack[i]->WealthEntry->WealthType == EWealthType::Widget) {UUserWidget* InstWidget = CreateWidget<UUserWidget>(GetDDWorld(), ClassSingleLoadStack[i]->WealthEntry->WealthClass);// 避免回收GCWidgetGroup.Push(InstWidget);BackWidget(ModuleIndex, ClassSingleLoadStack[i]->ObjectName, ClassSingleLoadStack[i]->FunName, ClassSingleLoadStack[i]->WealthEntry->WealthName, InstWidget);}}CompleteStack.Push(ClassSingleLoadStack[i]);}}for (int i = 0; i < CompleteStack.Num(); ++i) {ClassSingleLoadStack.Remove(CompleteStack[i]);delete CompleteStack[i];}
}void UDDWealth::BuildSingleClassWealth(EWealthType WealthType, FName WealthName, FName ObjectName, FName FunName, FTransform SpawnTransform)
{// 获取对应的资源结构体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->WealthType != WealthType) {DDH::Debug() << ObjectName << " Get Error Type : " << DDH::Endl();return;}// 如果资源已经加载if (WealthEntry->WealthClass) {// 根据类型来执行不同生成逻辑并且传递对象到请求者if (WealthType == EWealthType::Object) {UObject* InstObject = NewObject<UObject>(this, WealthEntry->WealthClass);InstObject->AddToRoot();// 传递对象到请求者BackObject(ModuleIndex, ObjectName, FunName, WealthName, InstObject);}else if (WealthType == EWealthType::Actor) {AActor* InstActor = GetDDWorld()->SpawnActor<AActor>(WealthEntry->WealthClass, SpawnTransform);BackActor(ModuleIndex, ObjectName, FunName, WealthName, InstActor);}else if (WealthType == EWealthType::Widget) {UUserWidget* InstWidget = CreateWidget<UUserWidget>(GetDDWorld(), WealthEntry->WealthClass);// 避免回收GCWidgetGroup.Push(InstWidget);BackWidget(ModuleIndex, ObjectName, FunName, WealthName, InstWidget);}}else {// 异步加载,获取加载句柄TSharedPtr<FStreamableHandle> WealthHandle = WealthLoader.RequestAsyncLoad(WealthEntry->WealthPtr.ToSoftObjectPath());// 创建新加载节点ClassSingleLoadStack.Push(new ClassSingleLoadNode(WealthHandle, WealthEntry, ObjectName, FunName, SpawnTransform));}
}
补全 DDWealth – DDModule – DDOO – 对象 的调用链。
DDModule.h
public: // 创建一个对象实例void BuildSingleClassWealth(EWealthType WealthType, FName WealthName, FName ObjectName, FName FunName, FTransform SpawnTransform);
DDModule.cpp
void UDDModule::BuildSingleClassWealth(EWealthType WealthType, FName WealthName, FName ObjectName, FName FunName, FTransform SpawnTransform)
{Wealth->BuildSingleClassWealth(WealthType, WealthName, ObjectName, FunName, SpawnTransform);
}
DDOO 有些不一样,它需要额外判断是否需要传入生成位置,这是专门为 Actor 准备的。
DDOO.h
protected:// 创建一个对象实例// 给 Object 和 Widget 用的void BuildSingleClassWealth(EWealthType WealthType, FName WealthName, FName FunName);// 给 Actor 用的void BuildSingleClassWealth(EWealthType WealthType, FName WealthName, FName FunName, FTransform SpawnTransform);
DDOO.cpp
void IDDOO::BuildSingleClassWealth(EWealthType WealthType, FName WealthName, FName FunName)
{IModule->BuildSingleClassWealth(WealthType, WealthName, GetObjectName(), FunName, FTransform::Identity);
}void IDDOO::BuildSingleClassWealth(EWealthType WealthType, FName WealthName, FName FunName, FTransform SpawnTransform)
{IModule->BuildSingleClassWealth(WealthType, WealthName, GetObjectName(), FunName, SpawnTransform);
}
测试加载并创建单个 UClass 资源对象
我们打算只测试创建一个 Actor 资源对象,并且让它一直旋转。
WealthCallObject.h
public:virtual void DDTick(float DeltaSeconds) override;// 回调方法UFUNCTION()void BuildActor(FName BackName, AActor* BackActor);public:// 保存返回的 ActorAActor* SingleActor;
WealthCallObject.cpp
void UWealthCallObject::DDLoading()
{Super::DDLoading();IsAllowTickEvent = true; // 实际上开启帧函数的bool值这个最好放在 DDInit()// ... 省略// 测试完毕后记得注释掉BuildSingleClassWealth(EWealthType::Actor, "ViewActor1", "BuildActor", ViewTrans);
}void UWealthCallObject::DDTick(float DeltaSeconds)
{Super::DDTick(DeltaSeconds);if (SingleActor) {SingleActor->AddActorWorldRotation(FRotator(1.f, 0.f, 0.f));}
}void UWealthCallObject::BuildActor(FName BackName, AActor* BackActor)
{DDH::Debug() << BackName << DDH::Endl();SingleActor = BackActor;
}
编译后运行游戏,可以看见左上角输出了 ViewActor1,场景中生成了 ViewActor1,并且它一直在旋转。说明加载并创建 UClass 对象的逻辑写好了。
顺便打开 PlayerData 将 AutoActorData 下的两个对象去掉,免得在场景里占位置。
48. 创建同类资源对象
接下来实现创建多个同种类名(WealthKind)的资源对象实例后返回给申请者。
DDWealth.h 里的反射回调函数的声明宏,调整一下原本第一个参数的命名,加一个 Single
,避免与最后一个参数重名。并且 .cpp 里对应的调用语句也要跟着修改。
DDWealth.h
public:// 创建同资源种类名的对象实例,同种类名下的每个资源链接创建一个对象实例void BuildKindClassWealth(EWealthType WealthType, FName WealthKind, FName ObjectName, FName FunName, TArray<FTransform> SpawnTransforms);protected:// 给之前的生成单个对象的反射回调函数,函数名后添加 “Single”DDOBJFUNC_TWO(BackObjectSingle, FName, BackName, UObject*, BackObject);// 添加同种类 Object 的反射回调函数DDOBJFUNC_TWO(BackObjectKind, TArray<FName>, BackNames, TArray<UObject*>, BackObjects);DDOBJFUNC_TWO(BackActorSingle, FName, BackName, AActor*, BackActor);// 添加同种类 Actor 的反射回调函数DDOBJFUNC_TWO(BackActorKind, TArray<FName>, BackNames, TArray<AActor*>, BackActors);DDOBJFUNC_TWO(BackWidgetSingle, FName, BackName, UUserWidget*, BackWidget);// 添加同种类 Widget 的反射回调函数DDOBJFUNC_TWO(BackWidgetKind, TArray<FName>, BackNames, TArray<UUserWidget*>, BackWidgets);
DDWealth.cpp
struct ClassKindLoadNode
{TSharedPtr<FStreamableHandle> WealthHandle;TArray<FClassWealthEntry*> UnLoadWealthEntry;TArray<FClassWealthEntry*> LoadWealthEntry;FName ObjectName;FName FunName;// 多个生成位置TArray<FTransform> SpawnTransforms;// 是否只加载 Classbool IsLoadClass;// 保存生成的对象与名字TArray<FName> NameGroup;TArray<UObject*> ObjectGroup;TArray<AActor*> ActorGroup;TArray<UUserWidget*> WidgetGroup;ClassKindLoadNode(TSharedPtr<FStreamableHandle> InWealthHandle, TArray<FClassWealthEntry*> InUnLoadWealthEntry, TArray<FClassWealthEntry*>& InLoadWealthEntry, FName InObjectName, FName InFunName){WealthHandle = InWealthHandle;UnLoadWealthEntry = InUnLoadWealthEntry;LoadWealthEntry = InLoadWealthEntry;ObjectName = InObjectName;FunName = InFunName;IsLoadClass = true; // 补充}// 创建 UClass 对象所使用的构造函数ClassKindLoadNode(TSharedPtr<FStreamableHandle> InWealthHandle, TArray<FClassWealthEntry*> InUnLoadWealthEntry, TArray<FClassWealthEntry*>& InLoadWealthEntry, FName InObjectName, FName InFunName, TArray<FTransform> InSpawnTransforms){WealthHandle = InWealthHandle;UnLoadWealthEntry = InUnLoadWealthEntry;LoadWealthEntry = InLoadWealthEntry;ObjectName = InObjectName;FunName = InFunName;SpawnTransforms = InSpawnTransforms;IsLoadClass = false; }
};void UDDWealth::DealClassSingleLoadStack()
{// ... 省略else {if (ClassSingleLoadStack[i]->WealthEntry->WealthType == EWealthType::Object) {UObject* InstObject = NewObject<UObject>(this, ClassSingleLoadStack[i]->WealthEntry->WealthClass);InstObject->AddToRoot();// 更改BackObjectSingle(ModuleIndex, ClassSingleLoadStack[i]->ObjectName, ClassSingleLoadStack[i]->FunName, ClassSingleLoadStack[i]->WealthEntry->WealthName, InstObject);}else if (ClassSingleLoadStack[i]->WealthEntry->WealthType == EWealthType::Actor) {AActor* InstActor = GetDDWorld()->SpawnActor<AActor>(ClassSingleLoadStack[i]->WealthEntry->WealthClass, ClassSingleLoadStack[i]->SpawnTransform);// 更改BackActorSingle(ModuleIndex, ClassSingleLoadStack[i]->ObjectName, ClassSingleLoadStack[i]->FunName, ClassSingleLoadStack[i]->WealthEntry->WealthName, InstActor);}else if (ClassSingleLoadStack[i]->WealthEntry->WealthType == EWealthType::Widget) {UUserWidget* InstWidget = CreateWidget<UUserWidget>(GetDDWorld(), ClassSingleLoadStack[i]->WealthEntry->WealthClass);GCWidgetGroup.Push(InstWidget);// 更改BackWidgetSingle(ModuleIndex, ClassSingleLoadStack[i]->ObjectName, ClassSingleLoadStack[i]->FunName, ClassSingleLoadStack[i]->WealthEntry->WealthName, InstWidget);}}CompleteStack.Push(ClassSingleLoadStack[i]);}}// ... 省略
}void UDDWealth::DealClassKindLoadStack()
{TArray<ClassKindLoadNode*> CompleteStack;for (int i = 0; i < ClassKindLoadStack.Num(); ++i) {// 补充判断条件,句柄可用才继续执行加载if (ClassKindLoadStack[i]->WealthHandle.IsValid() && ClassKindLoadStack[i]->WealthHandle->HasLoadCompleted() && ClassKindLoadStack[i]->UnLoadWealthEntry.Num() > 0) {// ... 省略}if (ClassKindLoadStack[i]->UnLoadWealthEntry.Num() == 0) {// 将原来的代码多套一层 if 判断,确定是否要生成对象// 加载 UClass 或者直接生成资源的情况来处理if(ClassKindLoadStack[i]->IsLoadClass) {TArray<FName> NameGroup;TArray<UClass*> WealthGroup;for (int j = 0; j < ClassKindLoadStack[i]->LoadWealthEntry.Num(); ++j) {NameGroup.Push(ClassKindLoadStack[i]->LoadWealthEntry[j]->WealthName);WealthGroup.Push(ClassKindLoadStack[i]->LoadWealthEntry[j]->WealthClass);}BackClassWealthKind(ModuleIndex, ClassKindLoadStack[i]->ObjectName, ClassKindLoadStack[i]->FunName, NameGroup, WealthGroup);CompleteStack.Push(ClassKindLoadStack[i]);} else { // 如果要生成对象// 从已加载的资源数组中取出第一个FClassWealthEntry* WealthEntry = ClassKindLoadStack[i]->LoadWealthEntry[0];// 移除出序列ClassKindLoadStack[i]->LoadWealthEntry.RemoveAt(0);// 根据资源类型生成对象if (WealthEntry->WealthType == EWealthType::Object) {UObject* InstObject = NewObject<UObject>(this, WealthEntry->WealthClass);InstObject->AddToRoot();// 添加找参数数组ClassKindLoadStack[i]->NameGroup.Push(WealthEntry->WealthName);ClassKindLoadStack[i]->ObjectGroup.Push(InstObject);// 判断是否生成了全部的对象if (ClassKindLoadStack[i]->LoadWealthEntry.Num() == 0) {// 给请求者传递生成的对象BackObjectKind(ModuleIndex, ClassKindLoadStack[i]->ObjectName, ClassKindLoadStack[i]->FunName, ClassKindLoadStack[i]->NameGroup, ClassKindLoadStack[i]->ObjectGroup);// 添加到完成序列CompleteStack.Push(ClassKindLoadStack[i]);}}else if (WealthEntry->WealthType == EWealthType::Actor) {// 获取生成位置FTransform SpawnTransform = ClassKindLoadStack[i]->SpawnTransforms.Num() == 1 ? ClassKindLoadStack[i]->SpawnTransforms[0] : ClassKindLoadStack[i]->SpawnTransforms[ClassKindLoadStack[i]->ActorGroup.Num()];// 生成对象AActor* InstActor = GetDDWorld()->SpawnActor<AActor>(WealthEntry->WealthClass, SpawnTransform);// 添加找参数数组ClassKindLoadStack[i]->NameGroup.Push(WealthEntry->WealthName);ClassKindLoadStack[i]->ActorGroup.Push(InstActor);// 判断是否生成了全部的对象if (ClassKindLoadStack[i]->LoadWealthEntry.Num() == 0) {// 给请求者传递生成的对象BackActorKind(ModuleIndex, ClassKindLoadStack[i]->ObjectName, ClassKindLoadStack[i]->FunName, ClassKindLoadStack[i]->NameGroup, ClassKindLoadStack[i]->ActorGroup);// 添加到完成序列CompleteStack.Push(ClassKindLoadStack[i]);}}else if (WealthEntry->WealthType == EWealthType::Widget) {UUserWidget* InstWidget = CreateWidget<UUserWidget>(GetDDWorld(), WealthEntry->WealthClass);// 避免回收GCWidgetGroup.Push(InstWidget);// 添加找参数数组ClassKindLoadStack[i]->NameGroup.Push(WealthEntry->WealthName);ClassKindLoadStack[i]->WidgetGroup.Push(InstWidget);// 判断是否生成了全部的对象if (ClassKindLoadStack[i]->LoadWealthEntry.Num() == 0) {// 给请求者传递生成的对象BackWidgetKind(ModuleIndex, ClassKindLoadStack[i]->ObjectName, ClassKindLoadStack[i]->FunName, ClassKindLoadStack[i]->NameGroup, ClassKindLoadStack[i]->WidgetGroup);// 添加到完成序列CompleteStack.Push(ClassKindLoadStack[i]);}}} }}// ... 省略
}void UDDWealth::BuildSingleClassWealth(EWealthType WealthType, FName WealthName, FName ObjectName, FName FunName, FTransform SpawnTransform)
{// ... 省略if (WealthEntry->WealthClass) {if (WealthType == EWealthType::Object) {UObject* InstObject = NewObject<UObject>(this, WealthEntry->WealthClass);InstObject->AddToRoot();// 更改BackObjectSingle(ModuleIndex, ObjectName, FunName, WealthName, InstObject);}else if (WealthType == EWealthType::Actor) {AActor* InstActor = GetDDWorld()->SpawnActor<AActor>(WealthEntry->WealthClass, SpawnTransform);// 更改BackActorSingle(ModuleIndex, ObjectName, FunName, WealthName, InstActor);}else if (WealthType == EWealthType::Widget) {UUserWidget* InstWidget = CreateWidget<UUserWidget>(GetDDWorld(), WealthEntry->WealthClass);GCWidgetGroup.Push(InstWidget);// 更改BackWidgetSingle(ModuleIndex, ObjectName, FunName, WealthName, InstWidget);}}// ... 省略
}void UDDWealth::BuildKindClassWealth(EWealthType WealthType, FName WealthKind, FName ObjectName, FName FunName, TArray<FTransform> SpawnTransforms)
{TArray<FClassWealthEntry*> WealthEntryGroup = GetClassKindEntry(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]->WealthPtr.ToSoftObjectPath().IsValid()) {DDH::Debug() << ObjectName << " Get Not Valid In Kind : " << WealthKind << " For Name : " << WealthEntryGroup[i]->WealthName << DDH::Endl();return;}// 如果资源类型不匹配if (WealthEntryGroup[i]->WealthType != WealthType) {DDH::Debug() << ObjectName << " Get Error Type In Kind : " << WealthKind << " For Name : " << WealthEntryGroup[i]->WealthName << DDH::Endl();return;}}// 对于 Actor,有多少个对象就有多少个 Transform;对于 Object 和 Widget 则只有一个 Transform// 判断 Transform 数组是否为 1 或者是否为 WealthEntryGroup 的数量if (WealthType == EWealthType::Actor && SpawnTransforms.Num() != 1 && SpawnTransforms.Num() != WealthEntryGroup.Num()) {DDH::Debug() << ObjectName << " Send Error Spawn Count : " << WealthKind << DDH::Endl();return;}// 未加载的资源链接TArray<FClassWealthEntry*> UnLoadWealthEntry;// 已加载的资源链接TArray<FClassWealthEntry*> LoadWealthEntry;// 资源分类for (int i = 0; i < WealthEntryGroup.Num(); ++i) {if (WealthEntryGroup[i]->WealthClass)LoadWealthEntry.Push(WealthEntryGroup[i]);elseUnLoadWealthEntry.Push(WealthEntryGroup[i]);}// 声明一个加载句柄TSharedPtr<FStreamableHandle> WealthHandle;// 如果有未加载的资源if (UnLoadWealthEntry.Num() > 0) {// 获取资源路径TArray<FSoftObjectPath> WealthPaths;for (int i = 0; i < UnLoadWealthEntry.Num(); ++i)WealthPaths.Push(UnLoadWealthEntry[i]->WealthPtr.ToSoftObjectPath());// 获取加载句柄WealthHandle = WealthLoader.RequestAsyncLoad(WealthPaths);}// 创建帧处理的节点ClassKindLoadStack.Push(new ClassKindLoadNode(WealthHandle, UnLoadWealthEntry, LoadWealthEntry, ObjectName, FunName, SpawnTransforms));
}
剩余部分(补全调用链与测试功能)留到下一节课。
相关文章:

UE4运用C++和框架开发坦克大战教程笔记(十五)(第46~48集)
UE4运用C和框架开发坦克大战教程笔记(十五)(第46~48集) 46. 批量加载 UClass 功能测试批量加载多个同类 UClass 资源 47. 创建单个资源对象测试加载并创建单个 UClass 资源对象 48. 创建同类资源对象 46. 批量加载 UClass 功能 逻…...

《Linux系列》Linux虚拟机,LVM逻辑卷扩容,xfs文件系统扩容
Linux虚拟机,LVM逻辑卷扩容,xfs文件系统扩容 1 虚拟机配置介绍 在创建虚拟机的时候只给了20G磁盘空间大小,但是现在需求变更,想要增加到40G磁盘空间大小,所以需要通过两步扩容磁盘空间。 系统版本是Centos7 根目录…...

springboot(ssm动漫手办商城 动漫周边商系统Java系统
springboot(ssm动漫手办商城 动漫周边商系统Java系统 开发语言:Java 框架:springboot(可改ssm) vue JDK版本:JDK1.8(或11) 服务器:tomcat 数据库:mysql 5.7…...

卸载 MariaDB:
如果你想将 MariaDB 5.5.68 替换为 MySQL 8,请按照以下步骤操作。在执行这些步骤之前,请确保你已经备份了所有重要的数据库和数据,以防发生意外情况。 1. 卸载 MariaDB: 使用适合你系统的包管理器卸载 MariaDB。在 CentOS/RHEL …...

javaweb总览
javaweb需要学习哪些技术 前端web开发: 技术描述HTML用于构建网站的基础结构的css用于美化页面的,作用和化妆或者整容作用一样JavaScript实现网页和用户的交互Vue主要用于将数据填充到html页面上的Element主要提供了一些非常美观的组件Nginx一款web服务…...

树,二叉树及其相关知识
1.树概念及结构 1.1树的概念 树是一种非线性的数据结构,它是由n(n>0)个有限结点组成一个具有层次关系的集合。把它叫做树是因 为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。 有一个特殊的结点&#…...

NumPy必知必会50例 | 5. 高级数组操作:成为 NumPy 数组的忍者
文章目录 5. 高级数组操作:成为 NumPy 数组的忍者数组重塑:变形大师例子:从一维到二维 数组合并:忍者团队联合例子:水平和垂直合并 数组分割:忍者的快速撤退例子:水平和垂直分割 5. 高级数组操作…...

《WebKit 技术内幕》学习之五(3): HTML解释器和DOM 模型
3 DOM的事件机制 基于 WebKit 的浏览器事件处理过程:首先检测事件发生处的元素有无监听者,如果网页的相关节点注册了事件的监听者则浏览器会将事件派发给 WebKit 内核来处理。另外浏览器可能也需要处理这样的事件(浏览器对于有些事件必须响应…...

extends 和 implements
以下是 extends 和 implements 在Java代码中的区别和示例: 示例1:使用 extends 实现类继承 // 定义一个父类 Animal public class Animal {public void eat() {System.out.println("动物在吃东西");}public void sleep() {System.out.printl…...

响应拦截器的 return Promise.reject(res.data.message)
今天在看老师讲解代码的时候,解决了我心中的一些疑惑。 在做excel文件导出的时候,没有告诉浏览器文件的格式是Blod产生了报错。 看下图: 可以看到下面的内容:如果业务成功 返回 res.data 如果业务失败,给出错误信息的提示,将这个错误抛出去。 因此我们在发送一个…...

Windows下 VS2022 编译OpenSSL 库
SSL是Secure Sockets Layer(安全套接层协议)的缩写,可以在Internet上提供秘密性传输。Netscape公司在推出第一个Web浏览器的同时,提出了SSL协议标准。其目标是保证两个应用间通信的保密性和可靠性,可在服务器端和用户端同时实现支持。已经成为Internet上保密通讯的工业标准…...

【GitHub项目推荐--一个简单的绘图应用程序(Rust + GTK4)】【转载】
一个用 Rust 和 GTK4 编写的简单的绘图应用程序来创建手写笔记。 Rnote 旨在成为一个简单但实用的笔记应用程序,用于手绘或注释图片或文档。它最终能够导入/导出各种媒体文件格式。而且输出的作品是基于矢量的,这使其在编辑和更改内容时非常灵活。 地址…...

【算法小记】——机器学习中的概率论和线性代数,附线性回归matlab例程
内容包含笔者个人理解,如果错误欢迎评论私信告诉我 线性回归matlab部分参考了up主DR_CAN博士的课程 机器学习与概率论 在回归拟合数据时,根据拟合对象,可以把分类问题视为一种简答的逻辑回归。在逻辑回归中算法不去拟合一段数据而是判断输入…...

MySQL数据库的锁机制
目录 一、引言 二、锁的类型及作用 2.1 行级锁 2.2 间隙锁与临键锁 2.3 共享锁与排他锁 2.4 意向锁 2.5 表级锁 2.6 元数据锁 三、锁的管理与优化 3.1 合理设置事务隔离级别 3.2 避免长事务 3.3 索引优化 3.4 明确锁定范围 3.5 避免不必要的全表扫描 四、实战分…...

解决 conda新建虚拟环境只有一个conda-meta文件&conda新建虚拟环境不干净
像以前一样通过conda 新建虚拟环境时发现环境一团糟,首先新建虚拟环境 conda create -n newenv这时候activate newenv,通过pip list,会发现有很多很多的包,都是我在其他环境用到的。但诡异的是,来到anaconda下env的目…...

React16源码: React中的completeWork对HostText处理含更新的源码实现
HostText 1 )概述 在 completeWork 中 对 HostText的处理在第一次挂载和后续更新的不同条件下进行操作 第一次挂载主要是创建实例后续更新其实也是重新创建实例 2 )源码 定位到 packages/react-reconciler/src/ReactFiberCompleteWork.js#L663 到 c…...

网络协议与攻击模拟_07UDP协议
一、简单概念 1、UDP协议简介 UDP(用户数据报)协议,是传输层的协议。不需要建立连接,直接发送数据,不会重新排序,不需要确认。 2、UDP报文字段 源端口目的端口UDP长度UDP校验和 3、常见的UDP端口号 5…...

生命在于折腾——WeChat机器人的研究和探索
一、前言 2022年,我玩过原神,当时看到了云崽的QQ机器人,很是感兴趣,支持各种插件,查询游戏内角色相关信息,当时我也自己写了几个插件,也看到很多大佬编写的好玩的插件,后来因为QQ不…...

融资项目——EasyExcel将Excel文件保存至数据库
上一篇博客已经基本介绍了EasyExcel的配置与基本使用方法。现在准备使用EasyExcel将Excel文件保存至数据库。 1.由于我们想每读取Excel中的N条记录后将这些记录全部写入数据库中。所以首先我们在Mybatis文件内先要写一个批量保存Excel文件中的记录的sql语句。 <insert id&q…...

【Oracle】设置FGA(Fine-Grained Audit)细粒度审计
文章目录 【Oracle】设置FGA(Fine-Grained Audit)细粒度审计参考 【声明】文章仅供学习交流,观点代表个人,与任何公司无关。 编辑|SQL和数据库技术(ID:SQLplusDB) 收集Oracle数据库内存相关的信息 【Oracle】ORA-32017和ORA-00384错误处理 【Oracle】设…...

js vue调用activex ocx
js vue调用activex ocx 与IE调用方式不同处 CLSID和TYPE <OBJECT id"MultiplyDemo" refocx1 CLSID"{8EEF7302-1FC8-4BA0-8EA5-EC29FDBCA45B}" TYPE"application/x-itst-activex" width15% height15%></OBJECT>//调用方式1 //或是 …...

Hbas简介:数据模型和概念、物理视图
文章目录 说明零 BigTable一 Hbase简介二 HBase 访问接口简介三 行式&列式存储四 HBase 数据模型4.1 HBase 列族数据模型4.2 数据模型的相关概念4.3 数据坐标 五 概念&物理视图 说明 本文参考自林子雨老师的大数据技术原理与应用(第三版)教材内容,仅供学习…...

uniapp css样式穿透
目录 前言css样式穿透方法不加css样式穿透的代码加css样式穿透的代码不加css样式穿透的代码 与 加css样式穿透的代码 的差别参考 前言 略 css样式穿透方法 使用 /deep/ 进行css样式穿透 不加css样式穿透的代码 <style>div {background-color: #ddd;} </style>…...

【立创EDA-PCB设计基础完结】7.DRC设计规则检查+优化与丝印调整+打样与PCB生产进度跟踪
前言:本文为PCB设计基础的最后一讲,在本专栏中【立创EDA-PCB设计基础】前面已经将所有网络布线铺铜好了,接下来进行DRC设计规则检查优化与丝印调整打样与PCB生产进度跟踪 目录 1.DRC设计规则检查 2.优化与丝印调整 1.过孔连接优化 2.泪滴…...

android 线程池的管理工具类
封装了各种类型的线程池,方便直接使用 看下有哪些类型: 默认线程池,搜索模块专用线程池,网络请求专用线程池,U盘更新,同步SDK读写操作线程池,日志打印使用线程池 DEFALUT,SEARCH&…...

编码风格之(5)GNU软件编码风格(3)
GNU软件编码标准风格(3) Author:Onceday Date: 2024年1月21日 漫漫长路,才刚刚开始… 本文主要翻译自《GNU编码标准》(GNU Coding Standards)一文。 参考文档: Linux kernel coding style — The Linux Kernel documentationGNU Coding Standard…...

8 种网络协议
什么是网络协议? 网络协议就是计算机之间沟通的语言,为了有效地交流,计算机之间需要一种共同的规则或协议,就像我们和老外沟通之前,要先商量好用哪种语言,要么大家都说中文,要么大家都说英语&a…...

Flash读取数据库中的数据
Flash读取数据库中的数据 要读取数据库的记录,首先需要建立一个数据库,并输入一些数据。数据库建立完毕后,由Flash向ASP提交请求,ASP根据请求对数据库进行操作后将结果返回给Flash,Flash以某种方式把结果显示出来。 …...

如何写出规范优雅的代码
编码规范是成为一个优质程序员的重要一课,它是编程的样式的模板。这篇文章将介绍12中编程规范及技巧,相信学习之后你的代码一定会提升一个档次。 首先我们要明确,为什么要遵循编码规范?遵循这样的约定有什么好处? 遵循…...

【数据结构】链表(单链表与双链表实现+原理+源码)
博主介绍:✌全网粉丝喜爱、前后端领域优质创作者、本质互联网精神、坚持优质作品共享、掘金/腾讯云/阿里云等平台优质作者、擅长前后端项目开发和毕业项目实战✌有需要可以联系作者我哦! 🍅附上相关C语言版源码讲解🍅 ὄ…...