How do i work with data tables or structs in c++? Should i use struct or datatable in this context? Or both?

Im very confused.

imagem

I have different types of batallions. Each batallion has its own specific type. Spearmen, swordsmen etc…
Spearmen has different attack than swordsmen.

In blueprints this is very easy to do. I get the Row using the node in the image. And quickly get all the values i need.

But in C++ im very confused on how to do this, even though i watched a bunch of tutorials.

For example this one, which is very good. But super confusing and too complicated.
Eventually things start falling a part and im unable to make it work.

Is there a step by step tutorial on how to do this?

What i need to do is:
1- Create a C++ struct UnitData with all the properties needed, Attack, defense etc…
2- When the game starts assign a unit type to my Actor, on spawn (if it is being spawned). For example, spearmen.
3- Need to get all the variables of the UnitData struct, row “spearmen” out of it, and into my struct inside my actor.
4- Need to be able to access these specific variables inside each struct in each actor. So that when im fighting i know what my attack and defense is.

Issue 1: I dont understand when and how should my struct array or TMap be initialized. When do i create the list of different unit types and their data? At begin play?

Issue 2: I asked chatgpt and it tells me that i can simply create a struct and that i dont need datatables. So according to chatgpt, i can make an array at the start that initializes every type of unit, with its own defense/attack variables etc… But must be done only once, and store them in a TMap<FString, Struct> where the FString is the name of the unit type, and the struct is the corresponding initialized struct of the unit type.
2.1 Where do i do this so that it only happens once? Chat gpt made it in all spawned units, with one if statement to check if its already initialized. Which it doesnt look right.
2.2 After having initialized them how do i store them in each corresponding struct in each unit? Should i do this when ? At begin play?

I created the struct, down below. How can i create a list of them for each specific unit type? Then store them in each corresponding unit?


#pragma once

#include "CoreMinimal.h"
#include "UnitDataStruct.generated.h"

USTRUCT(BlueprintType)
struct K2K_TEST_CPP_API FUnitDataStruct
{
public:
	GENERATED_BODY()
	UPROPERTY(BlueprintReadWrite, EditAnywhere)
	int Health;
	UPROPERTY(BlueprintReadWrite, EditAnywhere)
	int Attack;
	UPROPERTY(BlueprintReadWrite, EditAnywhere)
	int Defense;
	FUnitDataStruct();
	~FUnitDataStruct();
};

You can initialize your struct array or TMap in the constructor of your GameMode or GameInstance class. This ensures that it is only done once when the game starts.

You can create a function in your GameMode or GameInstance class that initializes the struct array or TMap. You can then call this function in the constructor of your GameMode or GameInstance class.

Once you have initialized the struct array or TMap, you can store them in each corresponding unit in your Spawn function. For example, you can check the unit type and then assign the corresponding struct to the unit’s struct variable.

So in your GameInstance or GameMode class you can create a function like this:

void AGameInstance::InitializeUnitData()
{
    TMap<FString, FUnitDataStruct> UnitData;
    UnitData.Add("Spearmen", FUnitDataStruct{100, 20, 10});
    UnitData.Add("Swordsmen", FUnitDataStruct{120, 25, 15});
    UnitData.Add("Archers", FUnitDataStruct{80, 15, 5});
    // And so on for each unit type
}

Then in your constructor of your GameInstance or GameMode:

AGameInstance::AGameInstance()
{
    InitializeUnitData();
}

In your unit’s spawn function:

void AUnit::BeginPlay()
{
    Super::BeginPlay();

    FString UnitType = "Spearm

Please let me know if this works for you!

1 Like

The idea seems to be good. I was able to initialize it in the GameModeBase.cpp.
I created a project with the name Test_Data_Table.


#pragma once

#include "CoreMinimal.h"
#include "GameFramework/GameModeBase.h"
#include "Test_Data_TableGameModeBase.generated.h"

/**
 * 
 */
USTRUCT(BlueprintType)
struct FUnitDataStruct
{
public:
	GENERATED_BODY()
		int32 Health;
};


UCLASS()
class TEST_DATA_TABLE_API ATest_Data_TableGameModeBase : public AGameModeBase
{
	GENERATED_BODY()
		ATest_Data_TableGameModeBase();
	public:
	UPROPERTY(EditAnywhere)
		TMap<FString, FUnitDataStruct> UnitData;
};

Until here its all good.
I created a simple struct with just one int32 Health.

#include "Test_Data_TableGameModeBase.h"

ATest_Data_TableGameModeBase::ATest_Data_TableGameModeBase() {


	FUnitDataStruct Spearman;
	Spearman.Health = 14;
	UnitData.Add("Spearman", Spearman);

	FUnitDataStruct Swordsman;
	Spearman.Health = 20;
	UnitData.Add("Swordsman", Swordsman);

	UnitData.Add("Archer", FUnitDataStruct{8}); //elliaswick way, faster.

}

I initialize the different units with different health

Then in my actor MyActor.h i try to make a variable FUnitDataStruct CurrentUnitData to store the data that corresponds to this specific unit.
But it doest allow that. And i dont know where to place it.


#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "MyActor.generated.h"

UCLASS()
class TEST_DATA_TABLE_API AMyActor : public AActor
{
	GENERATED_BODY()
	
public:	
	// Sets default values for this actor's properties
	AMyActor();
	FUnitDataStruct* CurrentUnitData;

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



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

};

Edit: I tried to include in my actor the #include “Test_Data_TableGameModeBase.h” So maybe that was necessary to include the struct thats in that header file. Though something feels fishy about including in my MyActor.h the GameModBase, but it compiled now.

Yes. I dont seem to be able to access GameModBase.h variable TMap that has the string and the FUnitDataStruct in MyActor.h. Doesnt work:

void AMyActor::BeginPlay()
{
	Super::BeginPlay();
	CurrentUnitData = UnitDate[Spearman];

}