Bootstrap Chameleon Logo

如何用Python在UE引擎中创建、修改 User Defined ENum, Struct 和 DataTable

alt ThreeTypesInBrowser

TAPython除了能够方便得创建编辑器界面,它还提供了若干编辑器扩展库。

在最新的1.0.5版本中的,PythonEnumLib, PythonStructLib, PythonDataTableLib 新增和扩展了对User Defined Enum,User Defined Struct,DataTable的支持。使得这些数据的操作可以完全Python化,进而将通过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

简而言之,在编辑器里能手动做的,现在用Python都可以做到了


下面是示例

User Defined Enum

创建User Defined Enum

下面的代码,可以在"/Content/CreatedByPython/IAmAEnum"位置,创建一个User Defined Enum。这使用的是UE内置功能,实际上,大部分的其他类型资源也都可以通过这个方法来创建:

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

然后可以对其设置枚举项:

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

UserDefinedEnum

上面 self.current_enum 就是上面新创建的User Defined Enum,当然我们也可以从工程中读取:

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

修改User Defined Enum

我们可以通过 PythonEnumLib.set_display_name 修改指定的枚举项。实际上User Defined Enum中有所谓的Display Name 和 "Raw Name",Display Name只用于显示,"Raw Name" 是实际的枚举项

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

修改 Enum's 描述

Enum上的描述文字是Enum对象自身的一个EditorProperty,因此可以通过 SetEditorProperty 对其进行修改:

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

enum_description

修改枚举项描述

每个枚举项自身的文字描述可以通过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}")

修改 BigFlags

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

移动枚举项

我们移动特定的枚举项到指定位置,其他的枚举项将依次按序移动。比如将[A, B, C, D, E], index 1 移动到 index 3 时结果将会是 [A, C, D, B, E]:

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

042_UserDefinedEnum_enumItemDescriptions


User Defined Struct

作为一个重要的“数据资源”,User defined Struct 当然也需要能够创建、查询和修改

创建User Defined Struct

创建User Defined Struct的方法和上面创建User Defined Enum的方式类似:

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

修改 User Defined Struct

添加变量

在Struct中添加变量会比在Enum中添加一个枚举项要复杂一些。因为变量可以是各种基础类型,也可以是其他Struct,接口,对象,甚至是一个“对象类的软引用”,同时Struct中的变量类型还可以指定为“单个”,“数组”,“集合”和“映射”(字典)类型

下面是 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

```

下面直接举例:

  • 添加一个布尔变量:

variable bool

    unreal.PythonStructLib.add_variable(self.current_struct, "bool", "", None, 0, False)
  • 添加一个int变量:

variable int

    unreal.PythonStructLib.add_variable(self.current_struct, "int", "", None, 0, False)
  • 添加一个int64变量:

variable int64

    unreal.PythonStructLib.add_variable(self.current_struct, "int64", "", None, 0, False)
  • 添加一个float类型的数组变量:

variable float array

    unreal.PythonStructLib.add_variable(self.current_struct, "real", "double", None, container_type_value=1, is_reference=False)
  • 添加一个Name类型的集合变量:

variable name set

    unreal.PythonStructLib.add_variable(self.current_struct, "string", "", None, container_type_value=2, False)
  • 添加一个Vector变量

variable vector

    unreal.PythonStructLib.add_variable(self.current_struct, "struct", "", unreal.Vector.static_struct(), 0, False)
  • 添加一个Transform变量

variable Transform

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

事实上,Factor, Vector, Rotator, Tranform 都是struct类型

  • 添加其他Struct类型的变量

variable Abc material

    unreal.PythonStructLib.add_directory_variable(self.current_struct, 'struct', '', unreal.AbcMaterialSettings.static_struct(), 0)
  • 添加一个接口类型的变量

variable interface

   unreal.PythonStructLib.add_variable(self.current_struct, 'interface', '', unreal.AnimationDataController.static_class(), 0, False)
  • 添加一个我们在上文中创建的User Defined Enum类型的变量

variable created enum

    unreal.PythonStructLib.add_variable(self.current_struct, "byte", "", unreal.load_asset('/Game/CreatedByPython/IAmAEnum'), 0, False)
  • 添加一个在引擎资源中的User Denfind Enum为类型的变量

variable land

    unreal.PythonStructLib.add_variable(self.current_struct, 'byte', '', unreal.load_asset('/Landmass/Landscape/BP/Enums/SectionSizeOptions'), 0)
  • 添加一个Actor类型的变量`

