Although Python has a bad performace when using it in gameplay runtime, however, it can still be a excellent edtior tool. In this post, we will focus on the basic knowledge of Unreal Python and introduce some examples.

 

1. Setup Unreal Python Environment

 

First, you need to enable the python plugin

  1. Open your Project, and choose Edit > Plugins from the main menu.
  2. In the Plugins window, go to the Scripting section.
    Find the Python Editor Script Plugin in the right-hand panel, and check its Enabled box.
Python Plugin

What’s more, we need to add IDE support for ot. Open the Settings window and navigate to Project > Python Interpreter, then click the gear and select Show all. In the Python Interpreter window, you can click the Paths button and click + to add the location of your stub file.

You will also need to raise the maximum Intellisense file size by navigating to Help > Edit Custom Properties and adding the following, note that it costs a lot memory:

idea.max.intellisense.filesize = 25000

It is also avaiable for Rider:

 

2. How to run your python code in Unreal Engine Editor?

 

There are several ways to run a python script in unreal engine, we only introduce some of them, the other can be found in unreal engine documentation:

Run the python console in output log

Create a directory in your game project directory called “PythonScripts” for managment, add then add a python file called “test_console.py” whcih implementation is:

import unreal 
unreal.log("called from console")

There is a log category for python called LogPython, everytime you call unreal.log, the result will be printed in this category.

Call a python script by this method is just like call a python script in console of os, which allow you to call it with parameters:

import sys
import unreal

unreal.log("called from console")

for arg in sys.argv:
    unreal.log(arg)

Startup Files

In your Project Settings, you can specify any number of Python scripts that you want to run every time you open that Project. The Editor runs these scripts after the default startup Level is fully loaded.

Select Edit > Project Settings…. Under the Plugins list, select Python. Then, add your scripts to the Startup scripts setting:

I created a python file called “log_hello_when_start.py”, which is simply implemented as:

import unreal

unreal.log("hello")

By blueprint

The following are the most generally used blueprint nodes about python scripting:

Node NameDescription
Execute Python ScriptExecutes the literal Python code that you pass in to or type in the Python Command input. It is the recommended way to call Python from Blueprints, and superscedes creating a new BlueprintFunctionLibrary(BPFL) type in Python.

The aformentioned BPFL method causes issues when an asset is saved or loaded due to Python-generated types being transient, and is no longer officially supported.

Execute Python Script can have can have custom input and output pins, which are available as variables within the Python script on the node and can be added by the properties of the node.

-Inputs are set prior to running the Python script on the node, and can be read in the script. -Outputs are read after running the Python script on the node, and can be set in the script.

Execute Python Script can contain multiple lines of Python code, and new-lines can be entered by using the keys Shift+Return.

The Success? output is true if the Python code was executed successfully, or false otherwise. If it is false, you can find the errors in the Output Log.

Node that this node cannot run files. It can only execute lines of Python code.
Execute Python CommandExecutes the literal Python code or file that you pass in to or type in the Python Script input. The node will attempt to determine based on your input whether it is literal code or a filename.

-The Return Value output is true if the Python code or file was executed successfully, or false otherwise. If it is false, you can find the errors in the Output Log.
Execute Python Command (Advanced)Executes the literal Python code or file that you pass in to or type in the Python Script input. This node is similar to Execute Python Command, but offers some additional inputs and outputs that may be useful in some scenarios.

Execution Mode indicates how you want to interpret the Python Script input.

Select Execute File to force the command to treat your input as the name of a file, and to attempt to find and run that file. Any values returned by this script are printed to the log.

Select Execute Script to force the command to treat your input as literal Python code, and to execute it as-is. Any values returned by this script are printed to the log.

Select Evaluate Script to force the command to treat your input as literal Python code, evaluate it as-is, and return any values produced by the script in the Command Result output.

