Bootstrap Chameleon Logo

How to manipulate User Defined ENum, Struct, DataTable with Python in Unreal Engine

alt ThreeTypesInBrowser

TAPython has extended lots of editor functions in PythonEditorLibs. In the latest version 1.0.5, TAPython add 40+ Editor API for use the User Defined Enum, User Defined Struct and DataTable. Now we can create, query and modify the User Defined Enum/Struct/DataTable with python.

Make it as simple as possible, but not simpler.

PythonEnumLib PythonStructLib PythonDataTableLib
get_display_name_map log_var_desc get_data_table_struct_path
set_enum_items log_var_desc_by_friendly_name get_data_table_struct
get_enum_len get_variable_description get_table_as_json
get_display_name_by_index get_guid_from_friendly_name get_row_names
set_display_name get_guid_from_property_name get_column_names
get_description_by_index get_variable_names get_shape
set_description_by_index get_friendly_names remove_row
get_name_by_index is_unique_friendly_name add_row
move_enum_item add_variable duplicate_row
is_bitflags_type add_directory_variable rename_row
set_bitflags_type remove_variable_by_name reset_row
get_cpp_form rename_variable move_row
change_variable_default_value get_row_name
get_column_name
get_flatten_data_table
get_property_as_string
get_property_as_string_at
set_property_by_string
set_property_by_string_at

In short, we can use Python to do almost everything you did manually in the editor with them. Download latest TAPython here

Below are the Cheat Sheets:

User Defined Enum

Create User Defined Enum

The code below will create a User Defined Enum asset at "/Content/CreatedByPython/IAmAEnum", with Unreal Engine's built-in function. In factor, other assets can be created using this method as well.

    asset_tools = unreal.AssetToolsHelpers.get_asset_tools()
    new_enum = asset_tools.create_asset("IAmAEnum", "/Game/CreatedByPython", unreal.UserDefinedEnum, unreal.EnumFactory())

Then we need add enum items to the Enum.

    items = ["A", "BB", "CCC", "DDDD", "EEEEE"]
    unreal.PythonEnumLib.set_enum_items(self.current_enum, items)

UserDefinedEnum

The Variable self.current_enum is the Enum we created in above. We can also load it from the package path.

    self.current_enum = unreal.load_asset("/Game/CreatedByPython/IAmAEnum")

Modify User Defined Enum

We can modify a specified item with a new name.

We can set the "name" with PythonEnumLib.set_display_name. The User Defined Enum has Two names, one is "Display Name" which used in UI, and another is the "Raw Name", the real name of enum item.

    # The enum item at index 3 will be: "iAmItem_3"
    unreal.PythonEnumLib.set_display_name(self.current_enum, 3, "iAmItem_{}".format(3))

Modfiy Enum's Description

The description of Enum is the description of Enum. It's an EditorProperty of UObject, so we can edit it with SetEditorProperty

    # set the description of enum
    self.current_enum.set_editor_property("enum_description", "Enum Description setted by Python")

enum_description

Modfiy Enum item's Description

Each Enum item also has an individual description. We can modify it with PythonEnumLib.set_description_by_index

    # we can iter each enum item, and set
    for i in range(unreal.PythonEnumLib.get_enum_len(self.current_enum)):
        unreal.PythonEnumLib.set_description_by_index(self.current_enum, i, f"item description {i}")

Set BitFlags Flag

    unreal.PythonEnumLib.set_bitflags_type(self.current_enum, True)

Moves the Enum item

We can move the enum iten to target index, other items will be shifting as needed. For example, with enum items: [A, B, C, D, E], moving index 1 to index 3 results in [A, C, D, B, E].

    unreal.PythonEnumLib.move_enum_item(self.current_enum, 1, 3)

042_UserDefinedEnum_enumItemDescriptions


User Defined Struct

As an important Data Assets, User Defined Structs also need to be able to be created, queried, and modified.

Create User Defined Struct

