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调用蓝图函数
(此操作可以让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的前提步骤
11、大概使用样子
调用call蓝图函数容易崩