当前位置:   article > 正文

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

UE4运用C++和框架开发坦克大战教程笔记(十五)(第46~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;
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

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);
	// 如果数量为 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;
		}
	}
	// 未加载资源序列
	TArray<FClassWealthEntry*> UnLoadWealthEntry;
	// 已加载资源序列
	TArray<FClassWealthEntry*> LoadWealthEntry;
	// 分类保存
	for (int i = 0; i < WealthEntryGroup.Num(); ++i) {
		if (WealthEntryGroup[i]->WealthClass)
			LoadWealthEntry.Push(WealthEntryGroup[i]);
		else
			UnLoadWealthEntry.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 数量大于 0
		if (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);
			// 清空 UnLoadWealthEntry
			ClassKindLoadStack[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];
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119

测试批量加载多个同类 UClass 资源

接下来是验证环节,我们打算让多个 Actor 生成在场景中,并且为了让它们不会挤到一块,所以每次生成的时候将位置偏移一点。

WealthCallObject.h

public:

	// 回调函数
	UFUNCTION()
	void LoadKindClass(TArray<FName> BackNames, TArray<UClass*> BackWealths);

public:

	// 生成的偏移量
	UPROPERTY(EditAnywhere)
	float OffsetValue;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

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());
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

编译后,在 Blueprint 文件夹下创建一个 ViewActor 文件夹,将 ViewActor1 放进去,并复制两个,分别取名为 ViewActor2ViewActor3。给它俩换一下网格体模型。

更改网格体模型
给 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
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

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);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

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));
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118

补全 DDWealth – DDModule – DDOO – 对象 的调用链。

DDModule.h

public:	

	// 创建一个对象实例
	void BuildSingleClassWealth(EWealthType WealthType, FName WealthName, FName ObjectName, FName FunName, FTransform SpawnTransform);
  • 1
  • 2
  • 3
  • 4

DDModule.cpp

void UDDModule::BuildSingleClassWealth(EWealthType WealthType, FName WealthName, FName ObjectName, FName FunName, FTransform SpawnTransform)
{
	Wealth->BuildSingleClassWealth(WealthType, WealthName, ObjectName,  FunName, SpawnTransform);
}
  • 1
  • 2
  • 3
  • 4

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);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

测试加载并创建单个 UClass 资源对象

我们打算只测试创建一个 Actor 资源对象,并且让它一直旋转。

WealthCallObject.h

public:

	virtual void DDTick(float DeltaSeconds) override;

	// 回调方法
	UFUNCTION()
	void BuildActor(FName BackName, AActor* BackActor);

public:

	// 保存返回的 Actor
	AActor* SingleActor;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

编译后运行游戏,可以看见左上角输出了 ViewActor1,场景中生成了 ViewActor1,并且它一直在旋转。说明加载并创建 Actor 对象的逻辑写好了。

在这里插入图片描述
顺便打开 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);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

DDWealth.cpp

struct ClassKindLoadNode
{
	TSharedPtr<FStreamableHandle> WealthHandle;
	TArray<FClassWealthEntry*> UnLoadWealthEntry;
	TArray<FClassWealthEntry*> LoadWealthEntry;
	FName ObjectName;
	FName FunName;
	// 多个生成位置
	TArray<FTransform> SpawnTransforms;
	// 是否只加载 Class
	bool 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]);
		else
			UnLoadWealthEntry.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));
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224

剩余部分(补全调用链与测试功能)留到下一节课。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/102086
推荐阅读
相关标签
  

闽ICP备14008679号