UE4 C++ 蓝图函数模板 自定义解析

UE4 C++ 蓝图函数模板 自定义解析

  • 版本:4.20
  • 泛型编程
  • 蓝图函数模板

版本:4.20

泛型编程

模板是泛型编程的基础,即以一种独立于任何类型的方式编写代码。模板是创建一系列类(Class)或函数(Function)的通用标准或公式,在C++中有很多使用模板概念的例子。如:迭代器,向量等( vector 或 vector )

我们知道,在C++中定义一个普通函数模板并不难,如:

template
int compare(const T& left, const T& right) {
if (left < right) {
return -1; 
}
if (right < left) {
return 1; 
}
return 0;
}

compare(1, 2); //使用模板函数

以上代码定义了一个Compare的函数模板,可以支持多种类型的比较。

蓝图函数模板

那么对于UE4而言,创建一个蓝图函数模板,以便于将C++泛型编程的思想运用于更多的场合是否可行呢?仔细分析,我们需要一个函数模板的原因,是因为我们需要一个能可以为任意类型的参数,即一个通用结构。那么问题就变成了如何创建一个可被蓝图调用的函数,该函数至少接收一个可能为任意类型的参数,即所谓的wildcard structs。

如何创建一个可被蓝图调用的函数?不会的要被打手手喔~

要创建一个wildcard struct,需要我们手动解析该Struct,即UProperty,这里额外补充一个各个属性的继承结构,如图:
UE4 C++ 蓝图函数模板 自定义解析_第1张图片

