Dynamic Data Type Selection

I have been having trouble figuring out exactly what I need to ask in reference to code rather than design, but I think this is the closest to describing what I’m looking for:
How can you allow selection from a list of specified data types, which provides a variable of that type?

I’m trying to create a DataAsset for creating conditions. For each condition, you select a subject with a data type for the parameter, a conditional for how to read that parameter, then either a value to compare to or a second subject with data type(does not have to be of same type) to compare to.

I’m not necessarily asking about flags for hiding/showing, as I have heard of others discussing metadata flags, but rather for help with designing a flexible framework for my data.

Examples:

  • Enemy strength > Self speed

    • Subject + DataType + Conditional + Subject + DataType

      • Which would provide an enum representing the subject, a variable to capture the relevant data, and an object/struct which would provide a means of comparison, (repeat first two)

  • Enemy equipment has shield

    • Subject + DataType + Conditional + value (of DataType selected)

The closest framework I have to achieving this is:

  • Enum EDataType to represent every data type available to conditions

  • Static TMap with key of EDataType, value of FDataTypeStruct

  • For individual entries I’d provide a derived struct which contains a variable for the type we desire

Unfortunately when evaluating these conditions, I’d have to figure out which struct I’m looking at in order to find the relevant data, and then access the method to retrieve that data (specific but not unique to the struct).
I’d prefer not having to create multiple structs for every data type, or even potentially creating structs to account for deviations, though I’m not sure it’s possible to create this flexibility without hardcoding it in.
If possible I was going to use an overloaded Get(datatype& param), so I’d simply need to provide a datatype to that Get(), it would select the correct overload and set the value of that OUT param, which would be part of a condition struct/object to maintain relevancy to other data in condition for the duration of the evaluation.

I was going to try and copy how blueprints create new variables (since they provide access to all types and then a value of that type once compiled), but blueprint scripts are contained in a virtual machine and I don’t know how/if I can access the actual code.

If there is a simple solution to this that I overlooked in my ignorance(self taught so I missed a lot of basics and am still figuring them out, sorry), please slap me with it. I’ve been wracking my brain on this for the past 3 days after I took a week trying to figure out an abstract design of the framework, so my brain and I may be a bit frizzled.

I have tried stepping through the process from when conditions are gathered to how they’ll be evaluated:

  • We create a Objective DataAsset

    • We add a condition

      • ESubject is target

      • FDataType is Health

      • EConditional is LessThan

      • ESubject is self

      • FDataType is Health

  • …(Irrelevant processes of moving objects to correct location)

  • Evaluating one of the potential targets of Objective

    • On target, access data object

      • Get(FDatatype->Enum, &FDataType->Value)

        • Can’t do this if the parent object doesn’t have a value type within

        • Can’t do a return function since return type would have to be covariant

      	</s>
      

I don’t know if this is a problem every dev would face, or if in my ignorance I don’t know the correct design to address this situation.
Initially I wanted to used typedefs for DataTypes, so we could have a value and the type in one location, and could compare Health<float> and Strength<float> and recognize them as different types and thus incompatible. But I found typedefs are incompatible with UHT and thus blueprints, so I prepared to manually access data from an object on characters.
Then I hoped to use an overloaded Get() with an OUT param that would manually set the value of the OUT param based on the Enum, but in that case I would require the parent struct to have a value.
So it lead me to this:

  • Get(TSubclassOf<>, ParentClass* derivedObject)

    • Cast derived object to type in parameter in implementation (since we know which class to cast object to)

    • Now we have a derived object with known type

I am about to begin testing it. The downside is I now have to use UObjects for every condition (which have more data than an empty struct), and I have to create an Object to represent every Data Type Enum, but so far this is the only design I’ve been able to come up with to satisfy the framework. Assuming it works.

Is this reasonable?

If you’re trying to create rpg formulas you should learn how to use bitmask enums.

Well, I guess trying to use TSubclassOf<> is too ambiguous for the compiler. So I’ve finally reached this:



void Get(Class1* obj){}
void Get(Class2* obj){}

void Test(EDataType type, UObject* obj)
{ switch (type)
{
  [INDENT=2]case EDataType::STRENGTH:[/INDENT]
  [INDENT=3]Get(Cast<Class1>(obj));[/INDENT]
  [INDENT=2]break;
case EDataType::HEALTH:[/INDENT]
  [INDENT=3]Get(Cast<Class2>(obj));[/INDENT]
  [INDENT=2]break;[/INDENT]
  }
 }


It’s ugly and requires maintenance, but it gets the job done and I can’t come up with a more elegant solution.

I’ve never even heard of those, so I’ll have to dig into that, thank you. Downside of self-teaching :rolleyes:

Oh man - you’re gonna love them!

Oh, what a geek I have become. I guess I should have expected this as a child with my love of Legos