Download

I want to do data driven design but I feel overly constrained on dynamic typing.

I’ve extensively looked at data assets, data tables, and lots of other things already and just feeling really frustrated.

I want a simple to define set of items with high flexibility.

Let’s say I have a sword.
I could create a struct that has “name”, “icon”, “damage”
Then I could create a data table that has all sorts of different types of swords.

But I also have some item, french bread
I could create a food struct that has “name”, “icon”, “hungerFill”
I could create a variety of breads.

But what if I want to make the some french bread also be equipped and be a weapon with a damage of 1. A bread sword!

Well I could just create a megaBaseItem
I could create a struct that has “name”, “icon”, “damage”, “hungerFill”, and “everythingPossible”
but now I have tons of items with ‘blank fields’ or I have to deal with what it means an item to recognize 0 or -1 as dummy value.
This feels wrong too.

With making items in ue4. I totally get I could have ‘components’ for ‘sword component’ and ‘food component’ and it makes perfect sense on how to properly separate these at a UActor type level.

But I have no idea how to handle this from a data driven side of the world.

I was thinking of just having a json file that I parse things manually myself because and handle the ‘dynamicness’ myself.

The problem with this, is I feel like I start to lose some benefit of having ‘typing’ and that I’d still love to be able to do “someItem.damage”
Even if that means doing something like “wildcardItem.As<Sword>().getDamage()” and “wildcardItem.As<Food>().getHungerFill()”.
But I’d also be interested in getting something like ItemDataTable.get(“FrenchBread”).As<Sword>().getDamage()
I suppose I could always create some ‘dummyClass’ whenever you do ItemDataTable.get(something) and then have regular interfaces on object. Sorta as a short lived object maybe, even just as a proxy data object for the actual UActor.

Ideally I think I’d like to define the item as



{
  "name": "FrenchBread",
  "icon": "Texture2D'/Game/Icons/Textures/FrenchBread.FrenchBread'",
  "components": {
      "meleeComponent": {
          "damage": 1
      },
      "foodComponent": {
          "hungerFill": 5
          // someMissingField that defaults to some defined constant for foodComponent someHow?
      }
]
}

But I wasn’t sure if this was great either.

Any thoughts/suggestions?

You need to use a couple of ideas, I think.

One is of classes and subclasses. The top class could be Game-Item, and the sub classes of that Food, Weapon, Edible-Weapon etc. Anything that needs to be common goes in higher classes, everything specific goes in the lower classes.

Also, you could get more generc with those attributes. Instead of having specific attributes, why can’t each item have a list of:

  • attribute-name, attribute-value
  • attribute-name, attribute-value
  • attribute-name, attribute-value

and you fill them in later…

As clockwork said thinking class/subclass is a good path to go with. For our game we created a BP class for weapons that become active upon position. Within the master class is everything needed to support the item such as animation sounds, specs. In essence it makes all player animation weapon or item driven and can included French bread as a usable item as equal to a crowbar.

Hi namrog84,

My approach would be to have a DataAsset for Items, then an Abstract DataAsset for ItemData, and for descriptor of item that an item can have I would have a subclass of ItemData and a USceneComponent/UActorComponent that would be created from the list of descriptors that the Item has and that would link to the proper ItemData.

Something like this:


UCLASS()
class DATADRIVENTEST_API UMyItem : public UPrimaryDataAsset
{
    GENERATED_BODY()

public:
    UPROPERTY(Instanced, EditDefaultsOnly, Category = "Item")
    TArray<UMyItemData*> ItemAttributes;

    UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Display")
    FText Name;

    UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Display")
    TSoftObjectPtr<UTexture2D> Icon;
};


UCLASS(EditInlineNew, Abstract, BlueprintType)
class DATADRIVENTEST_API UMyItemData : public UPrimaryDataAsset
{
    GENERATED_BODY()

};


UCLASS()
class DATADRIVENTEST_API UMyFoodData : public UMyItemData
{
    GENERATED_BODY()

public:
    UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Attributes")
    float HungerFill;
};


UCLASS()
class DATADRIVENTEST_API UMyWeaponData : public UMyItemData
{
    GENERATED_BODY()

public:
    UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Attributes")
    float Damage;

};

That would result in an asset like the following one:

Notice that by making ItemAttributes Instanced means that it can create a new one instead of referencing an Asset, so you don’t have to create an asset for every ItemData, and by specifying the ItemData as EditInlineNew you can edit directly in the Item DataAsset. And because it is Abstract, you can’t create bare ItemData but only subclasses of it. Once you have defined the data that data would be referenced in the proper components of the actor that needs that data. And you can access easily to it by looking for the component and you know that the component is going to hold that data, like:



class DATADRIVENTEST_API UMyFoodComponent : public USceneComponent
{
    GENERATED_BODY()

public:    
    UPROPERTY(BlueprintReadOnly, Category = "Items")
    UMyFoodData* FoodData;
...


Thanks Mario, that’s brilliant. I’ve tried to do nested things before with other things and never quite been able to achieve what you showed there. Specifically with the polymorphic behavior from TArray<UMyItemData*> ItemAttributes; combined with the ease of use of the UE editor to modify the things!

Incredible! Thanks again so much!