Differences in Data Assets, Data Tables and Structures (Overall Hierarchies in systems)

Context:
I am trying to create a basic inventory system that has 5 slots and can pick up the BP_Item one by one (no multiples, just single items), and then drops it one by one too. I went through a lot of videos and forum questions around the same, and figured out a basic Actor Component that can have the basic functions of adding and removing the item, using Blueprint Interfaces to try and remove casting.
The Line Trace helps pickup items and can trigger the transfer of data across from BP_Item into the inventory ‘actor component’
My questions are around how Data is pulled in from Data Assets, Structures or Data Tables, or any combinations of these. I want to learn a good setup of using data throughout (I am not a coder, so that’s why intending to create a toolkit for myself that I can expand out from)
Questions are around the theory, nodes and variable usage across. Data Structures and Data Table is secondary, but information for using that in the same context will also be helpful.
I keep making system diagrams as documentation for all systems I am learning, and in this one I want to understand the pink/red circled areas.


The yellow parts (DATA) are just shown in general, they are not connecting specifically to anything on the left yet.

Some Questions to set the context better:

  1. As the data is read only, if I am spawning copies of BP_Item using different data assets, in theory how does that work? And what nodes are used to get that? Is it something like the save game structure where I can ‘get game data’ and then ‘save game data’ by overlapping the older variable data in the save game file? (So like it comes in, and then goes out another end?)

  2. If I am using Data Assets as my primary method, is there a way to save entire data assets in arrays when picking it up. I found blueprints of how Data Structures can be picked up and put into inventory slots, so I am guessing the same can be done for Data Assets?

  3. There are 10 BP_Item copies that are lying out in the open, which have data from Data Asset 1, 2 or 3. So how to spawn these 10 while attaching them to different Data Assets, using nodes (let’s say in the Game Mode BP)? Are there specific nodes for that? (Same as Question 1)

  4. Am I thinking in a wrong way about something here? If so please do correct me.

Thanks :slight_smile:

Data Assets, Tables simply put are organized data containers. I treat them like a database.

Items are assigned an ID that’s used to lookup the data. For example I use GamePlay Tags to define what a “thing” is. My interaction system gets the ID and reads it to determine the Type of item, what data table to use and the specific item to look up.

WorldItem.Ammo.762 Tells my interaction system the Actor is a “Loot Item”. That’s it Ammo and the the caliber is 7.62

For specific information (UI icon, mesh, specs etc) I’d use data table DT_Ammo grabbing row name 762.

The Gameplay Tag can be predefined in a dedicated actor class, or runtime assigned on spawn of a dynamic actor.

You can then use Begin Play on the actor to pull the data needed… Build Config.


For UI data you can either store it in the actor class, or look it up as needed. For pick up items I lean toward doing ID look ups on interaction vs packing thousands of actors with “I might need” data. The data is only relevant if you interact, otherwise its memory bloat.

2 Likes

My Inventory stores flat data (Item quantities) for bagged type items (Ammo, meds, grenades etc).
For slotted items I use a named actor object reference.

Every slot that uses an Actor obj ref is a spawned and attached actor. If not empty it’s a visible actor attached to the characters. Either in hand or on the body or other gear based attached actor.

All the “Data” items are broke down to integer quantities. This struct contains all backpack stored items. There’s no need for the primary data inventory to be heavy.

Each base type of item (Meds, Ammo, Grenades etc) use a singular function to add and remove quantities.

For UI aspects the inventory system has a series of functions that get and pack a struct with the needed data. It’s essentially a copy of the simple data struct, but adds elements for icons and other data. The inventory system manages the data, but only the UI uses it.

e.g.
Then 0: Meds →

  • Medkits (2), (icon, X, Y, Z data)
  • FirstAid (1), (icon, X, Y, Z data)
  • Bandages (5), (icon, X, Y, Z data)
  • Caffeine (1), (icon, X, Y, Z data)
  • Adrenaline (2), (icon, X, Y, Z data)

At the moment 100% of my item data is Data Table based. It’s faster and easier to work with in regards to adding and refactoring. If I add a new item I do not need to create an asset. If I want to modify specs of any given actor or type of item I do not need to open and edit 15 gazillion actors.