variable actor

    # Add Actor Reference
    unreal.PythonStructLib.add_variable(self.current_struct, "object", "", unreal.Actor.static_class(), 0, False)
  • 添加一个以“Actor类”为类型的变量

variable actor

    # Object Class Reference
    unreal.PythonStructLib.add_variable(self.current_struct, "class", "", unreal.Actor.static_class(), 0, False)
  • 添加一个以“Actor软引用”为类型的变量

variable soft actor

     # Object Reference
    unreal.PythonStructLib.add_variable(self.current_struct, "softobject", "", unreal.Actor.static_class(), 0, False)
  • 添加一个以“Actor类软引用”为类型的变量

variable soft actor class

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

注意

  • 不是所有的举例类型都可以成为集合或者映射(字典)类型,只有可哈希的类型可以
  • 布尔类型的值,比如True是可哈希的,但却不能成为集合或者映射(字典)类型的变量

PythonStructLib.add_variable中,我们用参数container_type_value来指定变量的container类型是"Single", "Array"或者"Set",如果我们想要创建一个Map集合(dict字典) 类型的变量,我们可以用:PythonStructLib.add_directory_variable

下面这行python代码将 - 添加一个key为 actor soft calss类型的,值为string类型的映射(字典)类型变量:

Variable Map

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

那么我要如何才能知道变量的所谓"Category", "SubCategory" 和 "sub_category_object"分布都是什么呢?

我们先在编辑器中添加一个想要的变量,然后通过PythonStructLib.log_var_desc来查看:

    unreal.PythonStructLib.log_var_desc(self.current_struct)

struct var desc

比如上图,Category: softclass|None 分别是"Category", "SubCategory"的值,SubCategoryObject为 /Script/Engine.Actor;PinValueType: string|None 中的分别是字典类型Value的 "Terminal_Category" 和 "Terminal_Sub_Category"的值,TerminalSubCategoryObject为 None。

SubCategoryObject 和TerminalSubCategoryObject为对于类型的static_class或static_struct。具体可见上文范例

另外,我们也可以通过unreal.PythonStructLib.get_variable_description 以字典的形式获得指定变量的具体“变量描述”

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

DataTable

DataTable

创建DataTable

作为UE引擎的“正统数据表”,DataTable也是可以用Python创建的,唯一的不同是在DataTableFactory中需要指定该DataTable所使用的Struct,比如下面的例子直接使用了上文中同样通过python创建的IAMStruct

    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)

修改 DataTable

添加行

我们可以给DataTable添加一行:

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

上面的"row_name"作为行名是需要在这个DataTable中是唯一的,不许同名。所以我们需要查询该name是否已经存在

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

移除一行

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

重命名一行

重命名也是必需的

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

取得DataTable中内容

PythonDataTableLib 提供了多种方法来查询DataTable中的内容

我们可以将其整个以一个JSON文件的形式返回:

    unreal.PythonDataTableLib.get_table_as_json(self.current_datatable)

或者将其一个1D数组的形式返回。当然这个时候我们需要知道它的行列数量,通过下面的PythonDataTableLib.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)

此外,还可以通过指定某个单元格的形式来获取

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

设置DataTable内容

我们可以通过unreal.PythonDataTableLib.set_property_by_string_at来设置指定“单元格”中的内容

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

例如,下面的例子将把DataTable中引用的static_mesh指定为:'/Game/Somewhere/YourMesh'这个路径中的mesh

    # 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)

小结

至此,我们可以通过TAPython中的 PythonEnumLib,PythonStructLib, PythonDataTableLib “掌控”工程中的User Defined Enum,Struct 和DataTable。用脚本维护这些数据类型资源将会变得方便。那下一步是通过python创建整个工程的资源?