Before You Begin
To start this tutorial, you need an Unreal Engine Project with C++ classes.
This page continues from Add a First-Person Camera, Mesh, and Animation; however, you can complete this independently of the rest of the Code a First-Person Adventure Game tutorial series if you'd just like to learn about data-driven gameplay.
Data Organization in Games
How you organize and represent data is an important part of game design. Interactable items could have very different qualities and can exist in game or just as a piece of data. You could represent this data by creating separate classes and Blueprints for each item type, distributing and communicating data across different Actors. However, this gets inefficient as your game and the amount of stored data grows.
A better approach is data-driven gameplay. Instead of hardcoding values, you'll organize data in a centralized location managed by systems in your game. Data-driven gameplay lets you load what you need when you need it. For example, many games use spreadsheet documents to organize dialogue as it's easier for a system to pull a specific line of dialogue instead of storing potentially hundreds of lines in each character.
In this section, you’ll learn how use this methodology to start creating custom items.
Data-Driven Gameplay Elements
Before you start building basic items, it’s important to consider what defines an “item”. Since an item can be anything a player interacts with, it should have a minimal set of properties that are valid for any type of item. You’ll set this up in an Item Data Struct. You should also have a centralized place to organize and display this item data. For that, you’ll use a Data Table asset.
Your Item Data Struct acts as a template that defines the type of data your item has, then your Data Table and Data Assets store the actual data entries based on your struct. Data Assets also make it easier to inspect individual items and provide a natural extension point as item behavior becomes more complex.
The diagram below shows the four data-driven gameplay elements you’ll create in this part of the tutorial. Once you’ve finished setting up all four elements, you’ll revisit this diagram in more detail to help summarize what you’ve built.
First, you’ll create two files to define your item data:
ItemData.h: A container for the Item Data Struct (FItemData) declaration.ItemDefinition.h: A class that inherits fromUDataAssetso your item data can be used in Unreal Editor.
The Item Data Struct doesn’t inherit from UObject and can’t be instantiated in a level, so you also need the Data Asset class to use and reference in the editor.
Then, in Unreal Editor, you’ll create a Data Table and a Data Asset Instance based on ItemDefinition.
Define Your Item Data
Your Item Data Struct defines the data or properties each item in the Data Table should have, acting like the table's columns.
Your Item Data Struct will have the following properties:
ID: A unique name for the item, useful when referencing table rows later.
Item Type: The type of this item (in this case, you’ll define Tool and Consumable types).
Item Text: Textual data about the item, including a name and description.
Item Base: The ItemDefinition Data Asset associated with this item.
If you want to create your own table fields (columns), keep in mind that Data Table fields can be any types that are compatible with UPROPERTY().
Create a Header File Container for the Struct
First, set up a new folder and new header (.h) file to store your Item Data Struct definition.
You could create the FItemData struct within ItemDefinition.h, but putting the struct in a separate file helps organize your data elements and allows for reuse.
To set up a header file as a container for your Item Data Struct, follow these steps:
In Unreal Editor, go to Tools > New C++ Class.
In the Choose Parent Class window, select None as the parent class, then click Next.
Next to Path, click the folder icon. In your
Source/[ProjectName]folder, go into thePublicfolder, and create a new folder namedDatato store this class.Unlike a standard C++ project, Unreal only exposes headers placed under the game module’s
Publicfolder to other code. Since ItemData could be included by multiple classes, create it inPublic/Datato ensure it’s visible to the build system.Name the class
ItemDataand click Create Class.If Unreal Engine doesn't open your new class files automatically, open your project and
ItemData.hin Visual Studio.Delete all text in
ItemData.cpp, then save and close the file. You won't be using it.In
ItemData.h, delete everything under the#include “CoreMinimal.h”line.The
CoreMinimal.hheader includes basic types likeFName,FString, and other types you’ll need to define your data.In
ItemData.h, add the following include statements:#include “Engine/DataTable.h”: Required to have your struct inherit fromFTableRowBase.#include “ItemData.generated.h”: Required by Unreal Header Tool. Make sure this include statement comes last for your code to compile properly.
C++#pragma once #include "CoreMinimal.h" // --- New Code Start --- #include "Engine/DataTable.h" #include "ItemData.generated.h" // --- New Code End ---Add a forward declaration for a class named
UItemDefinition. This will be the Data Asset that you can work with in the editor.C++#pragma once #include "CoreMinimal.h" #include "Engine/DataTable.h" #include "ItemData.generated.h" // --- New Code Start --- class UItemDefinition; // --- New Code End ---
Define Item Properties
There's no existing variable types for an item type and text data, so you'll need to define these.
You'll create two properties:
An enum that lists all possible item types. In this tutorial, you’ll create Tool and Consumable-type items.
A struct that lists item properties (a name and a description).
To define an Item Type enum, follow these steps:
In
ItemData.h, after the#includestatements, define anenum classnamedEItemTypeand give it typeuint8.Add the
UENUM()macro above it with aBlueprintTypespecifier to declare this enum to Unreal Header Tool and expose it as data inside Blueprints, allowing it to appear in Data Tables.C++// Defines the type of the item. UENUM(BlueprintType) enum class EItemType : uint8 { };Add two custom values to this enum:
Tool, adding aUMETA()macro withDisplayName = “Tool”Consumable, adding aUMETA()macro withDisplayName = “Consumable”
C++// Defines the type of the item. UENUM(BlueprintType) enum class EItemType : uint8 { Tool UMETA(DisplayName = "Tool"), Consumable UMETA(DisplayName = "Consumable") };
These item types are custom and not built into Unreal Engine; so once you’ve learned the basics, you can make anything you like! (Perhaps a QuestItem, Currency, or Disguise?)
To define an Item Text struct, follow these steps:
After
EItemType, create a new struct namedFItemTextwith aUSTRUCT()macro containingBlueprintType. This struct holds textual data about your item.C++// Contains textual data about the item. USTRUCT(BlueprintType) struct FItemText { };Inside
FItemText, add theGENERATED_BODY()macro.Then, add two
FTextproperties namedNameandDescriptionto store the name and description for this item. Add theUPROPERTY()macro to each withEditAnywhereandBlueprintReadOnlyas the arguments.C++// Contains textual data about the item. USTRUCT(BlueprintType) struct FItemText { GENERATED_BODY() // The text name of the item. UPROPERTY(EditAnywhere, BlueprintReadOnly) FText Name;BlueprintReadOnlyexposes the property to Blueprints so it can be read and used in graphs, but prevents it from being modified at runtime.
Create the Item Data Struct
Now that you’ve added those prerequisite declarations, create the Item Data Struct with your item’s properties inside. These properties become the fields in your Data Table.
To define your data table's structure, follow these steps:
Below
FItemText's definition, define a struct namedFItemDatathat inherits fromFTableRowBase.Add the
publicspecifier to make it visible anywhere and addGENERATED_BODY()for Unreal Header Tool.FTableRowBaseis a base struct provided by Unreal Engine that lets you use your customUSTRUCTs in a Data Table asset. Unreal uses it to know how to serialize your row struct, to support importing and exporting data from CSV/JSON files, and to ensure type safety when pulling data from the table.C++// Defines a basic item that can be used in a data table. USTRUCT(BlueprintType) struct FItemData : public FTableRowBase { GENERATED_BODY() };In the
FItemDatastruct, add the following declarations:An
FNamenamedID. Each row in a Data Table needs an associatedFNameto reference.An
EItemType enumnamedItemType. This is the enum of item types you declared earlier.An
FItemTextstruct namedItemText. This is the struct of text data you declared earlier.
Add the
UPROPERTY()macro to each declaration withEditAnywhere,BlueprintReadOnly, andCategory = “Item Data”arguments.C++// Defines a basic item that can be used in a data table. USTRUCT(BlueprintType) struct FItemData : public FTableRowBase { GENERATED_BODY() // --- New Code Start --- // The ID name of this item for referencing in a table row. UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Item Data")Declare a
TSoftObjectPtrto aUItemDefinitionnamedItemBase. Give it the sameUPROPERTYmacro as the other properties in the struct.Data Tables must serialize their data to disk and therefore can’t store hard
UObjectreferences, so asset references likeWorldMeshshould use a soft pointer.TSoftObjectPtris a special type of weak pointer that references an asset by path and loads it only when needed. For more information, see Asynchronous Asset Loading.C++// Defines a basic item that can be used in a data table. USTRUCT(BlueprintType) struct FItemData : public FTableRowBase { GENERATED_BODY() // The ID name of this item for referencing in a table row. UPROPERTY(EditAnywhere, Category = "Item Data") FName ID;In the next step, you’ll create a
UDataAssetclass namedItemDefinition. In your Data Table, you’ll use theItemBasefield to referenceItemDefinition Data Assetinstances.Save your code. Don't compile yet, your code won't build until you've created
ItemDefinition.
ItemData.h should now look like this:
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Engine/DataTable.h"
#include "ItemData.generated.h"
class UItemDefinition; // NEW put this back
Build an Item Definition DataAsset
So far, you’ve defined your item data, or the type of data that appears in your Data Table. Next, you’ll implement the UItemDefinition class that you'll use to make Data Assets in Unreal Editor.
This class inherits from UDataAsset and therefore is a UObject, meaning you can create and use instances of it directly in the editor without going through code.
To create your ItemDefinition DataAsset class (ItemDefinition.h), follow these steps:
In Unreal Editor, go to Tools > New C++ Class.
In the Choose Parent Class window, click All Classes.
Search for and select DataAsset as the parent class, then click Next.
Name the class
ItemDefinitionand click Create Class.
VS should automatically open your new class’ .h and .cpp files. If not, refresh VS and open the files manually. You’ll just be working in the .h file, so you can close the .cpp file.
To set up ItemDefinition.h, follow these steps:
In
ItemDefinition.h, add an include forItemData.hbecause you’ll want to re-use theItemTypeandItemTextproperties you declared in that file.C++#include "CoreMinimal.h" #include "Engine/DataAsset.h" // --- New Code Start --- #include "Data/ItemData.h" // --- New Code End --- #include "ItemDefinition.generated.h"The module’s
Publicfolder acts as the include root, so you don’t includePublic/in the path — start from the folder inside it instead.In the
UCLASS()macro above the class definition, addBlueprintTypeandBlueprintablespecifiers to expose this as a base class for creating Blueprints.C++// Defines a basic item with a static mesh that can be built from the editor. UCLASS(BlueprintType, Blueprintable) class ADVENTUREGAME_API UItemDefinition : public UDataAsset { GENERATED_BODY() };In the class definition, add a
publicsection if one doesn't exist already.In the
publicsection, copy theFName ID,EItemType ItemType, andFItemText ItemTextdeclarations fromItemData.h.Your item definition gets the same data as the
FItemDatastruct so you don’t need to reference the original table when you want to get information about the item.C++// Defines a basic item with a static mesh that can be built from the editor. UCLASS(BlueprintType, Blueprintable) class ADVENTUREGAME_API UItemDefinition : public UDataAsset { GENERATED_BODY() public: // --- New Code Start ---After
ItemText, declare aTSoftObjectPtrof typeUStaticMeshnamedWorldMesh. You’ll use this static mesh to display this item in the world.Add the same
UPROPERTY(EditAnywhere, Category = “Item Data”)macro to this property.C++public: // The ID name of this item for referencing in a table row. UPROPERTY(EditAnywhere, Category = "Item Data") FName ID; // The type of the item. UPROPERTY(EditAnywhere, Category = "Item Data") EItemType ItemType;Save your code and compile it from Visual Studio.
Data Table rows are similar to CSV rows, meant for holding textual data and not storing full assets. To optimize data management, we recommend bundling information like an item’s mesh, material, and animations in a DataAsset as this is the central place where all data about one particular item lives. So, the item’s static mesh property is here in UItemDefinition instead of in the FItemData struct.
Your complete ItemDefinition.h should now look like this:
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Engine/DataAsset.h"
#include "Data/ItemData.h"
#include "ItemDefinition.generated.h"
// Defines an item definition built from data table data.
Create a Data Asset Instance
With your item data (FItemData struct from ItemData.h) and item definition (UItemDefinition class) defined, you’ve got all the pieces you need to build your item instances and Data Table.
First create a Data Asset for a new projectile pickup item, then create a Data Table and populate it with your Data Asset’s information.
To create a Data Asset item from your ItemDefinition class, follow these steps:
In the Content Browser, go to the Content > FirstPerson folder, click Add or right-click a blank area in the Asset View, select New Folder, and name it
Data.This is where you’ll store and organize data assets in your game.
In the
Datafolder, click Add or right-click a blank area in the Asset View and select Miscellaneous > Data Asset.Ensure you pick the Data Asset option with the pie chart icon.
In the Pick Class For Data Asset Instance window, select Item Definition (the C++ class you defined earlier), then click Select. Name the new Data Asset
DA_Pickup_001.Double-click
DA_Pickup_001to open it. In its Details panel, you’ll see all the properties you defined inItemDefinition.h.If your Data Asset is empty, you may need to click Compile in Unreal Editor.
Enter
pickup_001as the ID.Set the Item Type to Consumable.
Expand Item Text and enter a Name and Description.
The item name and description are just data right now, but as you continue to expand your game, you can use or display these fields. For example, by setting up the UI to show a tooltip of the inventory.
Set the World Mesh to
SM_FoamBullet.Click Save near the top-left corner of the window to save your Data Asset.
Define A Data Table
Now that you have at least one Data Asset to populate a Data Table with, you can create the table!
Each row in the Data Table is one filled-in instance of your ItemData struct.
To create a Data Table, follow these steps:
In the Content Browser, in your
Datafolder, right-click a blank area and select Miscellaneous > Data Table.In the Pick Row Structure window, select ItemData (the
FItemDatastruct you defined inItemData.h), then click OK.Name the new table
DT_PickupData, then double-click it to open it.
Your Data Table is empty initially. However, you’ll see the properties you defined in FItemData as headings at the top of the table, and an additional column named Row Name.
To add your pickup item Data Asset as a row in the table, follow these steps:
In the toolbar near the top of the window, click Add to add a new row to your table.
The Data Table editor lists row entries in the top-left panel in the Data Table tab.
Double-click the
NewRowrow name and change it topickup_001(the ID of your Data Asset).You can use any
FNameas the Row Name; however, to make it easier to reference the row in code, make the Row Name the same as the Data Asset’s ID.In the Row Editor panel, enter the same values you set in the
DA_Pickup_001Data Asset into the ID, Item Type, and Item Text fields.Set the Item Base to your
DA_Pickup_001Data Asset.Save your Data Table.
And that’s it! Take a look at the diagram of Data-Driven Gameplay elements you’ve created in this step again and see how they’re all connected:
You’ve created a Data Table that gets its columns from your FItemData struct. You populated the table with a row that contains the data from the Consumable-type ItemDefinition Data Asset instance you’ve created and used the ItemBase pointer to reference the Data Asset itself. Last, your Data Asset instance got its properties from the parent UItemDefinition Data Asset class you created.
Next Up
In the next section, you’ll learn how to extend this item definition to create a custom pickup class and instantiate it in your level.
Create a Respawning Pickup Item
Learn how to use C++ to create custom pickups and initialize them in your level.
Complete Code
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Engine/DataTable.h"
#include "ItemData.generated.h"
class UItemDefinition;
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Engine/DataAsset.h"
#include "Data/ItemData.h"
#include "ItemDefinition.generated.h"
// Defines an item definition built from data table data.