Gameplay Tag subclasses cannot use the generic Equals blueprint node

We’ve found that any native FGameplayTag subclass cannot use the generic BP equals node, unlike the base FGameplayTag which works fine. If you try connecting a child Gameplay Tag variable to the equals node, it changes the pin type to Gameplay Tag but remains disconnected:

[Image Removed]

Using the vanilla Gameplay Tag on the same node works fine.

[Attachment Removed]

Steps to Reproduce

  1. Open a project with the CommonUI plugin enabled to access the FUITag type
  2. Open any blueprint graph
  3. Create a variable of type FUITag
  4. Try to connect the variable to an equals node

Expected behaviour: variable connects correctly

Actual behaviour: the equals operator changes pin type to Gameplay Tag, but no connection is made

[Attachment Removed]

Hey Stefan, thanks for reporting this! I can repro the problem and will file a bug for it, so that we’ll eventually address this in the engine.

I believe this issue is caused by two factors:

  • FGameplayTag has an explicit comparison function (EqualEqual_GameplayTag).
  • FUITag has no conversion function to FGameplayTag known to blueprint.

Here is the fix I quickly created. It may or may not resemble the final engine fix, but feel free to use it as a local engine modification. Add the following to UCommonUILibrary.h:

struct FGameplayTag;
struct FUITag;
struct FUIActionTag;
 
// The rest as member functions:
 
/** Autocast function from UITag to GameplayTag */
UFUNCTION(BlueprintPure, BlueprintInternalUseOnly, meta = (BlueprintAutocast))
static UE_API FGameplayTag Conv_UITagToGameplayTag(FUITag InValue);
 
/** Autocast function from UIActionTag to GameplayTag */
UFUNCTION(BlueprintPure, BlueprintInternalUseOnly, meta = (BlueprintAutocast))
static UE_API FGameplayTag Conv_UIActionTagToGameplayTag(FUIActionTag InValue);

CommonUILibrary.cpp:

#include "UITag.h"
 
FGameplayTag UCommonUILibrary::Conv_UITagToGameplayTag(FUITag InValue)
{
	return InValue;
}
 
FGameplayTag UCommonUILibrary::Conv_UIActionTagToGameplayTag(FUIActionTag InValue)
{
	return InValue;
}

Note that although the “conversion” here is trivial, in general we don’t want to assume that derived structs can simply be up-casted for comparison. In this case, it’s allowed because FUITag/FUIActionTag doesn’t add any properties.

With this implementation, you can also compare FGameplayTags to FUITag.

I’ll still file a public trackable bug so you can keep an eye out on the official engine fix!

[Attachment Removed]

Note, just for explaining the code

The BlueprintAutocast meta tag on the UFUNCTIONS allows those to be considered in

FTypePromotion::IsValidPromotion(HighestLinkedType, FunctionPinType)which is called in UK2Node_PromotableOperator::UpdatePinsFromFunction(). The autocast function allows a FUITag value to be promoted to FGameplayTag, for which a blueprint equality operator exists.

[Attachment Removed]

Indeed, child classes may break assumptions about convertibility. The temp fix I provided only works so long as UITag should be interpreted as a GameplayTag for all comparison purposes. A GameplayTag and UITag variable are comparable, too.

I’ll discuss with CommonUI and GAS devs about what path we’ll take for the CommonUI structs, and what we recommend in general in terms of user subclasses of FGameplayTag.

[Attachment Removed]

I created UE-359341 for public trackability, but I’ll also give you an update after talking to CommonUI and GameplayTag devs.

[Attachment Removed]

The fix I suggested was accepted as-is and has been committed to the engine. It’s CL 49619791 on //UE5/Main and commit 351ed111ae30b71e7ead346975f8f761d0c89031 on GitHub.

We decided not to automatically let all FGameplayTag subclasses use EqualEqual_GameplayTag, because this would go wrong when user projects add variables or anything else that makes it invalid to compare the subclass as if they are FGameplayTags. Although with normal FGameplayTag input pins, a subclass can be passed as well, in those scenarios, the upcast is more obvious. With == nodes they’re not, so unaware users might use == on FGameplayTag subclasses and expect a proper comparison when that’s not the case.

So instead of that risk of fallout, we prefer people to decide for themselves whether to allow an FGameplayTag subclass to be comparable as regular tags. After speaking with CommonUI devs, they don’t foresee adding any properties to FUITag, so the autocast is safe to add. If you have custom FGameplayTag subclasses, either add BlueprintAutocast-to-FGameplayTag functions or define your own explicit comparison function.

[Attachment Removed]

I just looked it up to verify, and you are correct on those two requirements.

FTypePromotion::CreateOpTable() is where the functions are found that the generic operator nodes can be promoted to based on pin type. It’s called the first time the FTypePromotion singleton is created. It indeed only scans UBlueprintFunctionLibrary subclasses and looks for specific function name prefixes like EqualEqual_. The rest of the function name doesn’t matter and is based on variable type.

And I believe you already realized: if you introduce a subclass-specific EqualEqual function, then existing == BP nodes comparing the subclass have to be recreated since they are already promoted to whatever function was previously selected.

[Attachment Removed]

Hi Zhi,

Thank you for the quick reply. I tried something similar earlier but it seems like I had put the conversion function in the wrong place and the engine wasn’t able to find it. From inside a BPFL it now works as intended.

In light of this it may not be a bug in the end and more of an inconvenience? Since as you’ve suggested FGameplayTag child classes can add properties (even if it would be rare) and break any assumptions about convertibility. Unless the engine is able to generate its own BP-usable comparer based on the struct and its TStructTypeOpsTraits (changing TStructTypeOpsTraits values for the type was proposed by GPT-5 as a fix for this, not sure where it got that idea but might be relevant)

[Attachment Removed]

Thank you, that makes sense. For a custom comparison to be picked up by the equals node, is it correct that:

  • It needs to be in a BPFL
  • Name needs to start with EqualEqual (and for other operations, any of the OperatorNames listed at the top of BlueprintPromotion.cpp)
  • The function will be found by its inputs and return type?
    [Attachment Removed]