Data tables can be runtime edited. This makes it possible to make changes on a live branch without having to repackage the game… essentially pushing a patch.

Downsides to DT’s is the seek time to read data. DT’s work very similar to databases like MySQL/Oracle etc. The larger they are (rows and columns) the slower they get.

Breaking down your data into multiple DT’s will make lookups faster, but at the cost of human management.

For example I have 5 different med items. Very small DT footprint. Weapons on the other hand sits around 120. If/when performance takes a hit here. I can break that table down into sub groups.
AR, SR, DMR, LMG, SMG, Pistol, Shotgun, Misc… 8 data tables to manage vs 1 vs 120 data assets.

As is there are also DT’s for WeaponBallistics, WeaponRecoil, WeaponDmg, WeaponFX etc.

2 Likes

Took me a while to understand this system, but I’m finally there. Your solution about using more data tables instead of one big one is what I am trying out. I was finally able to spawn items replicating the same blueprint but taking data from the data tables, and am now figuring out the actor object ref inventory system.
I also explored Gameplay Tags, am trying to figure out my use case for that system right now.

Thank you for this!! :grinning_face:

Because they are simple Actor Object References, not of specific class type, you have to use BP Interfaces to call events, pull data etc. There’s zero casting.

I’m using each of the classes inherited base nodes to access the primary class (playerstate, controller, pawn etc). From those nodes I call BPI events/functions.

Use the C++ Implementation. It doesn’t require a cast to get the tags.

@macwanna Let me know if you want a demo vid explaining the interface usage. It is a bit complex.

1 Like

I was able to pick up actor object references without casting or tags so far. The picking up part is on a line trace and the inventory system is an actor component on the BP_Player. Also my BP_Player is a pawn and the rest are all actors, so no character components present.

Was trying to implement tags as a check before it picks up because it would pick up anything from the entire level.
Would love some info about how the gameplay tags system works - right now I have gameplay tags in the manager system and when the item is spawned I am able to ‘Add Gameplay Tag’, but I feel I am doing something wrong there (because when the item is spawned, the static mesh is present, including all the data I imported using data struct), but I can’t find the Gameplay Tag being implemented other than what I already put in the data struct.
Also you have a lotttt of Blueprint Interfaces. Do the contexts change based on for example - The ‘BPI_Controller vs. BPI_Interact or BPI_Inventory or BPI_Item’?

Thanks!

My setup uses the C++ implementation so I can use the Get Owned Gameplay Tags Interface.

In your Build.cs file you have to add the dependency module GameplayTags

header

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "GameplayTagContainer.h"
#include "GameplayTagAssetInterface.h"
#include "BaseGamePlayTags.generated.h"


UCLASS()
class SURROGATE_V52_API ABaseGamePlayTags : public AActor, public IGameplayTagAssetInterface
{
	GENERATED_BODY()
	
public:	
	// Sets default values for this actor's properties
	ABaseGamePlayTags();

	// Gameplay Tag Container variable, Exposed to BP
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "GameplayTags")
		FGameplayTagContainer ConfigTags;

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

public:	
	// Called every frame
	virtual void Tick(float DeltaTime) override;

	// Get GameplayTags
	virtual void GetOwnedGameplayTags(FGameplayTagContainer& TagContainer) const override { TagContainer = ConfigTags; return; }

};

CPP

// Fill out your copyright notice in the Description page of Project Settings.


#include "BaseGamePlayTags.h"

// Sets default values
ABaseGamePlayTags::ABaseGamePlayTags()
{
 	// Set this actor to call Tick() every frame.
 	// You can turn this off to improve performance if you do not need it.

	PrimaryActorTick.bCanEverTick = true;

}

// Called when the game starts or when spawned
void ABaseGamePlayTags::BeginPlay()
{
	Super::BeginPlay();
	
}

// Called every frame
void ABaseGamePlayTags::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

}

I then create a base class in BP using the C++ class as parent. All Items derive from that class.

Dynamically setting is a matter of call Set Config Tags