The goblin is a bit weak, so you want to raise its HP from 60 to 90. Opening each enemy's Blueprint to hunt for the number also makes it hard to compare balance across enemies.
With a Data Table you can gather enemy names, HP, and attack into one table. You tell the Blueprint "which row to read" and do the number tuning in the table. Since you can import CSV edited in Excel and the like, you can tune while surveying the numbers even as enemies multiply.
In other engines: since it is tabular data of rows, the closest usage in Unity is a database built with ScriptableObject or reading CSV/JSON, and in Godot a CSV or an array of
Resource.
In this article we build a table of slime, goblin, and ogre, and display the values loaded at game start as text. At the end we change one spot in the CSV and confirm that only the goblin's HP changes.
What You'll Learn
- Deciding the table's fields with a Struct and building a Data Table from CSV
- Specifying a row name to read one kind's data in Blueprint
- Confirming the not-found case separately
- Re-importing CSV to change only the numbers
It assumes you have created variables and nodes in Blueprint. If Structs are new to you, the Struct introduction is a useful reference.
How it works: the Struct holds fields, the Data Table holds values
First, look at the table we are about to build.
| Name | DisplayName | MaxHealth | Attack |
|---|---|---|---|
| Slime | Slime | 30 | 5 |
| Goblin | Goblin | 60 | 12 |
| Ogre | Ogre | 200 | 30 |
A Struct is a type for bundling one item's worth of information. Here we decide the fields that go in one row: "display name is text; max HP and attack are integers."
A Data Table lines up many rows of actual values in that shape. The goblin and the ogre have the same fields with different numbers inside.

The leftmost Name is the name used to designate the row you want to read. UE calls it the Row Name. Specify Goblin and you pull "Goblin, 60, 12" out as one unit. There is no need to remember the row order.
Name and DisplayName have different roles. The former is the name the program searches by; the latter is the name shown to the player. Change the display to "Forest Goblin" and, as long as the row name stays Goblin, the Blueprint's designation needs no change.
Preparation: building a table of three enemies
1. Decide the columns with a Struct
Right-click in the content browser, choose "Blueprints" → "Structure," and create S_EnemyStats. Open it and add these three fields.
| Field name | Type | Meaning |
|---|---|---|
| DisplayName | Text | The enemy name shown on screen |
| MaxHealth | Integer | Max HP. We use no decimals here |
| Attack | Integer | Attack power |
Delete the unnecessary field that exists initially and save. Do not add Name to the Struct. The row name is held by the Data Table side.
2. Prepare the CSV
CSV is a file format writing one table row with comma separators. You can edit it in Excel or Google Sheets, but for now you can also create it by pasting the following into a text editor.
Name,DisplayName,MaxHealth,Attack
Slime,Slime,30,5
Goblin,Goblin,60,12
Ogre,Ogre,200,30
Create a SourceData folder inside the project folder and save it as EnemyStats.csv. That puts your editing source outside Content. Choose UTF-8 as the encoding. In Excel, save in "CSV UTF-8 (Comma delimited)" format.
The first line is the header. After Name, line up the field names exactly as you named them in the Struct. Line two onward is the actual data, one enemy kind per row.

3. Import the CSV
- Open the folder in the content browser where you want to save the table.
- Drag and drop
EnemyStats.csvonto that folder. - Set "Import As" to
DataTable. - Choose
S_EnemyStatsin "Data Table Row Type" and import. - Rename the created asset to
DT_EnemyStatsand save.
Row Type designates "which Struct's shape one row of this table is read as." Leave the "Import Key Field" box empty if it appears. With that setting, the CSV's first column is used as the row name. Do not enable options to ignore missing or extra fields; if a warning appears, compare the header against the Struct.

Open DT_EnemyStats and confirm there are three rows. If the Goblin row shows Goblin as its display name with MaxHealth 60 and Attack 12, you are ready. If characters look corrupted, check the CSV's encoding and save it again.
The Data Table you made here is the asset the game reads. The CSV is its editing source, and the Blueprint we build next does not open the CSV directly during Play. Epic's CSV import procedure
Hands-On: reading a specified row and displaying the result
We place three of the same Blueprint and change only which row each reads. On success, each enemy's values are displayed, as in "Slime: HP 30 / ATK 5." Try one goblin first, then add more.

The figure in the diagram represents the reading Actor. In this exercise we add no mesh or movement and confirm through text output.
1. Create an Actor whose row you can choose
- Create a Blueprint
BP_EnemyDataDemowith "Actor" as parent class. - Create a variable
EnemyRowNameof type Name. - Enable the variable's "Instance Editable" and Compile.
- Place one
BP_EnemyDataDemoin the level. - Select the placed Actor and enter
GoblininEnemyRowNamein the details panel.
Name is the type for identifiers such as row names. Enabling "Instance Editable" lets you specify a value per Actor placed in the level. Even using the same Blueprint, one can be Slime and another Goblin.
2. Split the found and not-found cases
In BP_EnemyDataDemo's event graph, connect Get Data Table Row from Event BeginPlay. BeginPlay is the event called when this Actor starts running in the game.
Choose DT_EnemyStats in the node's "Data Table" and connect a Get of the variable EnemyRowName to "Row Name."

