Download

Driving Data with Excel - Inventory

Hi Everyone,

I have successfully retrieved a datarow from an excel file following this post here: https://forums.unrealengine.com/showthread.php?12572-Driving-Gameplay-with-Data-from-Excel
And using the following function:


FGameObjectLookupTable* GOLookupRow = GameObjectLookupTable->FindRow<FGameObjectLookupTable>(
		*FString::Printf(
		TEXT("%d"),
		parentRecipeId),
		ContextString
		);

I wanted to know if there was a way to return multiple rows from a given ID? I’m wanting to use the excel file to make a list of all the materials that encompass my item for crafting. Such as an axe will have a rock, wood, and rope. I’m wanting to return the rock, wood, and rope when I give the axe’s ID. Can I return multiple rows or should I make a column and return all the materials ID in that one column and parse them such as 01;02;03?

Any thoughts on this would be appreciated.

Thanks!

You can return multiple rows by adding all the rows found to an array.

Is there a pre-defined function that returns an array of rows based off an id or name as I don’t see one in the API? I saw there was a function for GetTableData() that turns the CSV datatable to an array. Are you talking about different way? I appreciate some more detail on this matter.

Thanks

The row ids have to be unique, so you can’t return multiple rows for a single id. Nor do data tables support mapping a single csv value to an array property. I think you have two options: parse a string as you said, or reference nested data tables. It should be possible to set up a spreadsheet which automatically generates a data table csv for the materials for each of your items, and then references those tables in your item table.

This reminds me of my SQL database dev experiences :slight_smile:

What you’re looking for is a data record which has a one to many relationship.

The solution is to create an intermediate table which does the mappings.

Tmp01.png

Then, you have to query a few tables at once to create your internal data structure. Since you have a one to many relationship, you’ll certainly have a TArray<> somewhere in your code which gets populated by objects pulled from another table.

This would be similar to a JOIN in SQL, or a complicated WHERE conditional.

@Slayemin That is basically what I am trying to setup yeah, was a one to many relationship. I thought of it as well as a join. But as everyone here is informing me, that isn’t possible with one excel file which is the design I was trying to aim for instead of using a database.

I’m currently doing this. No need for a database. Just use multiple excel files. I do the joining in code, something like this:

create a TMap, which is a key value pair.

The key is the ID of your object.
The value is a TArray of keys for your other objects.

When you build your TMap, on the first occurence of an object, you create a new key value pair. If the map already contains your key, just insert it into the array of ID’s.

If you need to look up the inventory for an object, all you need is that objects ID. That’s your lookup into the map. Then you get a bunch of ID’s for inventory items. You can then match these ID’s to the inventory items rows in O(1) time.

Here’s my code:



UMyGameInstance::UMyGameInstance(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
{
	ConstructorHelpers::FObjectFinder<UDataTable> Units(TEXT("/Game/Assets/GameData/UnitData"));
	ConstructorHelpers::FObjectFinder<UDataTable> UnitTags(TEXT("/Game/Assets/GameData/UnitTagData"));
	ConstructorHelpers::FObjectFinder<UDataTable> Skills(TEXT("/Game/Assets/GameData/Skills"));
	ConstructorHelpers::FObjectFinder<UDataTable> Tags(TEXT("/Game/Assets/GameData/Tags"));

	DT_Units = Units.Object;
	UDataTable* DT_UnitTags = UnitTags.Object;
	DT_Skills = Skills.Object;
	DT_Tags = Tags.Object;

	//we're gonna build a map which contains a unit ID to tag ID lookup, which is going to be a (1:*) relationship
	if (DT_UnitTags != NULL)
	{
		TArray<TArray<FString>> Data = DT_UnitTags->GetTableData();		//the first record is the row descriptors, so we skip it
		for (int a = 1; a < Data.Num(); a++)
		{
			int32 UnitID = FCString::Atoi(*Data[a][1]);	//unit ID
			int32 TagID = FCString::Atoi(*Data[a][2]);	//Tag ID

			if (UnitTagMap.Contains(UnitID))
			{
				UnitTagMap[UnitID].Add(TagID);
			}
			else
			{
				TArray<int32> NewArray;
				NewArray.Add(TagID);
				UnitTagMap.Add(UnitID, NewArray);
			}
		}
	}
}


Also, it’s possible to create your own hash table based on the “RowData” variable within the DataTable class. It’s publicly available, but you have to do some tricky pointer dereferencing and casting to get at the data (see FindRow<T>() implementation).

You could also use the GetTableData() method, but that will return a 2D array of strings, so you’d have to cast those strings to your data types. Not as clean as an array of structs, right? :stuck_out_tongue: I was thinking I’d do an engine mod to do this, but I’ve been a bit pressed for time. Maybe on a weekend :slight_smile:

@Slayemin Sorry for the late reply. This is definitely helpful. Thank you very much

Kamrann I was talking about using a for loop and comparison on each row and moving them into an array. It doesn’t matter though the other advice is better.