Can meta = (GetOptions=...) and ValidEnumValues be made more dynamic / maintainable?

1) Limitation of GetOptions being effectively “static” per property declaration

I’m using UPROPERTY’s meta = (GetOptions = …) to control which values are shown in the editor, and it works well.

However, it seems like GetOptions is bound statically to the property declaration, which makes it hard to vary per derived type.

For example, if I have a base struct FStrutctA and multiple derived structs FStructB, FStructC, FStructD, I’d like the same inherited property (NameA) to show different option lists depending on the derived struct type.

Currently, there’s no way (that I know of) to override the GetOptions metadata for an inherited property, so I end up duplicating the property in each derived struct just to assign a different GetOptions function—hurting maintainability.

Is there any plan to support a more dynamic / override-friendly approach for GetOptions, such as allowing the options provider to depend on the owning struct type (or to be overridden in derived structs)?

Example :

USTRUCT(BlueprintType)
struct FStructA
{
    UPROPERTY(EditDefaultsOnly, meta = (GetOptions = "GetOptions_NameA"))
    FName NameA;
};
 
USTRUCT(BlueprintType)
struct FStructB : public FStructA
{
    // Need a different GetOptions for NameA
};
 
USTRUCT(BlueprintType)
struct FStructC : public FStructA
{
    // Need a different GetOptions for NameA
};

2) ValidEnumValues is hard to read/maintain when many values are listed

Also, when using meta = (ValidEnumValues = “…”) to restrict which enum entries should appear in the editor, I have to list every allowed enum entry as a long comma-separated string inside the same UPROPERTY macro.

If the allowed set is large, readability and maintainability drop significantly.

Is there any plan to support a cleaner approach, similar to GetOptions, where the allowed enum values can be provided via a function (or otherwise separated/centralized)?

Example :

UPROPERTY(EditDefaultsOnly, meta = (ValidEnumValues =
    "Attack, Defense, MoveSpeed, AttackDamage, TakeDamage, MaxHP, "
    "BossAttackDamage, BossTakeDamage, GroggyValue, GroggyDamage, "
    "CriticalRate, CriticalDamage, TakeCriticalDamage, SkillGaugeChargeEfficiency, "
    "SkillDamage, SkillCriticalRate, SkillCriticalDamage, SkillCoolTime, "
    "AttackSpeed, WeaponDamage, BurstDuration, BurstGaugeChargeEfficiency, "
    "StaminaCost, EffectBeadsEfficiency, InventoryWeight, CaptureRate, "
    "RideDuration, BuffDuration, DebuffDuration, BuffEffectiveness, DebuffEffectiveness"))
ESpecificType A;

[Attachment Removed]

Hi there,

This case seems like a feature request so I will treat it as one and escalate this case to the epic team so that they can review it. However this issue seemed like there could be a potential workaround for it so I wanted to investigate a little.

I did manage to find a “sort of” workaround for your 1st issue. I say sort of because while it does work in certain cases it doesn’t work in the exact case you outlined in your post. Given an alternate situation where these were UObjects rather than structs. You can make the GetOptions function an overridable member function. When overridden in the child class, It will use the child’s function rather than the parent when getting the options for the property.

I tried testing another workaround by setting the metadata directly from the constructor.

USTRUCT(BlueprintType)

struct FStructB : public FStructA

{

GENERATED_BODY()

FStructB()

{

if (UScriptStruct\* ScriptStruct \= FStructB::StaticStruct())

{

  if (FProperty\* Prop \= ScriptStruct\-\>FindPropertyByName(GET\_MEMBER\_NAME\_CHECKED(FStructB, NameA)))

  {

    Prop\-\>SetMetaData(TEXT("GetOptions"), TEXT("GetOptions\_NameB"));

  }

}

}

};

This seemingly worked at first but upon re-opening my blueprint i used to house the variables for the struct all instances no matter the child/parent type of struct used the same metadata that was most recently set. So not a great workaround in this case. However this method could potentially be used for your second issue, parsing an array of strings into the SetMetaData function rather than a long CSV list in the property header.

Cheers,

Louis

[Attachment Removed]

Hello,

Let me try to answer question 1 first.

Unreal’s reflection system uses a linked list of properties when it describes derived types. In the FStructA, FStructB and FStructC case, the unique UScriptStruct instances actually point to the same FProperty instance for NameA. This makes it not possible to change the metadata on the property NameA without affecting the base class.

GetOptions can point to a function and if that function is virtual then you’d be able to point to different behaviour depending on the owning type. However, UFUNCTIONS are only available to UObjects - do you need to use USTRUCT or could you get away with making these UObjects that are Instanced?

If you cannot use objects, then the other option available to you would be to write an IPropertyTypeCustomization on for your various struct types to give you the ability to narrow down what is available to be selected for that property.

For question 2, we have GetRestrictedEnumValues but it is somewhat along the same lines with how it is implemented right now - it takes a UFunction and that function returns values that would be excluded. However I think it would be alright to provide it a secondary mode where it takes a list of enum strings that would form the exclusion list instead of using a function. For example,

UENUM()
enum class ESpecificType
{
    Attack, Defense, MoveSpeed, AttackDamage, TakeDamage
};
 
//...
UPROPERTY(EditDefaultsOnly, meta = (GetRestrictedEnumValues = "Attack, Defense"))
ESpecificType A;

A would only allow you to select MoveSpeed, AttackDamage and TakeDamage.

Would this help with the maintainability of these lists?

Logan

[Attachment Removed]