Write Metadata in Unreal: Blueprint-Driven Workflows

Introduction

In the world of digital workflows, handling metadata is a common practice. In most CG pipelines, metadata is stored in external text files, allowing tools to easily read and write data. But when dealing with Unreal Engine, which operates predominantly in real-time, the challenge arises to access metadata through Blueprint scripting, requiring the metadata to be in Unreal’s native format.

After some research, I came across a proposal from a staff member in the Unreal Developer Community. In this article, we’ll explore the different methods from the proposal and discuss the approach I’ve settled on.

Data Table

Data tables offer a flexible way to structure data. However, it’s important to note that you need to define the data structure in advance to use them effectively

To have a working data table we need to first create a data structure where each entry of the data table will conform to.

data table and data structure Data Table (left) with corresponding Data Structure (right)

How to Read Data Table

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import unreal

dt_path = r'/Game/Cinematics/Metadata/DT_TestMetadata.DT_TestMetadata'

dt = unreal.EditorAssetLibrary.find_asset_data(dt_path).get_asset()

col_name = "Version"
row_names = unreal.DataTableFunctionLibrary.get_data_table_row_names(dt)
positions = unreal.DataTableFunctionLibrary.get_data_table_column_as_string(dt, col_name)
for idx, col in enumerate(positions):
print(f"{row_names[idx]} - {col_name}: {col}")

# entry1 - Version: 2
# entry2 - Version: 0

How to Write Data Table

It seems like data tables are ready-only at first, as there are only get_data function sets: https://forums.unrealengine.com/t/set-data-table-row/85844

However, there are in fact a way to write to data table, and that is with the use of .csv or .json.

1
2
unreal.DataTableFunctionLibrary.fill_data_table_from_json_file(data_table, json_file_path, data_structure)
unreal.EditorAssetLibrary.save_loaded_asset(data_table, True)

I won’t go into how to update .csv or .json files, but do note that the formatting of the text file is important, and you can get a reference of what the file would look at by simply export out the data table with some test data like so:

export option

Metadata

To access metadat for each asset, you can do a right-click “Asset Actions”, “Show Metadata”.

asset action

This is quite handy since, based on our need, it can be bound to an asset.

And if we want to separate the binding between the asset and the data, we can also do so by creating an empty blueprint asset and use it as a central container to store metadata.

The limitation however is that all the values are string type.

How to Read Metadata

1
2
3
4
5
6
7
8
9
10
11
12
import unreal

u_asset_path = r'/Game/LevelPrototyping/Meshes/SM_ChamferCube.SM_ChamferCube'
asset = unreal.EditorAssetLibrary.find_asset_data(u_asset_path).get_asset()

map = unreal.EditorAssetLibrary.get_metadata_tag_values(asset)
print(map)
# {"version": "4"}

value = unreal.EditorAssetLibrary.get_metadata_tag(asset, "version")
print(value)
# 4

show metadata

How to Write Metadata

We can also easily set and remove the metadata tag and values like so.

1
2
3
4
unreal.EditorAssetLibrary.set_metadata_tag(asset, "version", "4")
unreal.EditorAssetLibrary.save_asset(u_asset_path)

unreal.EditorAssetLibrary.remove_metadata_tag(asset, "version")

Blueprint Variable

And lastly, I want to introduce the approach I ended up with: map variable in a custom blueprint asset.

We first create a Blueprint asset using the basic Object class, and then add our custom ‘Version’ variable as a string: integer map this is basically equivalent to:

1
"Version": {str: int}

variable-1

variable-2

Through scripting, we first need to get the default class object of the blueprint, which can be achieved like so:

1
2
3
4
5
6
7
8
9
10
import unreal

u_bp_path = r'/asset_path'
u_bp = unreal.EditorAssetLibrary.find_asset_data(u_bp_path).get_asset()

# generated class
u_bp_generated = unreal.load_object(None, u_bp.generated_class().get_path_name())

# dco
u_bp_dco = unreal.get_default_object(u_bp_generated)

The advantage here lies in having a single blueprint asset capable of storing numerous variables, each natively typed to Unreal. Nevertheless, I haven’t yet attempted to create variables solely through code.

How to Read Blueprint Variable

1
2
3
4
5
6
7
def get_version(dco, name):
try:
version = dco.get_editor_property('version')[name]
except KeyError:
print('no {} key'.format(name))

return version

How to Write Blueprint Variable

Note that we use the get_editor_property() to set the metadata

1
2
3
4
5
6
7
8
9
def set_version(dco, name, version):
try:
u_bp_dco.get_editor_property('version')[name] = version
except TypeError as e:
print('TypeError: {} of {} incorrect, {}'.format(type(version), version, e))
return False

unreal.EditorAssetLibrary.save_asset(bp_path)
return True

Example

Since, this approach is the one I have experimented the furthest along, here’s an example on how I access it in another Blueprint:

bp

Another way is to use ‘Get Class Default Object’ and ‘Cast To BP Test Metadata’ which return ‘As BP Test Metadata’ and can then access the “Version” variable.

References

Unreal Forum - How to store text data with Python?

Unreal Forum - Reimport DataTable(csv) with python

Unreal Forum - Reading a DataTable’s Values with Python