Items / Dynamic Data

What is Dynamic Data?

Dynamic data is how we store and manage variables specific to our item instance. This data can be changed, added, and removed at runtime.

For example, let's take a look at the flashlight blueprint from the included demo equipment content. When we use the flashlight the battery drains. When the battery runs out we shut it off and the player can't turn it back on.

To support this, we need a way to track the remaining battery power for the specific flashlight the player has equipped. Dynamic data makes this possible because it lives on the item instance itself. As the flashlight is used, we decrement the stored battery value. When it reaches zero, the flashlight turns off.

In this setup, the flashlight blueprint handles what happens when the battery is empty, while the dynamic data system provides the storage for the battery's current value.

Get Dynamic Data

Call getItemInSlot on the AC_Inventory_Storage component the item is stored in. Provide the Slot ID of the item and this function will return details about what is currently in the Slot, including the Dynamic Data as a map variable. Attach a find node to this map variable and enter your dynamic data key to find the current value for it. The data will be returned to you as a string, from there you can convert it to whatever type you need it as.

Dynamic Data Array and Map Helper Functions

In most cases you will be provided the dynamic data as a map variable, however in some instances you will come across Dynamic Data in a string array format, where each element in this array is structured like key:value. To make it easier to work with you can use the getDynamicDataMap global helper function to convert this array to an easier to use map variable.

You can also convert the map to an array, for when an array is requested as an input, using the getDynamicDataArray function.

The reason we store and use the array version is for ease of network transportation in multiplayer games, since we can't replicate map variables. These helper functions make it easier to switch back and forth between each form.

Add, Change & Remove Dynamic Data

setDataInSlot on the AC_Inventory_Storage component is the function we use if want to add, change or remove dynamic data at runtime. To use you will need to provide the following details.

Slot the slot id in the storage component holding the item you wish to modify the data of.
Key the dynamic data key you wish to add, change or remove.
Value the dynamic data value you wish to associate with the key.

Using this function and its inputs you can perform the following actions on this item's dynamic data:

Add Data if the Key is not currently set, the Key and your Value will be added to the item.
Change Data if the Key is already set, the Value you provide will replace the current value.
Remove Data if the Key is valid, and your Value is empty the data will be removed. Setting the value as 0 does not count as empty and will not remove the data. The value must be completely blank to remove it.

Dynamic Data Defaults

Default dynamic data for an item is defined in the DT_Items data table, using the DynamicData map variable. You will have the opportunity to provide additional data when adding or spawning the item. This will let you override and append the item's default dynamic data.

Showing Dynamic Data on the Item Tooltips

Only dynamic data that you pick is shown on the tooltips, and you pick what is shown by inclusively adding your Dynamic Data Key to a list. You also define how the syntax is formatted for the value at the same time.

To Show and Customize the Syntax for the Value of a Dynamic Data Key

Navigate to, and add a row to the DT_DynamicDataDefinitions data table in the Blueprints/Variables/DataTables/DynamicData/ folder.

The Row Name you add to this table should match your Dynamic Data Key. The rest of the variables let you define how to display your value for this key. You will have the following fields to customize your syntax:

TextPositiveValue If the value is numeric and greater than 0 this text will be used.
TextNegativeValue If the value is numeric and less than 0 this text will be used.
TextOtherValue If the value is 0, or non-numeric, this is the text that will be used.
isPercentage? Set to true if when showing your value it is instead converted to a percentage. As an example a value of 0.42 will show as 42% and a value of 3.8 will show as 380%.
customSyntaxHandler If you need a bit more flexibility with coming up with your text you can also create a custom handler to render it. To use, create a new child blueprint that inherits from the BPO_DynamicDataDefinitionCustomSyntax file in the Blueprints/Objects/ folder. Then inside of your new blueprint override the getCustomSyntax function. The value you return from this function is the one that will be used. This getCustomSyntax function will provide you with a number of inputs to help you extrapolate whatever data you need to render your custom syntax.
If you use a custom handler the other fields in the definition will not be used. I included a few examples of custom syntax handlers in the Blueprints/Objects/DynamicDataCustomSyntax/ folder. I recommend taking a look at how these work before you construct your own.

In-line text variables for showing the actual Value

If you are using the default Text fields mentioned above you can utilize the following text variables in your string. These variables will be replaced with the proper values when rendered.

{v} will be replaced with the Value currently set to your dynamic data key.
{max} will be replaced with the Value currently set to your dynamic data key appended with _max. For example if you are working on the text for battery and want to show max battery value from the same item in this line you would use battery_max.
{vmaxp} will be replaced with the percentage of your current Value divided by the max value stored in your key appended with _max.
{s} if your value is not equal to 1 the letter s will be injected into all occurences of {s}, allowing you to pluralize any related text.

These in-line variables are only designed to work with the three Text options for the definition. If you are using the custom handler you will be provided with all the related data, and will need to hand place your actual values in your custom strings, as there is no parser for custom handlers.

Showing Different Colored Text (RichText)

This version includes rich text formatting for the dynamic data text shown on the item tooltips. The font formatting options are defined in the DT_DynamicDataStyles data table in the Blueprints/Variables/DataTables/DynamicData/ folder. To help you get started there are 7 included styles and a default. The "default" row is required by the Rich Text system and is not one you should remove. The 7 styles I've included are:

Row Name Usage Output
red <red>my red text</> my red text
green <green>my green text</> my green text
blue <blue>my blue text</> my blue text
gray <gray>my gray text</> my gray text
orange <orange>my orange text</> my orange text
yellow <yellow>my yellow text</> my yellow text
pink <pink>my pink text</> my pink text

