大家好,这节我们来讲解一下如何通过UE4C++调用任意的UE4蓝图函数。
其实用法很简单。
首先在蓝图里面建立如下的函数,我这边是在GameMode里面建立的:
![v2-36c87d1d316f6623ba287789526a9033_b.jpg](http://img-01.proxy.5ce.com/view/image?&type=2&guid=f6976e31-9f2e-eb11-8da9-e4434bdf6706&url=https://pic4.zhimg.com/v2-36c87d1d316f6623ba287789526a9033_b.jpg)
然后我们在我们的CPP里面加入如下代码:
下面的代码中用到了元祖,如果不了解UE4元祖可以查看以下文章
人宅:UE4 TTuple 使用技巧zhuanlan.zhihu.com![v2-9d802acb563bb64e59188778b4a3d4ec_180x120.jpg](http://img-01.proxy.5ce.com/view/image?&type=2&guid=f6976e31-9f2e-eb11-8da9-e4434bdf6706&url=https://pic1.zhimg.com/v2-9d802acb563bb64e59188778b4a3d4ec_180x120.jpg)
粘贴如下代码:
template<typename... TReturns, typename... TArgs>
void ImplementFunction(UClass* OuterClass, UFunction* Function, TTuple<TReturns...>& OutParams, TArgs&&... Args)
{
void* FuncPramsStructAddr = (uint8*)FMemory_Alloca(Function->ParmsSize);
uint8* OutPramsByte = (uint8*)&OutParams;
TTuple<TArgs...> InParams(Forward<TArgs>(Args)...);
uint8* InPramsByte = (uint8*)&InParams;
if (Function->HasAnyFunctionFlags(FUNC_Native))
{
FFrame Frame(nullptr, Function, &FuncPramsStructAddr, nullptr, Function->Children);
uint8* ReturnParams = nullptr;
//执行无字节码的函数
//注册虚拟机参数列表
FOutParmRec** LastOut = &Frame.OutParms;
for (TFieldIterator<UProperty> ParmIt(Function); ParmIt; ++ParmIt)
{
UProperty *Property = *ParmIt;
if (Property->PropertyFlags & CPF_OutParm)
{
if (Property->PropertyFlags & CPF_ReturnParm)
{
ReturnParams = OutPramsByte;
OutPramsByte += Property->GetSize();
}
else
{
for (;;)
{
if (*LastOut == nullptr)
{
(*LastOut) = (FOutParmRec*)FMemory_Alloca(sizeof(FOutParmRec));
(*LastOut)->PropAddr = OutPramsByte;
(*LastOut)->Property = Property;
(*LastOut)->NextOutParm = nullptr;
LastOut = &(*LastOut)->NextOutParm;
OutPramsByte += Property->GetSize();
break;
}
else
{
LastOut = &(*LastOut)->NextOutParm;
}
}
}
}
else if (Property->PropertyFlags & CPF_Parm)
{
for (;;)
{
if (*LastOut == nullptr)
{
(*LastOut) = (FOutParmRec*)FMemory_Alloca(sizeof(FOutParmRec));
(*LastOut)->PropAddr = (uint8 *)(Property->ContainerPtrToValuePtr<void*>(&FuncPramsStructAddr));
if (UObjectProperty *ObjectPropert = Cast<UObjectProperty>(Property))
{
ObjectPropert->SetObjectPropertyValue(const_cast<uint8*>((*LastOut)->PropAddr), *(UObject**)InPramsByte);
}
else
{
FMemory::Memcpy((*LastOut)->PropAddr, InPramsByte, Property->GetSize());
}
(*LastOut)->Property = Property;
(*LastOut)->NextOutParm = nullptr;
LastOut = &(*LastOut)->NextOutParm;
InPramsByte += Property->GetSize();
break;
}
else
{
LastOut = &(*LastOut)->NextOutParm;
}
}
}
}
Function->Invoke(OuterClass, Frame, ReturnParams);
return;
}
//必须要有字节码,也就是它必须是蓝图函数,或者是BlueprintNativeEvent or BlueprintImplementableEvent 在蓝图编译的函数
//遍历函数
for (TFieldIterator<UProperty> i(Function); i; ++i)
{
UProperty* Property = *i;
void* PropAddr = Property->ContainerPtrToValuePtr<void*>(FuncPramsStructAddr);
if (Property->PropertyFlags & CPF_OutParm)
{
FMemory::Memcpy(PropAddr, OutPramsByte, Property->GetSize());
OutPramsByte += Property->GetSize();
}
else if (Property->PropertyFlags & CPF_Parm)
{
FMemory::Memcpy(PropAddr, InPramsByte, Property->GetSize());
InPramsByte += Property->GetSize();
}
}
//必须要有字节码
OuterClass->ProcessEvent(Function, FuncPramsStructAddr);
OutPramsByte = (uint8*)&OutParams;
for (TFieldIterator<UProperty> i(Function); i; ++i)
{
UProperty* Property = *i;
if (Property->PropertyFlags & CPF_OutParm)
{
void* PropAddr = Property->ContainerPtrToValuePtr<void*>(FuncPramsStructAddr);
FMemory::Memcpy(OutPramsByte, PropAddr, Property->GetSize());
OutPramsByte += Property->GetSize();
}
}
}
//TReturns 包含引用 和返回值 一般返回值放在后面
//TArgs 传递参数 包含指针和变量
template<typename... TReturns, typename... TArgs>
void Call(FName FunctionName, TTuple<TReturns...>& OutParams, TArgs&&... Args)
{
//可以找到带有字节码的函数
UFunction* Function = (UFunction*)StaticFindObjectFast(UFunction::StaticClass(), nullptr, FunctionName, false, true, RF_Transient);
if (!Function)
{
//寻找无字节码的函数
Function = FindObject<UFunction>(ANY_PACKAGE, *FunctionName.ToString());
}
ImplementFunction<TReturns...>(Function->GetOuterUClass(), Function, OutParams, Forward<TArgs>(Args)...);
}
如何使用呢?
其实很简单,如下,可以在BenginPlay里面直接调用即可
TTuple<TArray<AActor*>, UObject*> ReturnNative;
Call(TEXT("TestFuncCX"), ReturnNative, 13, 7.f, this, 6.7f);
TTuple<int32, FName, FTransform, bool> ReturnBlueprint;
Call(TEXT("EEERRR"),ReturnBlueprint, FRotator(3.f, 4.f, 5.f), 245, FString("-Hello"), this);
这样我们就实现了这个效果。
我们如何实现蓝图调用cpp呢?
因为cpp拥有模板,但是蓝图却没有这么高级的操作,如果想任意访问cpp,目前能想到的是通过定义两个函数的协议,然后通过字符串来实现,通过UE4 AddPin 来实现蓝图调用任意UE4函数。
目前官方给的方法是通过简单反射来实现比如:
BlueprintCallable
BlueprintImplementableEvent
BlueprintNativeEvent
BlueprintPure
这些方法并不是严格意义上的任意函数调用,我个人认为可以通过蓝图 AddPin 来实现,通过定义两个函数协议来操作,但是总是不完美。
如果实在看不懂我上面写的模板代码调用,可以看看以下教程,已经将该内容视频化。
视频去哪了呢?_哔哩哔哩 (゜-゜)つロ 干杯~-bilibiliwww.bilibili.com当前教程选自UE4编辑器开发进阶:
UE4编辑器开发进阶教程:反射与蓝图www.aboutcg.org![v2-e716447818d1bfdfb82d769cd19e66c8_180x120.jpg](http://img-01.proxy.5ce.com/view/image?&type=2&guid=f6976e31-9f2e-eb11-8da9-e4434bdf6706&url=https://pic1.zhimg.com/v2-e716447818d1bfdfb82d769cd19e66c8_180x120.jpg)
其他教程
人宅:UE4精品课程zhuanlan.zhihu.com![v2-9b03c45c6776aaf9f736f2e5cf46d53e_180x120.jpg](http://img-01.proxy.5ce.com/view/image?&type=2&guid=f6976e31-9f2e-eb11-8da9-e4434bdf6706&url=https://pic3.zhimg.com/v2-9b03c45c6776aaf9f736f2e5cf46d53e_180x120.jpg)