en Tutoriales, UE5

Usando Python en Unreal Engine

En un tutorial anterior vimos como utilizar un interprete de Python en una aplicación C++, ahora podemos utilizarlo como base para utilizar un interprete de Python en nuestros proyectos de Unreal Engine. Con ello podremos utilizar código Python en nuestros juegos

Empezaremos con un proyecto C++ vacio de Unreal Engine. En el definiremos una clase para gestionar el intérprete de Python

ue5_empty_project_cpp

Ya dentro del proyecto podemos crear una clase de C++ desde Tools > New C++ Class

ue5_newcpp_class

Creamos la clase para gestionar el intérprete

ue5_project_python_tree

Para este ejemplo vamos a definir un nodo de Unreal Engine que calculará el coseno de un angulo utilizando una función de Python

PythonInterpreter.h

#pragma once

#include "CoreMinimal.h"
#include "UObject/ObjectMacros.h"
#include "UObject/NoExportTypes.h"

#include "PythonInterpreter.generated.h"

UCLASS(Blueprintable, BlueprintType)
class UE5_T_PYTHON_API UPythonInterpreter : public UObject
{
	GENERATED_BODY()
public:

	UFUNCTION(BlueprintCallable, Category = "PythonInterpreter|math|cos")
		void CalculateCosineNode(const float& input, float& output);
		
	UPythonInterpreter();
	~UPythonInterpreter();

private:
	float calculate_cosine(const float& input);

};

PythonInterpreter.cpp

#include "PythonInterpreter.h"

#pragma push_macro("check")
#undef check
#pragma warning (push)
#pragma warning (disable : 4191)
#pragma warning (disable : 4591)
#pragma warning (disable : 4686)
#include <pybind11/embed.h>
#pragma warning (pop)
#pragma pop_macro("check")

namespace py = pybind11;

UPythonInterpreter::UPythonInterpreter()
{
}

UPythonInterpreter::~UPythonInterpreter()
{
}

void UPythonInterpreter::CalculateCosineNode(const float& input, float& output)
{
	output = calculate_cosine(input);
}

float UPythonInterpreter::calculate_cosine(const float& input)
{
	py::scoped_interpreter guard{};
	auto math_module = py::module_::import("math");
	py::object result = math_module.attr("cos")(input);
	return py::cast<float>(result);
}

Estamos utilizando pybind11 para envolver las llamadas a funciones de Python con llamadas de c++, tendremos que incluir ciertas claúsulas alrededor de sus includes para poder utilizarlo en UE5 y evitar errores durante la la compilación.

En la función calculate_cosine creamos una instancia del intérprete antes de importar su módulo math y llamar a su función cos. Pero cada vez que llamamos a esta función un intérprete de Python es creado y el módulo math es también cargado.

Para evitar este gasto de recursos con cada llamada podemos mover la inicialización del intérprete y linkar su tiempo de vida a tiempor de vida del objeto de la clase. Después solo necesitamos tener un objeto de la clase que va a contener el intérprete y utilizar este objeto para llamar nodos que van a ejecutar código Python.

Tendremos que incluir las librerías de terceros al fichero Build.cs de nuestro proyecto con PrivateIncludePaths

Build.cs

using System.IO;
using UnrealBuildTool;

public class UE5_T_Python : ModuleRules
{
    public UE5_T_Python(ReadOnlyTargetRules Target) : base(Target)
    {
        PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
        bUseRTTI = true;
        bEnableExceptions = true;

        PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" });

        PrivateDependencyModuleNames.AddRange(new string[] {  });

        PrivateIncludePaths.Add(Path.Combine(ModuleDirectory, "../UE5_T_Python/thirdparty/pybind11/include"));

        string python_path = @"D:\Programs\Python312";
        PrivateIncludePaths.Add(Path.Combine(python_path, "include"));
        PublicAdditionalLibraries.Add(Path.Combine(python_path, "libs", "python312.lib"));
    }
}

Estamos usando linkage estático con la librería de Python del sistema. Esta librería puede incluirse junto a la release y utilizar una ruta relativa al .lib, también tendremos que añadir los includes a las cabeceras de Python

Otra opción sería utilizar linkage dinámico y utilizar la librería .dll junto a una llamada de PublicDelayLoadDLLs en vez de PublicAdditionalLibraries

Ahora en el editor de Unreal podemos crear el Blueprint que recubrirá a nuestra clase PythonInterpreter para utilizarla como variable en the level

ue5_editor_class

Ahora podemos crear una variable en el blueprint del nivel

ue5_variables

Construiremos la variable y podremos usar su nodo CalculateCosineNode para calcular el coseno utilizando el módulo math de python

ue5_level_blueprint

En la consola vemos el resultado de la ejecución del nodo

ue5_terminal

Conclusion

Con este tutorial hemos visto una manera sencilla de incluir un interprete de Python en nuestros proyectos de Unreal Engine. Puedo resultarnos muy útil si tenemos módulos de Python que no queremos portar a c++ pero aún así necesitamos utilizarlos. Ahora podemos ejecutar código Python en nuestros juegos de Unreal Engine

Tutorial files

Ayudanos con este blog!

El último año he estado dedicando cada vez más tiempo a la creación de tutoriales, en su mayoria sobre desarrollo de videojuegos. Si crees que estos posts te han ayudado de alguna manera o incluso inspirado, por favor considera ayudarnos a mantener este blog con alguna de estas opciones. Gracias por hacerlo posible!

Escribe un comentario

Comentario