UE4 嵌入Python

12 篇文章 0 订阅

1、参考C++嵌入Python

2、UE4的新建插件HuGanTool,找到相应的Build.cs文件,手动添加第三方库

private string ModulePath
	{
		get
		{
			return ModuleDirectory;
		}
	}

	private string ThirdPartyPath
	{
		get
		{
			return Path.GetFullPath(Path.Combine(ModulePath, "../../ThirdParty"));
		}
	}

//以下部分在public HuGanTool(ReadOnlyTargetRules Target) : base(Target)的里面
PublicIncludePaths.Add(Path.Combine(ThirdPartyPath, "include"));
PublicIncludePaths.Add("C:\\Users\\26593\\AppData\\Local\\Programs\\Python\\Python39\\include");
PublicAdditionalLibraries.Add(Path.Combine(ThirdPartyPath, "lib", "python39.lib"));

3、新建PythonActor类,并添加    #include "Python.h"

4、创建相关函数声明,可以参考b站人宅的ue4调用蓝图函数

UE4反射调用蓝图函数教程

(此操作可以让python调用到ue4蓝图的函数)

template<typename... TReturns, typename... TArgs>
void Call(FName FunctionName, TTuple<TReturns...>& OutParams, TArgs&&... Args);

PyObject* UE4_call(PyObject* self, PyObject* args);


PyObject* PyInit_UE4(void);

5、在APythonActor内创建几个函数声明

UFUNCTION(BlueprintCallable, meta = (AdvancedDisplay = "Initialize"), Category = "Python")
bool Initialize();

UFUNCTION(BlueprintPure, meta = (AdvancedDisplay = "IsInitialized"), Category = "Python")
bool IsInitialized();

UFUNCTION(BlueprintCallable, meta = (AdvancedDisplay = "Finalize"), Category = "Python")
void Finalize();

UFUNCTION(BlueprintCallable, meta = (AdvancedDisplay = "RunSimpleString"), Category = "Python")
void RunSimpleString(const FString& Script);

UFUNCTION(BlueprintCallable, meta = (AdvancedDisplay = "RunFileFunction"), Category = "Python")
FString RunFileFunction(const FString& FilePath, const FString& FunName, const FString& FunValue);

分别是初始化、是否初始化成功、结束、运行一段python脚本、根据py文件运行函数

6、在cpp中写入

// 初始化Python环境
bool APythonActor::Initialize()
{
	PyImport_AppendInittab("UE4", &PyInit_UE4);
	Py_Initialize();
	return APythonActor::IsInitialized();
}

// 判断初始化是否成功
bool APythonActor::IsInitialized()
{
    if (!Py_IsInitialized())
    {
        return false;
    }
	return true;
}

// 关闭 Python 脚本解释器 释放内存
void APythonActor::Finalize()
{
	Py_Finalize();
}

// 运行一段字符串py代码
void APythonActor::RunSimpleString(const FString& Script)
{
	const TCHAR* c = *Script;
	PyRun_SimpleString(TCHAR_TO_UTF8(c));
}

// 根据py文件运行指定函数
FString APythonActor::RunFileFunction(const FString& FilePath, const FString& FunName, const FString& FunValue)
{
    PyObject* pModule = NULL;
    PyObject* pFunc = NULL;
    PyObject* pArg = NULL;

    const TCHAR * cFilePath = *FilePath;
	// 导入模块
	pModule = PyImport_ImportModule(TCHAR_TO_UTF8(cFilePath));
	if (!pModule) // 检查模块是否导入成功
	{
		return FString();
	}
    else
    {
        const TCHAR* cFunName = *FunName;
        // 导入函数
        pFunc = PyObject_GetAttrString(pModule, TCHAR_TO_UTF8(cFunName)); //python 脚本里的函数名
        if (!pFunc) // 检查函数是否导入成功
        {
            return FString();
        }
        const TCHAR* cFunValue = *FunValue;
        //C++类型转换为python类型
        PyObject* pParams = Py_BuildValue("(s)", TCHAR_TO_UTF8(cFunValue));

        pArg = PyObject_CallObject(pFunc, pParams); // 调用函数
        //获取结果,python类型转换为C++类型
        char* result;
        PyArg_Parse(pArg, "s", &result);

        FString fresult(UTF8_TO_TCHAR(result));
        return fresult;
    }
}

7、cpp中写入上面调用蓝图的函数call

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))
	{
		FField* ffield = FField::CreateFromUField(Function->Children);
		FFrame Frame(nullptr, Function, &FuncPramsStructAddr, nullptr, ffield);
		uint8* ReturnParams = nullptr;

		//执行无字节码的函数
		//注册虚拟机参数列表
		FOutParmRec** LastOut = &Frame.OutParms;

		for (TFieldIterator<FProperty> ParmIt(Function); ParmIt; ++ParmIt)
		{
			FProperty* 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 (FObjectProperty* ObjectPropert = Cast<FObjectProperty>(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<FProperty> i(Function); i; ++i)
	{
		FProperty* 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<FProperty> i(Function); i; ++i)
	{
		FProperty* 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());
		if (!Function)
		{
			return;
		}
	}

	ImplementFunction<TReturns...>(Function->GetOuterUClass(), Function, OutParams, Forward<TArgs>(Args)...);
}

8、把第7步创建的函数暴露给python当做一个ue4模块来调用

PyObject* UE4_call(PyObject* self, PyObject* args) {
	/* 在返回之前不需要Py_DECREF的共享引用。 */
	PyObject* obj = NULL;

	char* what;
	PyArg_ParseTuple(args, "s", &what);
	/* 函数实现从这里开始 */
	TTuple<int32, FName, FTransform, bool> ReturnBlueprint;
	Call(*FString(UTF8_TO_TCHAR(what)), ReturnBlueprint, FString(UTF8_TO_TCHAR(what)));

	Py_RETURN_NONE;
}

//UE4_functions添加到UE4的函数列表。
static PyMethodDef UE4_functions[] = {
	{ "call", (PyCFunction)UE4_call, METH_VARARGS | METH_KEYWORDS },
	{ NULL, NULL, 0, NULL } /* 标记数组结束 */
};

static PyModuleDef UE4_def = {
	PyModuleDef_HEAD_INIT, "UE4", NULL, -1, UE4_functions,
	NULL, NULL, NULL, NULL
};

PyObject* PyInit_UE4(void)
{
	return PyModule_Create(&UE4_def);
}

9、注意要点

如果C++想要在运行时候暴露库给python的话需要在初始化解释器之前导入库

// 初始化Python环境
bool APythonActor::Initialize()
{
	PyImport_AppendInittab("UE4", &PyInit_UE4);//这行是关键
	Py_Initialize();
	return APythonActor::IsInitialized();
}

10、我录的一个视频,简单说明了一下嵌入python的前提步骤

UE4嵌入python步骤

11、大概使用样子

 调用call蓝图函数容易崩

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值