Embed Python in Unreal Engine 4
This is a plugin allowing the mapping of Actors, Pawns and Components to python classes (and a lot more). It is not meant as a way to avoid blueprints or c++ but as a good companion to them (albeit reducing the amount of c++ required for coding a game could be an interesting thing ;)
Another funny feature is that you can change your python code even after the project has been packaged. You can potentially build a completely new game from an already packaged one.
Mid term goal is allowing scripting of the editor too.
The plugin embeds the python3 library and automatically adds an actor class (PyActor), a pawn class (PyPawn) and a component class (PythonComponent). They are available in your editor once you load the plugin.
All of the exposed engine features are under the 'unreal_engine' virtual module (it is completely coded in c into the plugin, so do not expect to run 'import unreal_engine' from a standard python shell)
The installation is pretty long (and boring) as you do not want the final users of the product to be required to install python, so we need to use the 'embedded python distribution' (available for windows in the official python site). Unfortunately the embedded distribution does not contain the python development headers so we need the official system-wide installation too.
- install Unreal Engine 4.12 and Python 3.5 as a system user
- run the unreal editor and create a new blank c++ project (NOT a blueprint one, otherwise visual studio will not be initialized)
- once the project is setup, close both unreal and visual studio
- move to the project directory (you can right click the project from the epic launcher and choose 'show in explorer')
- create a 'Plugins' directory into the project directory
- move into the just created Plugins directory and clone the repository:
git clone https://github.com/20tab/UnrealEnginePython
- from the explorer right click on the project main file and choose 'generate visual studio project files'
- open again visual studio, you should now see Plugins/UnrealEnginePython in your solution explorer
- before running the plugin build process you need to copy development headers and libs in the plugin directory (Plugins/UnrealEnginePython).
- create the directory Plugins/UnrealEnginePython/python35 (this is where the build script expects to find headers and static libs)
- copy "C:/Program Files/Python35/include" and "C:/Program Files/Python35/libs" into Plugins/UnrealEnginePython/python35
- run the compilation from visual studio
- once the compilation ends, copy the python35.dll (from "C:/Program Files/Python35" or from the embeded distribution) into the Binaries/Win64 directory of your project (you will find the plugin dll too in this directory)
- now you can re-run the unreal engine editor
If all goes well, open the output log and search for the string "Python". You should see the Python VM initialization message. It means your editor has now full python support.
On the Mac the installation is easier, as the final user is currently forced to install python on its system (there are obviously dozens of workarounds but at this stage of the project we prefer focusing on the api).
- install the latest official python distribution from python.org (the installation will end in the "/Library/Frameworks/Python.framework/Versions/3.5" directory).
- create a new unreal engine blank c++ project (NOT a blueprint one, otherwise XCode will not be initialized)
- create a Plugins directory in the project directory
- move to the Projects directory and clone the plugin repository
git clone https://github.com/20tab/UnrealEnginePython
- restart the editor and a popup should appear asking your for confirmation of the build of the plugin.
- Once the plugin is built, go to the output log console and filter for 'Python'. You should see the Python VM banner.
To upgrade to the latest development version of UnrealEnginePython:
- move to the Plugins directory in the project directory and use git pull
git pull
- move to UnrealEnginePython/Binaries/Mac from the Plugin directory
- remove the plugin library UE4Editor-UnrealEnginePython.dylib to warn UnrealEngine to recompile the plugin
rm UE4Editor-UnrealEnginePython.dylib
- restart the editor and a popup should appear asking your for confirmation of the build of the plugin.
- Once the plugin is built, go to the output log console and filter for 'Python'. You should see the Python VM banner.
Currently only Windows and MacOSX are supported, Linux support should be available soon. We are investigating Android support too via the kivy project.
We are going to create a new Actor based on python (instead of C++ or blueprints)
In the content browser click on 'add new' and choose 'blueprint class'
In the classes menu choose 'PyActor':
You now have a new asset, give it a meaningful name, and double click on it to start configuring it in the blueprint editor
On the right (in the 'Details' tab) you will find the Python section.
For now only 'Python Module' and 'Python Class' are meaningful.
Go to the Content directory of your project and create a directory named 'Scripts'. This is where all of your python modules will reside. With your favourite text editor create a new python module (like funnygameclasses.py), and define a new class into it:
import unreal_engine as ue
ue.log('Hello i am a Python module')
class Hero:
# this is called on game start
def begin_play(self):
ue.log('Begin Play on Hero class')
# this is called at every 'tick'
def tick(self, delta_time):
# get current location
x, y, z = self.uobject.get_actor_location()
# increase Z honouring delta_time
z += 100 * delta_time
# set new location
self.uobject.set_actor_location(x, y, z)
Now, go back to the blueprint editor and set 'funnygameclasses' in the 'Python Module' field, and 'Hero' in 'Python Class'
As you can see the actor will simply move over the z axis, but we need to give it some kind of visual representation to have a feedback in the scene. In the blueprint editor click on 'add component' and add some shape (a spehere, or a cube, or whatever you want). Save and Compile your blueprint.
Now you can drag the bluprint from the content browser to the scene and just click 'Play'.
You should see your actor moving along the 'z' axis at a speed of 1 meter per second
By default a 'begin_play' and a 'tick' method are expected (NOTE: currently you get an exception if you do not define them, this should be addressed soon). The following methods are defined for PyActor (they are triggered on specific events):
def on_actor_begin_overlap(self, other_actor):
pass
def on_actor_end_overlap(self, other_actor):
pass
# *args is currently under investigation
def on_actor_hit(self, other, *args):
pass
# maybe touched_actor is useless ?
def on_actor_clicked(self, touched_actor, button_pressed)
pass
more events are going to be added
To allow seamless Python integration, each UObject of the engine is automatically mapped to a special Python Object (ue_PyUObject).
Whenever you want to access a UObject from python, you effectively get a reference to a ue_PyUObject exposing (via its methods) the features of the UObject (properties, functions, ....)
This special python object is cached into the UObject (via the __PyObject property) so you do not need to recreate it every time (and more important you can hold state informations into this python object)
To be more clear, a call to:
text_render_component = unreal_engine.find_class('TextRenderComponent')
will internally search for the 'TextRenderComponent' class (via unreal c++ reflection) and when found will check if it exposes a '__PyObject' property. If available, it will return its value to the Python VM, otherwise a new one will be created and mapped to it.
From the previous example the 'text_render_component' maintains a mapping to the UObject (well a UClass in this example).
Pay attention: the python class you map to the PyActor class, is not a ue_PyUObject (it is a classic python class) but holds a reference (via the 'uobject' field) to a ue_PyUObject mapped to the AActor: PyActor exposes 'uobject' field mapped to the c++ UObject.
In the following lines, whenever you find a reference to 'uobject' it is meant as a ue_PyUObject object.
This works in the same way as the PyActor class, but it is, well, a component. You can attach it (search for the 'Python' component) to any actor.
Remember that for components, the self.uobject field point to the component itself, not the actor.
To access the actor you can use
actor = self.uobject.get_class()
This works like PyActor, but this time you generate a new Pawn class (that you can posses with a controller)
You can attach a PythonComponent to a GameMode blueprint. Its tick method will be executed during the whole game lifetime.
This is a generic module, exposing functions not related to a particular 'World' (see below).
You can use these functions from an actor, a pawn, a component or directly from a module.
Just remember to import it:
import unreal_engine
or (as an example)
import unreal_engine as ue
uclass = unreal_engine.find_class('name')
This function searches for a class in the engine with the specified name and returns its 'uobject' (a c++ UClass object internally). You can use this uobject to search for objects of that type or to spawn new actors (and lot of more things)
uclass = unreal_engine.find_object('name')
This is a more generic (and slower) variant of find_class that searches for every UObject. You can use it (for example) for getting references to assets (like materials, meshes...)
unreal_engine.log('message')
log a string into the editor output log (under the LogPython category/class)
unreal_engine.log_warning('message')
log a warning string (yellow) into the editor output log (under the LogPython category/class)
unreal_engine.log_error('message')
log an error string (red) into the editor output log (under the LogPython category/class)
unreal_engine.add_on_screen_debug_message(key, timeout, 'message')
low-level equivalent of blueprint 'print string' function. It disappear after 'timeout' seconds and can get a numeric key (use -1 for disabling keys feature)
TODO: support for colors
unreal_engine.print_string('message')
python equivalent of the blueprint 'print string' function. It disappears after 2 seconds and it is wrote in cyan color.
unreal_engine.vector_add_vector(x, y, z[, x1, y1, z1, ...])
optimized vector math function, allows adding 3 dimensions vectors exposed as 3 float values
unreal_engine.vector_add_float(x, y, z[, d1, d2, ...])
optimized vector math function, allows adding a 3 dimensions vector with one or more floats
unreal_engine.vector_mul_vector(x, y, z[, x1, y1, z1, ...])
optimized vector math function, allows multiplyging 3 dimensions vectors exposed as 3 float values
unreal_engine.vector_mul_float(x, y, z[, d1, d2, ...])
optimized vector math function, allows multiplying a 3 dimensions vector with one or more floats
editor = unreal_engine.get_editor_world()
(available only into the editor) it allows to get a reference to the editor world. This will allow in the near future to generate UObjects directly in the editor (for automating tasks or scripting the editor itself)
Every uobject is mapped to a world (UWorld in c++). Generally when you play on a Level your objects all live in the same world, but at the same time there could be multiple worlds (for example while testing in the editor there is a world for the editor and one for the simulation)
While it is pretty rare to reference other worlds, you may need to compare the world of two uobject's (for example you may have a reference in your python module to a uobject of a hidden world and you want to check if you need to use it).
The uobject.get_world() function returns a uobject representing the world (the C++ UWorld class)
Each uobject represent a UObject class of the Engine. This C++ class is basically the root of all the other classes (Actors, Pawns, components, properties ...). Thanks to Unreal Engine reflection system we do not need to implement a python class for each unreal engine class, but for performance reason we expose the most common methods. The uobject system checks for the type of the mapped C++ UObject and will call the method only if it is safe to call it.
Sometime methods are implemented for automatically getting the right object. As an example get_actor_location() when called over a component will automatically retrieve the related actor and will call C++ AActor::GetActorLocation() method over it.
When this automagic approach is too risky, the method will check for the uobject type and will raise an exception in the case of inconsistencies.
Remember, there is no need to implement every single engine class method, the reflection system is powerful enough to be governed only via properties and function calls (check the uobject call() method)
Most-used methods are implemented directly as uobject methods for performance reasons.
You can get the the list of uobject api methods here: https://github.com/20tab/UnrealEnginePython/blob/master/uobject_API.md
When in the editor, you can change the code of your modules without restarting the project. The editor will reload the module every time a PyActor, PyPawn or PyComponent is instantiated. This is obviously not the best approach. In the future we would like to implement timestamp monitoring on the file to reload only when needed.
The plugin does not expose FVector, FRotator or FQuat. You have optimized shortcuts in the unreal_engine module, but you can use any python math library if you want (included numpy)
import unreal_engine
class ActorGoingUp:
def begin_play(self):
# 1 meter by second
self.speed = 100
def tick(self, delta_time):
# get the up vector
up = self.uobject.get_up_vector()
# get current position
position = self.uobject.get_actor_location()
# build a direction vector based on speed
up_amount = unreal_engine.vector_mul_float(*up, self.speed, delta_time)
# sum the direction to the position
new_position = unreal_engine.vector_add_vector(position, up_amount)
# set the new position
self.uobject.set_actor_location(*new_position)
The only exposed navigation-related method is 'simple_move_to_location'. It expects a Pawn with a movement component (like a Character)
class MoveToTargetComponent:
def begin_play(self):
# get a 'target point' reference from a pawn public property
target = self.uobject.get_owner().get_property('target')
self.uobject.get_owner().simple_move_to_location(*target.get_actor_location())
def tick(self, delta_time):
pass
Another example for diablo-like movements (click to move, add this component to a character)
class Walker:
def begin_play(self):
self.uobject.show_mouse_cursor()
def tick(self, delta_time):
if not self.uobject.is_input_key_down('LeftMouseButton'):
return
clicked_actor, x, y, z, nx, ny, nz = self.uobject.get_hit_result_under_cursor(ue.COLLISION_CHANNEL_VISIBILITY)
if not clicked_actor:
return
self.uobject.simple_move_to_location(x, y, z)
The 'set_simulate_physics' method is exposed for enabling physic on PrimitiveComponent.
Remember that you cannot enable physics withotu setting the collision presetes accordingly:
# PyActor with a staticmeshcomponent (a sphere)
# when overlapped it became a physic object
class Ball:
def begin_play(self):
self.sphere = self.uobject.get_actor_component_by_type(ue.find_class('StaticMeshComponent'))
def tick(self, delta_time):
pass
def on_actor_begin_overlap(self, other_actor):
# change collision profile
self.sphere.call('SetCollisionProfileName BlockAll')
# enable physics
self.sphere.set_simulate_physics()
# once the object became a physics one, hit event will start arriving
def on_actor_hit(self, other, *args):
ue.print_string('HIT with ' + other.get_name())
TODO: expose more physics functions, expecially the one for applying forces
Fracturing is one of the best features you get for free in unreal engine.
You can apply damage to destructible objects directly from python (more kind of damages will be added soon)
class DestroyMeComponent:
def begin_play(self):
# get a reference to a destructible component in the actor
self.destructible = self.uobject.get_actor_component_by_type(ue.find_class('DestructibleComponent'))
def tick(self, delta_time):
pass
def explode(self):
# damage amount
amount = 1000
strength = 20000
position = self.uobject.get_owner().get_actor_location()
up = self.uobject.get_owner().get_actor_up()
self.destructible.destructible_apply_damage(amount, strength, *position, *up)
you can now call the 'explode' method via blueprints using the 'Call Python Component Method' node
Another approach (way more easier) would be adding a RadialForceComponent and fire it when you want to destroy something:
# get a reference to the RadialForceComponent
self.radial_force = self.uobject.get_owner().get_actor_component_by_type(ue.find_class('RadialForceComponent'))
# fire it !
self.radial_force.call('FireImpulse')
Splines are another amazing UE4 feature.
The following component shows how to move another actor over a spline path:
class Spline:
def begin_play(self):
# get a reference to a spline component
self.spline = self.uobject.get_owner().get_actor_component_by_type(ue.find_class('SplineComponent'))
# find the length of the spline
self.max_distance = self.spline.get_spline_length()
self.distance = 0.0
# get a reference to the actor to move (as a blueprint property)
self.actor_to_move = self.uobject.get_owner().get_property('ObjectToMove')
def tick(self, delta_time):
if self.distance >= self.max_distance:
return
# find the next point on the spline
next_point = self.spline.get_world_location_at_distance_along_spline(self.distance)
self.actor_to_move.set_actor_location(*next_point)
self.distance += 100 * delta_time
You can call blueprints functions (or custom events) via the .call() and .call_function() methods:
your_funny_blueprint_object.call('AFunctionOrACustomEvent with_a_arg')
Whenever you need to reference external object, avoid using find_object() and similar. Instead add a public variable in your blueprint pointing to the specific object. You can then reference this object easily getting the property value:
the_other_object = self.uobject.get_property('target')
the_other_object.set_actor_location(0, 0, 0)
.call_function() is more advanced, as it allows for return values and python args:
# an example of moving an object z with curves:
class Curver:
def begin_play(self):
self.curve = self.uobject.get_owner().get_property('curve')
self.accumulator = 0.0
def tick(self, delta_time):
x, y, z = self.uobject.get_actor_location()
z = self.curve.call_function('GetFloatValue', self.accumulator) * 100
self.uobject.set_actor_location(x, y, z)
self.accumulator += delta_time
Currently there is no support for defining custom events from python. The best thing to do is defining them from a blueprint and mapping each event to a python method:
The uobject.play_sound_at_location(sound, x, y, z[, volume_multiplier, pitch_multiplier, start_time]) api method is exposed:
# get a reference to asound
sound = ue.find_object('my_sound')
# play the sound at position 0,0,0
self.uobject.play_sound_at_location(sound, 0, 0, 0)
If you prefer to work with AudioComponent:
class Sounder:
def begin_play(self):
# find the AudioComponent of this actor
self.audio = self.uobject.get_component_by_type('AudioComponent')
self.audio.call('Stop')
def tick(self, delta_time):
# start the sound when pressing 'A'
if self.uobject.is_input_key_down('A'):
self.audio.call('Play')
When you package your projects, remember to include the libpython (dll or dylib) in the binaries folder and the Scripts directory.
If you do not want to distribute pythn sources, you can include only the __pycache__
directory with the bytecode.
Do not forget to include python third party modules (if you use any of them in your project)
This is a PyActor destroying itself whenever another actor overlap it. Remember to add a mesh component to it (like a sphere) and set its collision behaviour as 'OverlapAll'. This could be tested with the third person official template.
class Ball:
def begin_play(self):
ue.print_string('Hello')
def tick(self, delta_time):
pass
def on_actor_begin_overlap(self, other_actor):
ue.print_string('Collided with ' + other_actor.get_name())
self.uobject.actor_destroy()
Now we create (at runtime !!!) a whole new PyActor:
class SuperHero:
def begin_play(self):
# spawn a new PyActor
new_actor = self.uobject.actor_spawn(ue.find_class('PyActor'), 0, 0, 0, 0, 0, 90)
# add a sphere component as the root one
static_mesh = new_actor.add_actor_root_component(ue.find_class('StaticMeshComponent'), 'SphereMesh')
# set the mesh as the Sphere asset
static_mesh.call('SetStaticMesh /Engine/EngineMeshes/Sphere.Sphere')
# set the python module
new_actor.set_property('PythonModule', 'gameclasses')
# set the python class
new_actor.set_property('PythonClass', 'Vertical')
def tick(self, delta_time):
pass
Dealing with 2 different GC's is really challenging.
PyActor, PyPawn and PythonComponent automatically DECREF's the mapped classes. This should be enough unless you hold references in the python modules themselves. As this would be a bad programming practice, the current approach should be safe enough.
In addition to this, every time a uobject has to return its UObject mapping, it checks for its validity and safety:
#define ue_py_check(py_u) if (!py_u->ue_object || !py_u->ue_object->IsValidLowLevel() || py_u->ue_object->IsPendingKillOrUnreachable())\
return PyErr_Format(PyExc_Exception, "PyUObject is in invalid state")
The project could be considered in beta state.
Code should be organized way better as currently all of the uobject api is in a single huge file.
The property system still lacks Struct support
We would like to find a way to define and manage custom events directly from python
.py files are not recognized by the editor. This should be fixed soon
We still do not have a plugin icon ;)
The build system is not very robust. Maybe linking the python static library into the plugin dll could be a better approach.
Investigate what to do with threads (maybe disallowing unreal engine calls in python threads should be enough)
We need to find a way for automatic testing :)
If you want to contact us, drop a mail to info at 20tab.com