If you pass a filename to the Python Script input, the File Execution Scope determines whether that file is executed in the global scope (Public) or in a sandboxed scope (Private). If you want the code in your file to be able to access variables, functions, and so on that have already been defined in the Python environment, then you will need to choose Public. However, doing so also gives the Python file the ability to redefine variables and functions. This may accidentally cause unexpected behavior. Therefore, it may be safer to use the Private scope.

The Command Result output depends on the Execution Mode. When the Python Script input is executed or evaluated successfully, and the Execution Mode is Evaluate Script, this output returns the value produced by the script. When the Python Script input is executed or evaluated successfully, and the Execution Mode is either Execute File or Execute Script, this output returns None. When the Python Script input is not executed or evaluated successfully, this output contains the error information.

The Log Output provides access to the full list of messages written to the Python log during the execution of the input script or file. You can iterate through this array if you need to look for specific messages written by the script.

The Return Value output is true if the Python code was executed successfully, or false otherwise. If it is false, you can find the errors in the engine's Output Log, in the Log Output output pin, and in the Command Result.

 

3. Python Environment & Path in UE

 

When you use a relative path to run a Python script, or to import another script module using the import command in one of your scripts, the script that you run or import can be in any path that is listed in the sys.path variable of your Python environment.

The Unreal Editor automatically adds several paths to this sys.path list:

  • The Content/Python sub-folder in your Project’s folder.
  • The Content/Python sub-folder in the main Unreal Engine installation.
  • The Content/Python sub-folder in each enabled Plugin’s folder.
  • The Documents/UnrealEngine/Python folder inside your user directory. For example, on Windows 10, this is equivalent to C:/Users/Username/Documents/UnrealEngine/Python

You can also add your own paths to this list using any of the  approaches when mentioned before, add the path in project settings->Plugins->Python->Additional Paths

 

4. About the Unreal Python APi

 

The Python Editor Script Plugin exposes a wide range of classes and functions that you can use to interact with the Unreal Editor, the Assets in your Project, and the content in your Levels. This API is all contained in the unreal module. To access it, import this module at the beginning of any Python script you run in the Editor’s Python environment:

import unreal

The unreal module exposes nearly everything that is exposed from C++ to Blueprints in your Editor environment. It’s not pre-generated; it automatically reflects whatever is available in Blueprints in your Editor. As you enable new plugins in the Unreal Editor, anything those plugins expose to Blueprints also becomes available in Python as well. The same goes for any C++ code that you write in your Project and expose to Blueprints.

The Python API makes every effort to expose native Unreal APIs in a way that is as friendly as possible to Python developers. For example:

  • Simple data types are transparently converted back and forth between Python and native types whenever necessary.
    When you pass in a Python list, set or dict, it is automatically converted to an Unreal array, set, or map. When you retrieve a list, set, or dict returned by an API function, you are actually getting an instance of an Unreal class, but its API is fully consistent with the base Python list, set, or dict type.
  • Python classes maintain the same inheritance hierarchy as the native types they represent. That means, for example, that you can use the built-in Python functions isinstance() and type() to test whether an object derives from or matches a given class.
  • The API tries to strike a good balance between the naming conventions used in Unreal for C++ and Blueprints on one hand, and Python naming conventions on the other hand. Classes and objects in the Python API have the same names as they do in Blueprints. This is typically the same as their native types, omitting the prefix (e.g. U or T). Function and property names are automatically exposed as lower-case snake_case. So, for example, you typically call functions like unreal.StaticMeshActor.get_actor_transform(). Enumeration value names are automatically exposed as upper-case SNAKE_CASE.
  • All exposed functions can accept either ordered parameters, or named parameters in any order. For example, the following two function calls are exactly equivalent:

 

5. Editor Extension Examples

 

1. Create a progress dialogs for slow operation

import unreal
import time

total_frame = 100
text_label = "Working with slow progress"

unreal.log("start slow progress")
with unreal.ScopedSlowTask(total_frame, text_label) as slow_task:
    slow_task.make_dialog(True)
    for i in range(total_frame):
        # Sleep for 0.25 seconds
        time.sleep(0.25)
        if slow_task.should_cancel():
            break
        slow_task.enter_progress_frame(1)

 

