一、需求描述:
最近在做一个数字孪生项目,需要大量的接收上游的数据显示到程序界面上,同时程序也会给上游反馈数据,比如一个按钮,按下时,给上游反馈1,松开后反馈0.
二:问题说明
需要展示的数据有很多,差不多400个数据。不仅要接收数据显示,还要反馈给上游数据。由于上游制定的传输协议是UDP,将字段都塞到了一个struct中,这个结构我们不能要求改变成更便捷的,所以,只能基于这个通信结构进行开发优化。传统方式是在c++中写对应字段的Get、Set函数,开放给蓝图,支持蓝图调用。但是因为字段量级太大,同时重复性工作非常多,并且这些字段可能还会随时增加,如果依旧采用传统方式,就会导致每次新增时,都要新增代码。
三、解决方案
基于这种情况,我采用的通过反射系统进行结构体字段值的获取与赋值方式。这样,每次结构体有变化时,只需要修改结构体,其他字段的更新与获取无需任何改变。
整体调用的方式为,c++中编写反射脚本,传入字段名可获取对应字段值与设置对应字段值,在蓝图中可调用该方法,并传入字段名。
c++脚本:
首先新建基于BlueprintFunctionLibrary的脚本,
测试结构体:
注意:如果要使用反射,结构体和字段,必须进行UE的标记。
USTRUCT(BlueprintType)
struct FTestStruct
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "TestStruct")
double aa;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "TestStruct")
double bb;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "TestStruct")
int cc;
}
.h文件脚本:
static FTestStruct testStruct;
UFUNCTION(BlueprintCallable, Category = "UDP")
static bool GetStructField(const FString& FieldName, FString& OutValue);
UFUNCTION(BlueprintCallable, Category = "UDP")
static bool SetStructField(const FString& FieldName, const FString& inputvalue);
.cpp脚本:
#include "UObject/NoExportTypes.h"
#include "UObject/Class.h"
#include "UObject/PropertyPortFlags.h"
#include "UObject/TextProperty.h"
#include "UObject/UnrealType.h"
bool UMyUDPBPFunctionLibrary::GetStructField(const FString& FieldName, FString& OutValue)
{
OutValue = "";
UStruct* stu = FTestStruct::StaticStruct();
if (stu)
{
// 使用反射系统获取字段属性
FProperty* Property = stu->FindPropertyByName(*FieldName);
if (Property)
{
// 获取字段的值
const void* ValuePtr = Property->ContainerPtrToValuePtr<void>(&testStruct);
if (ValuePtr)
{
// 将值转换为字符串并存储在OutValue中
Property->ExportTextItem(OutValue, ValuePtr, nullptr, nullptr, PPF_None);
return true;
}
//}
}
}
return false;
}
bool UMyUDPBPFunctionLibrary::SetStructField(const FString& FieldName, const FString& inputvalue)
{
UStruct* stu = FSendToMagic::StaticStruct();
if (stu)
{
// 使用反射系统获取字段属性
FProperty* Property = stu->FindPropertyByName(*FieldName);
if (Property)
{
void* ValuePtr = Property->ContainerPtrToValuePtr<void>(&SPackage_Magic);
// 获取字段的值
//const void* ValuePtr = Property->ContainerPtrToValuePtr<void>(&RPackage_Dymola22);
if (ValuePtr)
{
Property->ImportText(*inputvalue, ValuePtr, 0, nullptr);
return true;
}
}
return false;
}
在蓝图中,即可全局调用GetStructField节点和SetStructField即可使用