UE5/UE4通过函数名调用带参函数
目标:
1. 通过函数名称,直接调用没有入参的函数;
2. 通过函数名和json字符串,直接调用带参函数;
*tips:后面会增加包含返回值的调用案例*
目标一:直接调用没有入参的函数
void CallFunctionByNameNoParam(UObject* inObj, const FString inName) {
UClass* TempClass = inObj->GetClass();
UFunction* TempFunc = TempClass->FindFunctionByName(*inName);
inObj->ProcessEvent(TempFunc,NULL);
目标二:通过函数名+Json字符串,调用带参函数
思路:
1. 通过函数指针获取到参数队列,然后构造一个结构体
2. 结构体转成Json对象,通过读取和设置Json对象的方式,设置参数数值
3. 将参数结构序列化,再反序列化存储之后,作为参数传入到上面执行函数即可
问题:
1. 获取函数参数数量和名称
2. 获取函数参数属性:入参,出参,等
3. 怎么构造函数参数结构
4. 构造参数结构之后,怎么给参数结构设置我们输入的参数值
5. 函数参数数据类型:string,int,color,vector,等
原理:UE5的反射系统函数和参数的关系
//方法原型
int32 UMyClass::Func(float param1);
UFUNCTION(BlueprintCallable)
int32 InvokeFunction(UObject* obj, FName functionName,float param1)
struct MyClass_Func_Parms //定义一个结构用来包装参数和返回值,就像在gen.cpp里那样
float param1;
int32 ReturnValue;
UFunction* func = obj->FindFunctionChecked(functionName);
MyClass_Func_Parms params;
params.param1=param1;
obj->ProcessEvent(func, ¶ms);
return params.ReturnValue;
int r=InvokeFunction(obj,"Func",123.f);
问题解决:
- 获取参数数量和参数结构尺寸
void CallFunctionByNameWithParam(UObject* inObj, const FString inName, const FString inJsonStr)
UClass* TempClass = inObj->GetClass();
UFunction* TempFunc=TempClass->FindFunctionByName(*inName);
if (!TempFunc) check(false);
uint16 TempParamsSize = TempFunc->ParmsSize;
uint8 TempParamsNum = TempFunc->NumParms;
}
2. 把参数容器反序列化为Json对象
void CallFunctionByNameWithParam(UObject* inObj, const FString inName, const FString inJsonStr)
UClass* TempClass = inObj->GetClass();
UFunction* TempFunc=TempClass->FindFunctionByName(*inName);
if (!TempFunc) check(false);
uint16 TempParamsSize = TempFunc->ParmsSize;
uint8 TempParamsNum = TempFunc->NumParms;
TArray<uint8> Params;
Params.AddUninitialized(TempParamsSize);
TempFunc->InitializeStruct(Params.GetData());
SharedPtr<FJsonObject> TempJsonObject = MakeShared<FJsonObject>();
bool Ret = FJsonObjectConverter::UStructToJsonObject(TempFunc, TempFunc, TempJsonObject.ToSharedRef());
}
3. 把传入的Json字符串反序列化为Json对象
TSharedRef<TJsonReader<>> JsonReader = TJsonReaderFactory<>::Create(inJsonStr);
TSharedPtr<FJsonObject> inJsonObject;
bool bIsOk = FJsonSerializer::Deserialize(JsonReader, inJsonObject);
4. 读取入参Json字符串构造的Json对象值给到结构反序列化的对象
//已经测试过的入参数据类型
FString
FRotator
bool
uint8
int64
double
FName
int32
FVector
FTransform
FColor
FLinearColor
//所有参数区分
/** All parameter flags */
#define CPF_ParmFlags (CPF_Parm | CPF_OutParm | CPF_ReturnParm | CPF_ReferenceParm | CPF_ConstParm)
//UProperty属性区分:EPropertyFlags
enum EPropertyFlags : uint64
CPF_None = 0,
CPF_Edit = 0x0000000000000001, ///< Property is user-settable in the editor.
CPF_ConstParm = 0x0000000000000002, ///< This is a constant function parameter
CPF_BlueprintVisible = 0x0000000000000004, ///< This property can be read by blueprint code
CPF_ExportObject = 0x0000000000000008, ///< Object can be exported with actor.
CPF_BlueprintReadOnly = 0x0000000000000010, ///< This property cannot be modified by blueprint code
CPF_Net = 0x0000000000000020, ///< Property is relevant to network replication.
CPF_EditFixedSize = 0x0000000000000040, ///< Indicates that elements of an array can be modified, but its size cannot be changed.
CPF_Parm = 0x0000000000000080, ///< Function/When call parameter.
CPF_OutParm = 0x0000000000000100, ///< Value is copied out after function call.
CPF_ZeroConstructor = 0x0000000000000200, ///< memset is fine for construction
CPF_ReturnParm = 0x0000000000000400, ///< Return value.
CPF_DisableEditOnTemplate = 0x0000000000000800, ///< Disable editing of this property on an archetype/sub-blueprint
//CPF_ = 0x0000000000001000, ///<
CPF_Transient = 0x0000000000002000, ///< Property is transient: shouldn't be saved or loaded, except for Blueprint CDOs.
CPF_Config = 0x0000000000004000, ///< Property should be loaded/saved as permanent profile.
//CPF_ = 0x0000000000008000, ///<
CPF_DisableEditOnInstance = 0x0000000000010000, ///< Disable editing on an instance of this class
CPF_EditConst = 0x0000000000020000, ///< Property is uneditable in the editor.
CPF_GlobalConfig = 0x0000000000040000, ///< Load config from base class, not subclass.
CPF_InstancedReference = 0x0000000000080000, ///< Property is a component references.
//CPF_ = 0x0000000000100000, ///<
CPF_DuplicateTransient = 0x0000000000200000, ///< Property should always be reset to the default value during any type of duplication (copy/paste, binary duplication, etc.)
//CPF_ = 0x0000000000400000, ///<
//CPF_ = 0x0000000000800000, ///<
CPF_SaveGame = 0x0000000001000000, ///< Property should be serialized for save games, this is only checked for game-specific archives with ArIsSaveGame
CPF_NoClear = 0x0000000002000000, ///< Hide clear (and browse) button.
//CPF_ = 0x0000000004000000, ///<
CPF_ReferenceParm = 0x0000000008000000, ///< Value is passed by reference; CPF_OutParam and CPF_Param should also be set.
CPF_BlueprintAssignable = 0x0000000010000000, ///< MC Delegates only. Property should be exposed for assigning in blueprint code
CPF_Deprecated = 0x0000000020000000, ///< Property is deprecated. Read it from an archive, but don't save it.
CPF_IsPlainOldData = 0x0000000040000000, ///< If this is set, then the property can be memcopied instead of CopyCompleteValue / CopySingleValue
CPF_RepSkip = 0x0000000080000000, ///< Not replicated. For non replicated properties in replicated structs
CPF_RepNotify = 0x0000000100000000, ///< Notify actors when a property is replicated
CPF_Interp = 0x0000000200000000, ///< interpolatable property for use with cinematics
CPF_NonTransactional = 0x0000000400000000, ///< Property isn't transacted
CPF_EditorOnly = 0x0000000800000000, ///< Property should only be loaded in the editor
CPF_NoDestructor = 0x0000001000000000, ///< No destructor
//CPF_ = 0x0000002000000000, ///<
CPF_AutoWeak = 0x0000004000000000, ///< Only used for weak pointers, means the export type is autoweak
CPF_ContainsInstancedReference = 0x0000008000000000, ///< Property contains component references.
CPF_AssetRegistrySearchable = 0x0000010000000000, ///< asset instances will add properties with this flag to the asset registry automatically
CPF_SimpleDisplay = 0x0000020000000000, ///< The property is visible by default in the editor details view
CPF_AdvancedDisplay = 0x0000040000000000, ///< The property is advanced and not visible by default in the editor details view
CPF_Protected = 0x0000080000000000, ///< property is protected from the perspective of script
CPF_BlueprintCallable = 0x0000100000000000, ///< MC Delegates only. Property should be exposed for calling in blueprint code
CPF_BlueprintAuthorityOnly = 0x0000200000000000, ///< MC Delegates only. This delegate accepts (only in blueprint) only events with BlueprintAuthorityOnly.
CPF_TextExportTransient = 0x0000400000000000, ///< Property shouldn't be exported to text format (e.g. copy/paste)
CPF_NonPIEDuplicateTransient = 0x0000800000000000, ///< Property should only be copied in PIE
CPF_ExposeOnSpawn = 0x0001000000000000, ///< Property is exposed on spawn
CPF_PersistentInstance = 0x0002000000000000, ///< A object referenced by the property is duplicated like a component. (Each actor should have an own instance.)
CPF_UObjectWrapper = 0x0004000000000000, ///< Property was parsed as a wrapper class like TSubclassOf<T>, FScriptInterface etc., rather than a USomething*
CPF_HasGetValueTypeHash = 0x0008000000000000, ///< This property can generate a meaningful hash value.
CPF_NativeAccessSpecifierPublic = 0x0010000000000000, ///< Public native access specifier
CPF_NativeAccessSpecifierProtected = 0x0020000000000000, ///< Protected native access specifier
CPF_NativeAccessSpecifierPrivate = 0x0040000000000000, ///< Private native access specifier
CPF_SkipSerialization = 0x0080000000000000, ///< Property shouldn't be serialized, can still be exported to text
};
//访问我们需要的参数类型和名称
for (TFieldIterator<FProperty> i(TempFunc); i; ++i)
FProperty* TempParam = *i;
FString TempType = TempParam->GetCPPType();
UE_LOG(LogTemp, Warning, TEXT("Current param Type is ==%s"), *TempType);
const FString One_Param = TempParam->GetNameCPP();
UE_LOG(LogTemp, Warning, TEXT("Current param Name is ==%s"), *One_Param);
}
//把入参Json字符串构造的对象值,赋值给参数对象数组
需要注意FVector和FRotator参数的读取方式
for (TFieldIterator<FProperty> i(TempFunc); i; ++i)
FProperty* TempParam = *i;
FString TempType = TempParam->GetCPPType();
UE_LOG(LogTemp, Warning, TEXT("Current param Type is ==%s"), *TempType);
const FString One_Param = TempParam->GetNameCPP();
if (TempParam->PropertyFlags & CPF_Parm)
if (TempType.Equals("bool")){
bool TempValue = inJsonObject->GetBoolField(One_Param);
TempJsonObject->SetBoolField(One_Param, TempValue);
}else if (TempType.Equals("FString")){
FString TempValue = inJsonObject->GetStringField(One_Param);
TempJsonObject->SetStringField(One_Param, TempValue);
}else if (TempType.Equals("int32")) {
int32 TempValue = inJsonObject->GetNumberField(One_Param);
TempJsonObject->SetNumberField(One_Param, TempValue);
}else if (TempType.Equals("int64")) {
int64 TempValue = inJsonObject->GetNumberField(One_Param);
TempJsonObject->SetNumberField(One_Param, TempValue);
}else if (TempType.Equals("FVector")) {
FVector TempValue;
TSharedPtr<FJsonObject> TempObj = inJsonObject->GetObjectField(One_Param);
TempValue.X = TempObj->GetNumberField("X");
TempValue.Y = TempObj->GetNumberField("Y");
TempValue.Z = TempObj->GetNumberField("Z");
TempJsonObject->GetObjectField(One_Param)->SetNumberField("X", TempValue.X);
TempJsonObject->GetObjectField(One_Param)->SetNumberField("Y", TempValue.Y);
TempJsonObject->GetObjectField(One_Param)->SetNumberField("Z", TempValue.Z);
}else if (TempType.Equals("FRotator")) {
FRotator TempValue;
TSharedPtr<FJsonObject> TempObj = inJsonObject->GetObjectField(One_Param);
TempValue.Roll = TempObj->GetNumberField("Roll");
TempValue.Pitch= TempObj->GetNumberField("Pitch");
TempValue.Yaw= TempObj->GetNumberField("Yaw");
TempJsonObject->GetObjectField(One_Param)->SetNumberField("Roll", TempValue.Roll);
TempJsonObject->GetObjectField(One_Param)->SetNumberField("Pitch", TempValue.Pitch);
TempJsonObject->GetObjectField(One_Param)->SetNumberField("Z", TempValue.Yaw);
}else if (TempType.Equals("FTransform")){
}else if (TempType.Equals("FColor")) {
}else if (TempType.Equals("FLinearColor")) {
}else if (TempType.Equals("FName")) {
}else if (TempType.Equals("double")) {
}else if (TempType.Equals("uint8")) {
}
5. 序列化已经有了值的Json对象,然后调用即可
TArray<uint8> ParamsContainer;
FMemoryWriter Writer(ParamsContainer);
TSharedRef<TJsonWriter<UCS2CHAR>> JsonWriter = TJsonWriter<UCS2CHAR>::Create(&Writer);
FJsonSerializer::Serialize(TempJsonObject.ToSharedRef(), JsonWriter);
FMemoryReader Reader(ParamsContainer);
FJsonStructDeserializerBackend DeserializerBackend(Reader);
FStructDeserializer::Deserialize(Params.GetData(), *TempFunc, DeserializerBackend);
inObj->ProcessEvent(TempFunc, Params.GetData());
Params.Empty();
Reader.Close();
Writer.Close();
6.测试结果
遗留问题:
1. 每次修改蓝图函数之后,保存,调用,会直接崩溃:可能原因,重新保存会导致反射系统重新编译函数,和上次函数调用的参数结构出现冲突
2.再函数体里面print输出之前没法添加append,但是事件可以
补充解决上面问题
.cpp
调用蓝图中普通带参的函数内容
//SetActorScale3D
"NewScale3D": {
"X": 100.0,
"Y": 200.0,
"Z": 300.0
//SetActorHiddenInGame
"bNewHidden":true
}
void UOpenAPICoreSystem::CallFunctionByNameWithParam(UObject* inObj, const FString inName, const FString inJsonStr) {
UFunction* TempFunc = nullptr;
UClass* TempClass = nullptr;
UClass* Super = nullptr;
uint16 TempParamsSize=0;
uint8 TempParamsNum = 0;
TempClass = inObj->GetClass();
TempFunc = TempClass->FindFunctionByName(*inName);
//开始遍历参数写入到Json数据里面
TempParamsSize = TempFunc->ParmsSize;
TempParamsNum = TempFunc->NumParms;
if (TempParamsNum & 0)
CallFunctionByNameNoParam(inObj, inName);
else {
TArray<uint8> Params;
Params.AddUninitialized(TempParamsSize);
TempFunc->InitializeStruct(Params.GetData());
TSharedPtr<FJsonObject> TempJsonObject = MakeShared<FJsonObject>();
for (TFieldIterator<FProperty> it(TempFunc); it; ++it) {
FProperty* p = *it;
if (p->PropertyFlags & CPF_Parm) {
TempJsonObject->SetField(p->GetNameCPP(), NULL);
if (TempJsonObject)
//将输入的参数转换为Json对象,方便读取之后,给函数赋值
TSharedRef<TJsonReader<>> JsonReader = TJsonReaderFactory<>::Create(inJsonStr);
TSharedPtr<FJsonObject> inJsonObject;
bool bIsOk = FJsonSerializer::Deserialize(JsonReader, inJsonObject);
if (bIsOk) {
//遍历函数参数,获取参数名称和类型
FString inStr
FRotator inRot
uint8 inByte
int64 inInt64
double infloat
FName inName
int32 inInt
FVector inVec
FTransform inTrans
FColor inColor
FLinearColor
for (TFieldIterator<FProperty> i(TempFunc); i; ++i)
FProperty* TempParam = *i;
FString TempType = TempParam->GetCPPType();
UE_LOG(LogTemp, Warning, TEXT("Current param Type is ==%s"), *TempType);
const FString One_Param = TempParam->GetNameCPP();
if (TempParam->PropertyFlags & CPF_Parm)
if (TempType.Equals("bool")){
bool TempValue = inJsonObject->GetBoolField(One_Param);
TempJsonObject->SetBoolField(One_Param, TempValue);
}else if (TempType.Equals("FString")){
FString TempValue = inJsonObject->GetStringField(One_Param);
TempJsonObject->SetStringField(One_Param, TempValue);
}else if (TempType.Equals("int32")) {
int32 TempValue = inJsonObject->GetNumberField(One_Param);
TempJsonObject->SetNumberField(One_Param, TempValue);
}else if (TempType.Equals("int64")) {
int64 TempValue = inJsonObject->GetNumberField(One_Param);
TempJsonObject->SetNumberField(One_Param, TempValue);
}else if (TempType.Equals("FVector")) {
TSharedPtr<FJsonObject> TempObj = inJsonObject->GetObjectField(One_Param);
TSharedPtr<FJsonObject> VecObject = MakeShared<FJsonObject>();
VecObject->SetNumberField("X", TempObj->GetNumberField("X"));
VecObject->SetNumberField("Y", TempObj->GetNumberField("Y"));
VecObject->SetNumberField("Z", TempObj->GetNumberField("Z"));
TempJsonObject->SetObjectField(One_Param, VecObject);
}else if (TempType.Equals("FRotator")) {
TSharedPtr<FJsonObject> TempObj = inJsonObject->GetObjectField(One_Param);
TSharedPtr<FJsonObject> VecObject = MakeShared<FJsonObject>();
VecObject->SetNumberField("Roll", TempObj->GetNumberField("Roll"));
VecObject->SetNumberField("Pitch",TempObj->GetNumberField("Pitch"));
VecObject->SetNumberField("Yaw", TempObj->GetNumberField("Yaw"));
TempJsonObject->SetObjectField(One_Param, VecObject);
}else if (TempType.Equals("FTransform")){
}else if (TempType.Equals("FColor")) {
}else if (TempType.Equals("FLinearColor")) {
}else if (TempType.Equals("FName")) {
}else if (TempType.Equals("double")) {
}else if (TempType.Equals("uint8")) {
UE_LOG(LogTemp, Warning, TEXT("Current param is ==%s"), *TempParam->GetNameCPP());
else if (TempParam->PropertyFlags & CPF_ReturnParm) {
TArray<uint8> ParamsContainer;
FMemoryWriter Writer(ParamsContainer);
TSharedRef<TJsonWriter<UCS2CHAR>> JsonWriter = TJsonWriter<UCS2CHAR>::Create(&Writer);
FJsonSerializer::Serialize(TempJsonObject.ToSharedRef(), JsonWriter);
FMemoryReader Reader(ParamsContainer);
FJsonStructDeserializerBackend DeserializerBackend(Reader);
FStructDeserializer::Deserialize(Params.GetData(), *TempFunc, DeserializerBackend);
inObj->ProcessEvent(TempFunc, Params.GetData());
}
调用带有引用参数的函数
k2_setActorLocation
{
"NewLocation": {
"X": 100.0,
"Y": 200.0,
"Z": 300.0
"bsweep": false,
"SweepHitResult": "",
"btelepot": false
void UOpenAPICoreSystem::CallFunctionByNameWithParam(UObject* inObj, const FString inName, const FString inJsonStr) {
UFunction* TempFunc = nullptr;
UClass* TempClass = nullptr;
UClass* Super = nullptr;
uint16 TempParamsSize=0;
uint8 TempParamsNum = 0;
TempClass = inObj->GetClass();
TempFunc = TempClass->FindFunctionByName(*inName);
if (!TempFunc)
Super = TempClass->GetSuperClass();
TempFunc=Super->FindFunctionByName(*inName);
if (!TempFunc) {
bool Out = true;
int times = 0;
while (Out && times < 1024) {
Super = Super->GetSuperClass();
TempFunc = Super->FindFunctionByName(*inName);
++times;
if (TempFunc) {
Out = false;
//开始遍历参数写入到Json数据里面
TempParamsSize = TempFunc->ParmsSize;
TempParamsNum = TempFunc->NumParms;
if (TempParamsNum & 0)
CallFunctionByNameNoParam(inObj, inName);
else {
TArray<uint8> Params;
Params.AddUninitialized(TempParamsSize);
TempFunc->InitializeStruct(Params.GetData());
TSharedPtr<FJsonObject> TempJsonObject = MakeShared<FJsonObject>();
if (TempJsonObject)
//将输入的参数转换为Json对象,方便读取之后,给函数赋值
TSharedRef<TJsonReader<>> JsonReader = TJsonReaderFactory<>::Create(inJsonStr);
TSharedPtr<FJsonObject> inJsonObject;
bool bIsOk = FJsonSerializer::Deserialize(JsonReader, inJsonObject);
if (bIsOk) {
//遍历函数参数,获取参数名称和类型
FString inStr
FRotator inRot
uint8 inByte
int64 inInt64
double infloat
FName inName
int32 inInt
FVector inVec
FTransform inTrans
FColor inColor
FLinearColor
FHitResult
for (TFieldIterator<FProperty> i(TempFunc); i; ++i)
FProperty* TempParam = *i;
FString TempType = TempParam->GetCPPType();
UE_LOG(LogTemp, Warning, TEXT("Current param Type is ==%s"), *TempType);
const FString One_Param = TempParam->GetNameCPP();
if (TempParam->PropertyFlags & CPF_Parm)
if (TempType.Equals("bool")){
bool TempValue = inJsonObject->GetBoolField(One_Param);
TempJsonObject->SetBoolField(One_Param, TempValue);
continue;
}else if (TempType.Equals("FString")){
FString TempValue = inJsonObject->GetStringField(One_Param);
TempJsonObject->SetStringField(One_Param, TempValue);
continue;
}else if (TempType.Equals("int32")) {
int32 TempValue = inJsonObject->GetNumberField(One_Param);
TempJsonObject->SetNumberField(One_Param, TempValue);
continue;
}else if (TempType.Equals("int64")) {
int64 TempValue = inJsonObject->GetNumberField(One_Param);
TempJsonObject->SetNumberField(One_Param, TempValue);
continue;
}else if (TempType.Equals("FVector")) {
TSharedPtr<FJsonObject> TempObj = inJsonObject->GetObjectField(One_Param);
TSharedPtr<FJsonObject> VecObject = MakeShared<FJsonObject>();
VecObject->SetNumberField("X", TempObj->GetNumberField("X"));
VecObject->SetNumberField("Y", TempObj->GetNumberField("Y"));
VecObject->SetNumberField("Z", TempObj->GetNumberField("Z"));
TempJsonObject->SetObjectField(One_Param, VecObject);
continue;
}else if (TempType.Equals("FRotator")) {
TSharedPtr<FJsonObject> TempObj = inJsonObject->GetObjectField(One_Param);
TSharedPtr<FJsonObject> VecObject = MakeShared<FJsonObject>();
VecObject->SetNumberField("Roll", TempObj->GetNumberField("Roll"));
VecObject->SetNumberField("Pitch",TempObj->GetNumberField("Pitch"));
VecObject->SetNumberField("Yaw", TempObj->GetNumberField("Yaw"));
TempJsonObject->SetObjectField(One_Param, VecObject);
continue;
}else if (TempType.Equals("FTransform")){
continue;
}else if (TempType.Equals("FColor")) {
continue;
}else if (TempType.Equals("FLinearColor")) {
continue;
}else if (TempType.Equals("FName")) {
continue;
}else if (TempType.Equals("double")) {
continue;
}else if (TempType.Equals("uint8")) {
continue;
else if (TempType.Equals("FHitResult")) {
FHitResult Result;
Result.Init();
TSharedPtr<FJsonObject> ResultJsonObject = MakeShared<FJsonObject>();
FJsonObjectConverter::UStructToJsonObject(FHitResult::StaticStruct(), &Result, ResultJsonObject.ToSharedRef());
TempJsonObject->SetObjectField(One_Param, ResultJsonObject);
continue;
UE_LOG(LogTemp, Warning, TEXT("Current param is ==%s"), *TempParam->GetNameCPP());
else if (TempParam->PropertyFlags & CPF_ReturnParm) {
TArray<uint8> ParamsContainer;
FMemoryWriter Writer(ParamsContainer);
TSharedRef<TJsonWriter<UCS2CHAR>> JsonWriter = TJsonWriter<UCS2CHAR>::Create(&Writer);
FJsonSerializer::Serialize(TempJsonObject.ToSharedRef(), JsonWriter);