2. Changing Editor Property

A good way to change properties in multiple assets:

import unreal

search_path = "/Game"

blueprint_generated_class = unreal.load_object(None, "/Game/StaticMeshActor.StaticMeshActor_C")
if blueprint_generated_class is None:
    unreal.log("obj is none")
blueprint_cdo = unreal.get_default_object(blueprint_generated_class)
blueprint_cdo.set_editor_property("Replicates", False)

 

3. Opearting all assets in given path

import unreal

search_path = "/Game"

unreal.log("print all file names under " + search_path)

asset_register = unreal.AssetRegistryHelpers.get_asset_registry()
assets = asset_register.get_assets_by_path(unreal.Name(search_path))
for asset in assets:
    unreal.log(asset.get_full_name())

text = unreal.NSLOCTEXT("Editor", "Entry", "入口")

 

4. Support Undo and Redo

We take the changing editor property we mentioned before as an example, you can use ctrl+z to undo it, note that you can check it in Edit->Undo History

import unreal

blueprint_generated_class = unreal.load_object(None, "/Game/StaticMeshActor.StaticMeshActor_C")
if blueprint_generated_class is None:
    unreal.log("obj is none")
blueprint_cdo = unreal.get_default_object(blueprint_generated_class)
with unreal.ScopedEditorTransaction("My Transaction Test") as trans:
    blueprint_cdo.set_editor_property("Replicates", True)

 

5. Add a Button Entry for Engine Editor

Note that there is no label text for this button because I use English culture for screenshot, however, I use Chinese culture by default and did not do localization for it.

import unreal


@unreal.uclass()
class MyEntryScript(unreal.ToolMenuEntryScript):
    @unreal.ufunction(override=True)
    def execute(self, context):
        on_button_clicked()

    @unreal.ufunction(override=True)
    def can_execute(self, context) -> bool:
        return True


def on_button_clicked():
    unreal.log("Button clicked")


def create_menu_widget():
    menu = unreal.ToolMenus.get()
    editor = menu.find_menu(unreal.Name("LevelEditor.MainMenu.Edit"))
    if not editor:
        unreal.log("cannot find LevelEditor.MainMenu.Edit")
        return
    entry = unreal.ToolMenuEntry()
    entry.name = "My Entry"
    entry.set_label(unreal.NSLOCTEXT("Editor", "Python Test Label", "Our Entry"))
    entry.type = unreal.MultiBlockType.MENU_ENTRY
    entry.insert_position = unreal.ToolMenuInsert(unreal.Name("Delete"), unreal.ToolMenuInsertType.AFTER)

    script_object = MyEntryScript()
    entry.script_object = script_object
    editor.add_menu_entry(unreal.Name("EditMain"), entry)


unreal.log("Add button extension to menubar")
create_menu_widget()

 

6. Logging and Feedback

The unreal object exposes functions that you can use in your code to send log, warning, and error messages through the same messaging system used by all Engine and Editor subsystems. We recommend using this standardized logging framework anytime your script needs to send a message to the user.

  • Use unreal.log() for information messages. For your convenience, the Python print() function has also been implemented to pass through unreal.log() internally.
  • Use unreal.log_warning() to alert users of potential problems.
  • Use unreal.log_error() for severe problems that prevent your script from running as expected.
unreal.log("log")
unreal.log_warning("warning")
unreal.log_error("error")

By JiahaoLi

Hypergryph - Game Programmer 2023 - Now Shandong University - Bachelor 2019-2023

2 thoughts on “Using Slate for UI development and editor(13): Python for Unreal Engine”
  1. Amazing! This blog looks just like my old one! It’s on a totally different topic but it has
    pretty much the same page layout and design. Wonderful choice of colors!

Leave a Reply

Your email address will not be published. Required fields are marked *