Challenges of Complex Data Management and the Need for Structs
When developing games with Unreal Engine, you'll inevitably encounter situations where you want to handle multiple related pieces of data as a group—character stats, item properties, complex configuration values, etc.
For example, consider managing character "health," "stamina," and "attack power." Keeping these as separate variables causes problems like:
- Reduced Readability: Multiple variables scattered around, making it unclear which variables belong to which data set.
- Blueprint Node Clutter: Too many function arguments, or having to "Set" and "Get" related variables individually creates many nodes, making graphs complex.
- Data Table Inefficiency: When using DataTable, related data becomes columns instead of rows, making data structuring difficult.
The powerful tool for solving these challenges and managing data logically and efficiently is Unreal Engine's Struct.
What is a Struct?
A struct is a mechanism that bundles variables of different data types (Variable Type) together to define a new custom data type.
Think of a struct like a "business card." A business card contains different types of information like "name (string)," "title (string)," "phone number (integer)," organized under the common theme of a single person.
In Unreal Engine, structs can be defined and used in both Blueprint and C++.
| Characteristic | Struct | Array |
|---|---|---|
| Storable Data Types | Can mix different data types | Single data type only |
| Purpose | Group logically related data | Hold same-type data as a list |
| Nature | Treated as Value Type | Treated as Value Type |
Relationship Between TArray and UObject
TArrayitself is copied as a value type, but when storing UObject pointers likeTArray<UObject*>, the contents are treated as reference types. Even if you copy the array, the stored UObjects point to the same instances.
USTRUCT and FStruct
When defining structs in C++, use the USTRUCT() macro to have them recognized by Unreal Engine's type system.
USTRUCT(): Used to define structs compatible with Unreal Engine's garbage collection and reflection system.FPrefix: By Unreal Engine convention, struct names have theFprefix (e.g.,FCharacterStats).
Practice! Creating and Using Structs in Blueprint
For beginners, the easiest approach is defining structs in Blueprint.
1. Create the Struct
- Right-click in Content Browser and select "Blueprint" -> "Structure."
- Give it a name (e.g.,
F_ItemData). - Double-click the created struct to open it and add needed variables with "New Variable."
Example: Item Data Struct F_ItemData
| Variable Name | Type | Role |
|---|---|---|
ItemName | String | Item name |
ItemID | Integer | Item ID |
IconTexture | Texture 2D Object Reference | Icon image |
Stackable | Boolean | Whether stackable |
2. Using Structs (Make/Break Struct Nodes)
Structs can easily input and output data using "Make Struct" and "Break Struct" nodes in Blueprint graphs.
- Make F_ItemData (Make Struct): Takes multiple input pins (
ItemName,ItemID, etc.) and outputs as a single struct pin. Used when creating new data. - Break F_ItemData (Break Struct): Takes a single struct pin as input and expands individual member variables as output pins. Used when extracting data from struct.
This consolidates function and event dispatcher arguments into a single struct, greatly simplifying node wiring.
graph LR
A[Item Name String] --> M
B[Item ID Int] --> M
C[Icon Texture Ref] --> M
M(Make F_ItemData) --> S[F_ItemData Variable]
S --> B2(Break F_ItemData)
B2 --> D[Item Name]
B2 --> E[Item ID]
Struct Definition in C++ and Exposing to Blueprint
For more advanced data management or when performance is required, define structs in C++ and make them usable from Blueprint.
// CharacterStats.h
#pragma once
#include "CoreMinimal.h"
#include "Engine/DataTable.h" // Needed when using with Data Table
#include "CharacterStats.generated.h"
// USTRUCT() macro to make usable from Blueprint
USTRUCT(BlueprintType)
struct FCharacterStats : public FTableRowBase // Use as Data Table row
{
GENERATED_BODY()
public:
// UPROPERTY() macro to make editable in Blueprint
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Stats")
float Health;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Stats")
float Stamina;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Stats")
int32 AttackPower;
// Constructor (set initial values)
FCharacterStats()
: Health(100.0f)
, Stamina(100.0f)
, AttackPower(10)
{}
};
Key Points:
USTRUCT(BlueprintType): Makes this struct usable as a data type in Blueprint.UPROPERTY(EditAnywhere, BlueprintReadWrite): Exposes struct member variables for read/write from Blueprint.FTableRowBase: Inherit this when you want to use this struct as Data Table rows.
C++-defined structs are automatically recognized on the Blueprint side, and "Make/Break" nodes become available just like Blueprint-created structs.
Common Mistakes and Best Practices
Structs are very convenient, but using them without understanding their nature can cause unexpected problems.
Common Mistake: Treating Structs Like Objects
Structs are Value Types. This means when assigning a struct to another variable or passing it to a function, the entire data is copied.
Example of Mistake: Assigning "Character A's stats" struct to "Character B's stats," then changing "Character B's stats" health—"Character A's stats" isn't affected. This is because data was copied during assignment.
Best Practice: Distinguishing Objects and Structs
| Use Case | Recommended Type | Reason |
|---|---|---|
| Static Data Definition | Struct | Treat as simple data collections like item base data, settings values. Excellent compatibility with Data Table. |
| Dynamic State Management | UObject / ActorComponent | Character inventory, in-game ongoing events, etc. Use when unique ID, lifecycle, or dynamic behavior (functions) is needed. These are treated as Reference Types. |
Common Mistake: Adding Functions to Structs (Blueprint)
Blueprint structs are pure data containers and cannot have functions (logic).
C++ Structs Can Define Functions
C++
USTRUCTcan define regular C++ member functions (thoughUFUNCTIONmacro cannot be used). For example, helper functions to calculate health can be inside the struct. However, these functions can't be called directly from Blueprint, so when Blueprint usage is expected, the recommended design is to have logic in a separate class.
Best Practice: Put Logic in Separate Classes
Have structs hold only data, and put logic operating on that data (e.g., damage calculation, status display formatting) in Actors, ActorComponents, or Blueprint Function Libraries. This separates data and logic, improving maintainability.
Key Points for Struct Usage
Structs in Unreal Engine are an essential data management technique for stepping up from beginner to intermediate.
| Struct Usage Benefits | Details |
|---|---|
| Data Encapsulation | Logically group related data, improving code and Blueprint readability. |
| Data Table Integration | Foundation for efficiently importing and managing complex game data from Excel or CSV. |
| Interface Simplification | Significantly reduce function and event input/output pins, simplifying Blueprint graph wiring. |
| C++ and Blueprint Integration | Make robust C++-defined data structures easily usable in Blueprint. |
Using structs dramatically organizes your project's data management, evolving toward robust design that withstands large-scale development. Start by choosing your most complex data set and try converting it to a struct.