The way of create a User Defined Struct is similar to creating Enum.

    asset_tools = unreal.AssetToolsHelpers.get_asset_tools()
    result = asset_tools.create_asset("IAMStruct", "/Game/CreateByPython", unreal.UserDefinedStruct, unreal.StructureFactory())

Modify User Defined Struct

Add Variable

Add variable is a little more complex than adding an item to the Enum. As a variable, it can be a bool, float, struct, enum, interface, object or even a soft object class. And the data "container" type also could be: single, array, set or map.

The function doc of PythonStructLib.add_variable:

``` add_variable(...) X.add_variable(struct, category, sub_category, sub_category_object, container_type_value, is_reference=False) -> bool Add a new variable to specified User Defineded Strcut

Args:
    struct (UserDefinedStruct):         The User Defineded Strcut you want to modify.
    category (Name):                    The Category of the new Variable
    sub_category (Name):                The SubCategory of the new Variable
    sub_category_object (Object):       The SubCategoryObject of the new Variable
    container_type_value (int32):       Container type. 0: single, 1: array, 2: set. Use add_directory_variable if you want add a map variable
    is_reference (bool): Whether e new Variable passed as reference

Returns:
    bool: True if the new variable has been added

```

Let's look some examples:

  • Add a bool variable:

variable bool

    unreal.PythonStructLib.add_variable(self.current_struct, "bool", "", None, 0, False)
  • Add a int variable:

variable int

    unreal.PythonStructLib.add_variable(self.current_struct, "int", "", None, 0, False)
  • Add a int64(long) variable:

variable int64

    unreal.PythonStructLib.add_variable(self.current_struct, "int64", "", None, 0, False)
  • Add a float Array variable

variable float array

    unreal.PythonStructLib.add_variable(self.current_struct, "real", "double", None, container_type_value=1, is_reference=False)
  • Add a Name(FName) Set variable

variable name set

    unreal.PythonStructLib.add_variable(self.current_struct, "string", "", None, container_type_value=2, False)
  • Add a Vector variable

variable vector

    unreal.PythonStructLib.add_variable(self.current_struct, "struct", "", unreal.Vector.static_struct(), 0, False)
  • Add a Transform variable

variable Transform

    unreal.PythonStructLib.add_variable(self.current_struct, "struct", "", unreal.Transform.static_struct(), 0, False)

In fact, Vector, Rotator, Transform are all "struct".

  • Add another Struct variable

variable Abc material

    unreal.PythonStructLib.add_directory_variable(self.current_struct, 'struct', '', unreal.AbcMaterialSettings.static_struct(), 0)
  • Add a Interface variable

variable interface

   unreal.PythonStructLib.add_variable(self.current_struct, 'interface', '', unreal.AnimationDataController.static_class(), 0, False)
  • Add the User Denfind Enum that create above, which also created by python code.

variable created enum

    unreal.PythonStructLib.add_variable(_r, "byte", "", unreal.load_asset('/Game/CreatedByPython/IAmAEnum'), 0, False)
  • Add the User Denfind Enum which was defined in Engine Content:

variable land

    unreal.PythonStructLib.add_variable(self.current_struct, 'byte', '', unreal.load_asset('/Landmass/Landscape/BP/Enums/SectionSizeOptions'), 0)
  • Add a actor variable:

variable actor

    # Add Actor Reference
    unreal.PythonStructLib.add_variable(self.current_struct, "object", "", unreal.Actor.static_class(), 0, False)
  • Add a actor class variable:

variable actor

    # Object Class Reference
    unreal.PythonStructLib.add_variable(self.current_struct, "class", "", unreal.Actor.static_class(), 0, False)
  • Add a soft actor variable:

variable soft actor

     # Object Reference
    unreal.PythonStructLib.add_variable(self.current_struct, "softobject", "", unreal.Actor.static_class(), 0, False)
  • Add a actor soft class variable:

variable soft actor class

    unreal.PythonStructLib.add_variable(self.current_struct, "softclass", "", unreal.Actor.static_class(), 0, False)

