Download

Data Driven Data Tables and structs with overriding component defaults

I am trying to take a data driven approach to designing my objects.
I’d like to be able to make new objects with combining behaviors(components) and attributes (modifying those components)

My struct and/or data table would be defined as

For example


struct FS_ComponentInfo{
        TMap<FName, FString> Attributes;
};
struct FS_ItemInfo{
    FName ItemID;
    TMap<FName, FS_ComponentInfo> Components;
};

So then I could define a new item perhaps by


SomeDataTable:
{
    ItemID = "StinkySword"
    Components = {
        "WeaponComponent": {
            MinDamage= "3",
            MaxDamage= "7"
        },
        "SmellyComponent": {
            Smell="50",
            Radius="10"
        }
    }
}

I don’t want to have to define every attribute in a given component(for example, I left the field Speed in WeaponComponent as the default value), and since it needs to be super flexible. I have to stay very flexible on types. Thus things must be strings from user, right?
Is there a better way to do this?

I started getting stuck on the item generation part.
for example in AddItem(“StinkySword”)
I’d find the item in the data table, add the components which are matched to actual components I’ve implemented. No problem so far.

With using UProperty, FindPropertyByName, and SetPropertyValue, I believe I can look up each attribute from my S_ComponentInfo and then override any defaults for corresponding names. The conversion from string to respective parts aren’t too bad.

However I am running into issues in that passing a ustruct around works fine in C++, but trying to maintain C++/Blueprint interopability starts to run into some issues. And I don’t want to create the full weight of all the components of the object when its just in an inventory or something.

Its proving far more difficult to do what I felt like should be a fairly easy thing to do. However, I feel like I keep running into road blocks and now am reevaluating if anyone has any suggestions, references, links, help, criticism or whatever.

Thanks!

tl;dr;
I want to have a defined (csv/json) txt that I can load up (via datatable?) and dynamically add corresponding components, and override a subset of their fields based upon whatever happened to be defined. Is there a better/easier way to do this?

I have made some success in this space.

For those who are interested in doing something similiar.


USTRUCT(Blueprintable)
struct MYGAMEDERP_API FS_ComponentInfo :  public FTableRowBase
{
    GENERATED_USTRUCT_BODY()
public:
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "CategoryTBD")
    TMap<FName, FString> Attributes;
};

USTRUCT(BLueprintType)
struct MYGAMEDERP_API FS_ItemInfo : public FTableRowBase
{
    GENERATED_USTRUCT_BODY()
public:
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "CategoryTBD")
    FName ItemID;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "CategoryTBD")
        TMap<FName, FS_ComponentInfo> Components;
};


    UFUNCTION(BlueprintCallable, Category = "DataTableHelperFunction")
    static void UpdateComponentAttributes(UObject* Target, UDataTable * MasterItemTable, FName ItemToGen);


void UDataTableHelperFunctions::UpdateComponentAttributes(UObject* TargetComponent, UDataTable* MasterItemTable, FName ItemToGen)
{
    FName ComponentName = TargetComponent->GetFName();
    if (MasterItemTable) {
        FS_ItemInfo* ItemInfo= MasterItemTable->FindRow<FS_ItemInfo>(ItemToGen, FString("MerpDerp"));
        if (ItemInfo) {
            if (ItemInfo->Components.Contains(ComponentName))
            {
                FS_ComponentInfo ComponentInfo = ItemInfo->Components[ComponentName];

                for (auto Attribute = ComponentInfo.Attributes.CreateIterator(); Attribute; ++Attribute)
                {
                    UProperty * Property = TargetComponent->GetClass()->FindPropertyByName(Attribute.Key());

                    void* ValuePtr = Property->ContainerPtrToValuePtr<void>(TargetComponent);
                    auto AttributeValue = *Attribute.Value();

                    if (UNumericProperty *NumericProperty = Cast<UNumericProperty>(Property))
                    {
                        NumericProperty->SetNumericPropertyValueFromString(ValuePtr, AttributeValue);
                    }
                    if (UBoolProperty* BoolProperty = Cast<UBoolProperty>(Property))
                    {
                        FString bStr = AttributeValue;
                        if (bStr.StartsWith("T", ESearchCase::IgnoreCase)) {
                            BoolProperty->SetPropertyValue(ValuePtr, true);
                        }
                        else if (bStr.StartsWith("F", ESearchCase::IgnoreCase)) {
                            BoolProperty->SetPropertyValue(ValuePtr, false);
                        }
                    }
                    if (UNameProperty* NameProperty = Cast<UNameProperty>(Property))
                    {
                        NameProperty->SetPropertyValue(ValuePtr, AttributeValue);
                    }
                    if (UStrProperty* StringProperty = Cast<UStrProperty>(Property))
                    {
                        StringProperty->SetPropertyValue(ValuePtr, AttributeValue);
                    }
                    // Add more supported types here.
                }
            }
        }
    }
}


Example DataTable Json

    {
        "Name": "SmellySword",
        "ItemID": "SmellySword",
        "Components":
        {
            "BP_InventoryItemComp":
            {
                "Attributes":
                {
                    "Weight": "3.442",
                    "Value": "456",
                    "Name": "Sword o Stank"
                }
            },
            "BP_WeaponComp":
            {
                "Attributes":
                {
                    "MaxDamage": "9001"
                }
            }
        }
    }
]

a.png

It appears to work with no issue on the attached components.

NextSteps:

  1. Add dynamic addition of components to a base Item, if referenced in master item list component list before modifying attributes.
  2. Add support for multiple definitions for layered overriding. e.g. define an item to have 2 definitions, so that 1 is “ShortSword” and all the components that come along with it, then I can define an additional ItemDefinition ontop of that one for only the parts that differentiate it over the standard item.