function函数的用法_通过UE4C++反射调用UE4蓝图函数

大家好,这节我们来讲解一下如何通过UE4C++调用任意的UE4蓝图函数。

其实用法很简单。

首先在蓝图里面建立如下的函数,我这边是在GameMode里面建立的:

v2-36c87d1d316f6623ba287789526a9033_b.jpg

然后我们在我们的CPP里面加入如下代码:

下面的代码中用到了元祖,如果不了解UE4元祖可以查看以下文章

人宅:UE4 TTuple 使用技巧​zhuanlan.zhihu.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 来实现,通过定义两个函数协议来操作,但是总是不完美。

如果实在看不懂我上面写的模板代码调用,可以看看以下教程,已经将该内容视频化。

视频去哪了呢?_哔哩哔哩 (゜-゜)つロ 干杯~-bilibili​www.bilibili.com

当前教程选自UE4编辑器开发进阶:

UE4编辑器开发进阶教程:反射与蓝图​www.aboutcg.org
v2-e716447818d1bfdfb82d769cd19e66c8_180x120.jpg

其他教程

人宅:UE4精品课程​zhuanlan.zhihu.com
v2-9b03c45c6776aaf9f736f2e5cf46d53e_180x120.jpg
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值