Note

  • Not all datatype can be a Set or a Map variable. Only the "Hashable" type could.
  • The boolean value, "True" is hashable, but it can't be a Set or a Map variable.

In PythonStructLib.add_variable we use the param: container_type_value to specify the variable is "Single", "Array" or a "Set" variable. If we want to add a Map(dict) variable, we can use unreal.PythonStructLib.add_directory_variable

The code below will add a key-value map variable in the struct, the type of the key is actor soft class, and the value type is "string".

Variable Map

    unreal.PythonStructLib.add_directory_variable(_r, "softclass", "", unreal.Actor.static_class(), False, "string", "", None)

And how could I know the a variable's "Category", "SubCategory" or "sub_category_object"?

We could add the target variable by hand in the editor, then log the detail with PythonStructLib.log_var_desc

    unreal.PythonStructLib.log_var_desc(self.current_struct)

struct var desc

And we can see: "Category: softclass|None" are the values of "Category", "SubCategory"; SubCategoryObject is "/Script/Engine.Actor";
"PinValueType: string|None" are the values of "Terminal_Category", "Terminal_Sub_Category" which used for a Map container type value. TerminalSubCategoryObject is None.

And also we can get the var desc as a dict. PythonStructLib.get_variable_description

    desc = unreal.PythonStructLib.get_variable_description(self.current_struct, var_name)

DataTable

DataTable

Create DataTable

Creating DataTable is also an easy job using Python. The only difference with Creating Struct is that the struct is set to DataTableFactory. For example, the following example directly uses the "IAMStruct" created in Python above.

    asset_tools = unreal.AssetToolsHelpers.get_asset_tools()
    factory = unreal.DataTableFactory()
    factory.struct = unreal.load_asset("/Game/CreatedByPython/MyStruct")
    result = asset_tools.create_asset("IAmADataTable", "/Game/CreatedByPython", unreal.DataTable, factory)

Modify DataTable

Add a row

We can add a new row with the code below.

    unreal.PythonDataTableLib.add_row(self.current_datatable, row_name)

The "row_name" need to be unique in the datatable. So we can get and check it with the existing names.

    exists_names = unreal.PythonDataTableLib.get_row_names(self.current_datatable)
    bUnique = name not in exists_names

Remove a row

    unreal.PythonDataTableLib.remove_row(self.current_datatable, row_name)

Rename a row

A rename function is also useful.

    unreal.PythonDataTableLib.rename_row(self.current_datatable, row_name, new_row_name)

Get Content of DataTable

There are several ways to get the content of the datatable with PythonDataTableLib.

We can get it as a JSON object.

    unreal.PythonDataTableLib.get_table_as_json(self.current_datatable)

Or get it as a flatten array, and it's dimension(shape) can be get with get_shape

    unreal.PythonDataTableLib.get_flatten_data_table(self.current_datatable, include_header=False)`
    row_count, column_count = unreal.PythonDataTableLib.get_shape(self.current_datatable)

We can also get the cell content of datatable.

    # get value
    current_value = unreal.PythonDataTableLib.get_property_as_string_at(self.current_datatable, row_index, column_index)

Set the content of datatable

We can set every cell content of datatable with PythonDataTableLib.set_property_by_string_at

    # set value
    unreal.PythonDataTableLib.set_property_by_string_at(self.current_datatable, row_index, column_index, new_value)

This example will assign the static mesh which located in '/Game/Somewhere/YourMesh' to the datatable.

    # modify the static mesh reference in datatable at row 1 column 2
    new_value = "StaticMesh'/Game/Somewhere/YourMesh.YourMesh_0'"
    unreal.PythonDataTableLib.set_property_by_string_at(self.current_datatable, 1, 2, new_value)

Summary

Now, we can manipulate User Defined ENum, Struct, DataTable with Python in UE. The next step will be creating blueprint, and The final goal is to create the entire Unreal project in Python without any manual work? I hope so.