详情见官方文档(https://api.unrealengine.com/INT/API/Runtime/CoreUObject/UObject/UField/index.html)
其中蓝图使用的结构为UScriptStruct,也就是说如果你是通过蓝图创建的结构,该结构的基类就是UScriptStruct。UScriptStruct比较折磨人的一点是在它生成的key后面带了一大串自动生成的后缀,好像是以"_"拼接的(感兴趣的可以自己去看看这个磨人的小妖精什么样儿~),因此在使用本结构搞事情的时候,如果没有得到意想中的结果,可往这方面调试。

手动解析UProperty需要在声明函数时添加宏 CustomThunk ,阻止UBT自动生成解析代码,还应添加 CustomStructureParam 表明我们为此函数添加手动解析,声明代码如下:

    // 仅有一个通用变量
	UFUNCTION(BlueprintCallable, Category = "TemplateSample", CustomThunk, meta = (CustomStructureParam = "AnyStruct"))
	static void ReceiveAnyStruct(UProperty* AnyStruct);

我们对UStructProperty进行解析,解析代码如下:

//BlueprintFunctionTemplateSample.h

DECLARE_FUNCTION(execReceiveAnyStruct)
{
	// Steps into the stack, walking to the next property in it
	Stack.Step(Stack.Object, NULL);

	// Grab the last property found when we walked the stack
	// This does not contains the property value, only its type information
	UStructProperty* StructProperty = ExactCast(Stack.MostRecentProperty);

	// Grab the base address where the struct actually stores its data
	// This is where the property value is truly stored
	void* StructPtr = Stack.MostRecentPropertyAddress;

	// We need this to wrap up the stack
	P_FINISH;

	// Iterate through the struct
	IterateThroughStructProperty(StructProperty, StructPtr);
}

static void IterateThroughStructProperty(UStructProperty* StructProperty, void* StructPtr);
static void ParseProperty(UProperty* Property, void* ValuePtr);








//BlueprintFunctionTemplateSample.cpp


// Fill out your copyright notice in the Description page of Project Settings.

#include "BlueprintFunctionTemplateSample.h"



DEFINE_LOG_CATEGORY(BlueprintFunctionTemplate);

void UBlueprintFunctionTemplateSample::IterateThroughStructProperty(UStructProperty* StructProperty, void* StructPtr)
{
// Walk the structs' properties
if (StructProperty == nullptr)
{
	return;
}

UScriptStruct* Struct = StructProperty->Struct;
for (TFieldIterator It(Struct); It; ++It)
{
	UProperty* Property = *It;

	// This is the variable name if you need it
	FString VariableName = Property->GetName();

	// Never assume ArrayDim is always 1
	for (int32 ArrayIndex = 0; ArrayIndex < Property->ArrayDim; ArrayIndex++)
	{
		// This grabs the pointer to where the property value is stored
		void* ValuePtr = Property->ContainerPtrToValuePtr(StructPtr, ArrayIndex);

		// Parse this property
		ParseProperty(Property, ValuePtr);
	}
}

}

void UBlueprintFunctionTemplateSample::ParseProperty(UProperty* Property, void* ValuePtr)
{
float FloatValue;
int32 IntValue;
bool BoolValue;
FString StringValue;
FName NameValue;
FText TextValue;
FString PropertyName;
Property->GetName(PropertyName);

// Here's how to read integer and float properties
if (UNumericProperty* NumericProperty = Cast(Property))
{
	if (NumericProperty->IsFloatingPoint())
	{
		FloatValue = NumericProperty->GetFloatingPointPropertyValue(ValuePtr);
		UE_LOG(BlueprintFunctionTemplate, Warning, TEXT("%s : %d"), *PropertyName, FloatValue);
	}
	else if (NumericProperty->IsInteger())
	{
		IntValue = NumericProperty->GetSignedIntPropertyValue(ValuePtr);
		UE_LOG(BlueprintFunctionTemplate, Warning, TEXT("%s : %d"), *PropertyName, IntValue);

	}
}

// How to read booleans
if (UBoolProperty* BoolProperty = Cast(Property))
{
	BoolValue = BoolProperty->GetPropertyValue(ValuePtr);
	UE_LOG(BlueprintFunctionTemplate, Warning, TEXT("%s : %s"), *PropertyName, BoolValue ? "True" : "False");

}

// Reading names
if (UNameProperty* NameProperty = Cast(Property))
{
	NameValue = NameProperty->GetPropertyValue(ValuePtr);
	UE_LOG(BlueprintFunctionTemplate, Warning, TEXT("%s : %s"), *PropertyName, *(NameValue.ToString()));

}

// Reading strings
if (UStrProperty* StringProperty = Cast(Property))
{
	StringValue = StringProperty->GetPropertyValue(ValuePtr);
	UE_LOG(BlueprintFunctionTemplate, Warning, TEXT("%s : %s"), *PropertyName, *StringValue);

}

// Reading texts
if (UTextProperty* TextProperty = Cast(Property))
{
	TextValue = TextProperty->GetPropertyValue(ValuePtr);
	UE_LOG(BlueprintFunctionTemplate, Warning, TEXT("%s : %s"), *PropertyName, *(TextValue.ToString()));

}

// Reading an array
if (UArrayProperty* ArrayProperty = Cast(Property))
{
	// We need the helper to get to the items of the array            
	FScriptArrayHelper Helper(ArrayProperty, ValuePtr);
	for (int32 i = 0, n = Helper.Num(); i < n; ++i)
	{
		ParseProperty(ArrayProperty->Inner, Helper.GetRawPtr(i));
	}
}

// Reading a nested struct
if (UStructProperty* StructProperty = Cast(Property))
{
	IterateThroughStructProperty(StructProperty, ValuePtr);
}

}

注,我们这里是对结构体进行解析哈,输入基本类型是不可以的~

蓝图调用如下:
UE4 C++ 蓝图函数模板 自定义解析_第2张图片

输出结果:

在这里插入图片描述

其实自动解析在JSON自动解析那一节已经用到了,感兴趣的同学可以翻阅之前的文章。
如果有需要源码的同学可以私我~

你可能感兴趣的:(unreal4,UE4,C++,自定义解析,蓝图函数模板)