It is important to note that you close all tags with </>. While it looks like HTML it really isn't, and you should not use the same keyword you used in the opener tag with the closing tag (it will not work). You also want to avoid embedding tags within other tags, as that does not work since it is a very simple parser. This is using Unreal's internal Rich Text system if you would like to learn more about the subject.

Also worth noting, you can change anything realted to the font for each row, not just the color. In the included examples gray is actually a slightly smaller font size then the others.

Showing hidden dynamic data using the debug mode in the Editor

While playing in the editor you can press the \ (backslash) key to toggle showing hidden dynamic data variables and values on the item tooltips. This will temporarily show you all the data on the item, for hidden variables it will show them on the tooltip like: (hidden) variable=value

This is intended to help you debug dynamic data, as when you start using some of the advanced use cases (as described below) you will want a way to validate the data ending up on the item.

This feature is only available while playing in the editor and is intentionally disabled in packaged builds. Hidden data is data your player shouldn't need to see, but you as a developer might need to see it while working on your project.

A good example of hidden data used in the demo world are the spoilable items. Enabling debug mode and hovering over one of these items will show a number of behind the scene variables that work together to handle tracking and expiring our spoilable item. The player does not care about this data, which is why it is hidden. In most cases the player only cares about the time remaining on a spoilable, and to come up with that value we need to track a few other variables. These other variables are the hidden ones. In the next part of this chapter you will learn about advanced handlers, which for our spoilables, is how we handle the functionality of expiring them, as well as render our custom widgets shown on the item and the tooltip to display the time remaining to the player.

Dynamic Data Helpers (for custom functionality)

Advanced Dynamic Data Helpers let us inject functionality into the system, to run at certain points, for specific items utilizing the Dynamic Data we are associated with.

In the previous version of this system we had Tap in Points which is the same idea, however the key difference is that in this version the handlers are only loaded when they are actually needed. In the last version they were always running, which was adding extra unncessary overhead. The goal was to decouple as much of the custom and unique logic from the base system.

The Spoilable Items system was specifically built to showcase the advanced functionality of this Dynamic Data System using these helpers. If you are interested in using this part of the dynamic data system I recommend that you check out that page of the docs even if you are not using spoilables in your game. It will give you a breakdown of how it utilizes dynamic data to handle expiring items.

Dynamic Data Helpers are an advanced feature that is intended for use by those who have strong dev skills. If you are new to development in Unreal you may have trouble using these.

Create an Advanced Dynamic Data Helper

  1. Navigate to, and open, DT_DynamicDataHelpers in the Blueprints/Variables/DataTables/DynamicData/ folder.
  2. Add a new row, setting the row name to an exact match of your Dynamic Data Key.
  3. Use the variables (details of each below) in the data table to define your custom helpers.
Events trigger at certain lifecycle hooks, and when they do, they run our defined custom code.
Available lifecycle hooks for Events
onFirstLoad The first time your item touches a storage component this logic will run. Internally this is tracked using the dynamic data key .init. If this dynamic data key is found on the item the system knows it already ran the first load logic and will instead run the onTransfer event.
onTransfer Whenever the item changes slots, within the current, or to/from a storage component this logic will run.
onPing Run logic on a loop at a duration defined by the DynamicDataPingRange float variable set on the AC_InventorySystem added to the player controller. This value is passed to the global game state component. By default this value is set to 3 seconds.
This ping is turned on and off automatically based on if items are in the storage that are actually using the ping event. All items in the storage are checked on load, and then the items in slots that change are checked automatically (after the change).

Define the custom logic

You can find the base class, BPO_DynamicDataStateEvent in the Blueprints/Objects/ folder. The included examples can be found in the DynamicDataStatEvents/ subfolder.

Create a child of BPO_DynamicDataStateEvent and link it to your Event in the data table.

Then within the helper you created, override the function related to the lifecycle hook (listed above).

You can use one helper to manage different lifecycle hooks, but just remember they are re constructed for each use, and the data you save within will not persist to the other events.


ExtraData Variable

You also have the ability to provide additional custom data using the ExtraData array. If you are reusing a helper and need to send some customized data with the particular instance of it being triggered for the dynamic data key you can define it here. There is no parser included for extra data, you will need to handle that yourself on the inside.

ItemWidgets Widgets that are appended to all instances of UI_Item containing this dynamic data key (defined by row name). For each widget that you add to the list you will want to also integrate the BPI_DynamicDataHelperWidgets blueprint interface into it. The interfaced onItem function will be called, and it will contain a reference to the UI_Item it is being attached to. You can use this reference to get more data about the item.
TooltipWidgets Widgets that are appended to all instances of UI_ItemTooltip containing this dynamic data key (defined by row name). For each widget that you add to the list you will want to also integrate the BPI_DynamicDataHelperWidgets blueprint interface into it. The interfaced onTooltip function will be called, and it will contain a reference to the UI_ItemTooltip it is being attached to. You can use this reference to get more data about the item.
Keep in mind the UI_Item reference inside the tooltip is only valid if the tooltip was spawned from an actual UI_Item. If it was spawned from another helper (like ingredients in crafting window), this internal variable will not be available. In these cases it is for a not yet existing item, and the item row name is provided instead. You can use the getItem global function to get the item details.
I can't emphasize this enough but if you are going to make your own dynamic data helpers you really should spend some time looking over the Spoilable Items page. It was built as a high level example of this dynamic data system, utilizing these helpers.

Dynamic Data Limitations

Dynamic Data lives on the instance of the item, so if you modify the dynamic data in the data table all current items that already exist in your world will not get that update. You will need to remove the old and spawn new ones. Dropping the item and picking it back up at runtime does not count.

Keys and values are stored as strings.

Dynamic Data on stackable items will override data when merged together. The stack is not keeping track of each individual item in it, but rather shares the data with all in the stack.