The node has these outputs.
| Output | Role |
|---|---|
| Row Found | The white exec pin taken when the row is found |
| Row Not Found | The white exec pin taken when the row is not found |
| Out Row | The one row's worth of values. Here that is S_EnemyStats |
Place two Print Text nodes that output text to the screen. Connect a white line from "Row Found" to the success one and from "Row Not Found" to the failure one, and enter Row not found in the failure one's "In Text." We build the success display's content in the next step.
Success and failure split on those two exec lines. Use Out Row's value on the found path. Epic's Get Data Table Row API
3. Turn one row's values into a readable sentence
Drag from "Out Row" and create Break S_EnemyStats. Break is the node that pulls each field out of a Struct bundle. "Display Name," "Max Health," and "Attack" become readable.
Next place a Format Text and enter the following in "Format."
{Name}: HP {HP} / ATK {ATK}
The { } parts are where values go. Entering it adds "Name," "HP," and "ATK" pins, so connect the Break's three fields to each. This diagram extracts the value connections. The Get node at the left is the one you placed in the previous step.

| Break output | Format Text input |
|---|---|
| Display Name | Name |
| Max Health | HP |
| Attack | ATK |
Finally connect Format Text's "Result" to the success Print Text's "In Text." For the goblin row, the sentence becomes Goblin: HP 60 / ATK 12. The Name here is the display slot's name, separate from the EnemyRowName that chooses the row.

Break and Format Text have no white exec pins. They are nodes computed when the values to display are needed. The order of operations is decided by the white lines from BeginPlay → get the row → Print Text, and colored lines pass the display content. Epic's Format Text API
On both Print Texts, enable "Print to Screen" and "Print to Log" and set "Duration" to 10. Expand the details pin if it is collapsed. Leave "Key" unspecified (None).
4. Try both a valid row and a missing one
Compile and Play. Goblin: HP 60 / ATK 12 on screen means you read one row of the table. To check the display later, open the editor's "Output Log." Print Text's "Print to Log" is the setting that also leaves the sentence in that log. Epic's Print Text API
Stop Play once and change the placed Actor's EnemyRowName to MissingEnemy, which is not in the table. This time Row not found appears, confirming the failure path too. Restore Goblin afterwards.
Then duplicate the Actor twice and set the three EnemyRowName values to Slime, Goblin, and Ogre. Play again and confirm all three names and numbers appear. Output order does not matter; verify by each row's contents.
Tuning: changing only the goblin's HP in CSV
Now for the number tuning. Leave the Blueprint's nodes alone and change only the goblin's row in EnemyStats.csv.
Name,DisplayName,MaxHealth,Attack
Slime,Slime,30,5
Goblin,Goblin,90,12
Ogre,Ogre,200,30
- Stop Play.
- Change
Goblin'sMaxHealthfrom60to90in the CSV and save to the same file. - Right-click
DT_EnemyStatsin the content browser and choose "Reimport." - Open the table and confirm
Goblin'sMaxHealthis now90. - Save the Data Table and Play again.
Reimport re-reads the file at the original import location and updates the asset's contents. Pair saving the CSV with updating the table. Epic's reimport procedure

Success is Goblin: HP 90 / ATK 12 displayed while the slime stays at HP 30 and the ogre at HP 200. With the reading logic in place, balance tuning no longer requires editing nodes.
If it still shows 60, check the Data Table first. If the table also shows 60, check where the CSV was saved and whether you reimported; if the table shows 90, check whether you restarted Play. Since we read in BeginPlay, editing the CSV while running does not make the display logic run again automatically.
Bonus: Good to Know Up Front
Keep max HP separate from remaining HP
What we displayed is the per-kind initial settings written in the table. In real combat, put the loaded MaxHealth into something like the enemy Actor's CurrentHealth and reduce that Actor's current HP on damage.
Consider the pre-tuning max HP of 60. Spawn two goblins and damage one for 10, and their current HP is 50 and 60. You do not want to change the goblin kind's max HP to 50.

With that split you can handle changing every goblin's strength and damaging the one in front of you without confusing them. Logic putting current HP into a variable and handling damage is added on the combat side.
Keep row names stable, separate from display names
Change Goblin to a different row name and the Actors specifying that name need fixing too. When you only want to change the name shown to players, edit DisplayName.
The Name type used for Row Names compares case-insensitively. You cannot use Goblin and goblin as different row names. To reduce misreadings while working, aligning the notation between the table and Blueprint keeps it easy to follow. Epic's documentation on the Name type
Decide whether CSV or the editor is your source
Data Tables can be edited inside the editor too. But re-importing a CSV updates the table with that file's contents. Set HP to 90 in the editor and then reimport an old CSV and you unintentionally revert to 60.
In this article's workflow, the CSV is the source. If you move the file, also check the import source the Data Table references. When you want to move an existing table into CSV, use the Data Table's "Export as CSV" to produce a template and reduce mistakes copying field names by hand.
Separate changing a table's fields from tuning values
HP 60 → 90 is changing an existing field's value. Adding a new field to the Struct, or changing a name or type, also affects the CSV header and Break nodes. Keep the pre-change file, and check the import warnings, the table's values, and the Blueprints using it.
Data Tables suit lining numbers up vertically for comparison. When you want to pick meshes and sound effects per item in the editor, looking at how to use Data Assets alongside makes choosing where to put your data easier.
Summary
- The Struct holds "the table's fields" and the Data Table holds "their values"
- Specify a row name to read, and handle the not-found case separately
- Re-import the CSV and you can swap just the numbers
The question before using it is "will many rows of the same shape line up?" If they will, a table; if each item differs in nature, a Data Asset suits better.
To give each item its own settings, go to Data Assets and data-driven design; to carry the values you read around, go to Structs.