Using Gameplay Tags in C++?

Hello, was looking to convert all my ‘FNames’ to GameplayTags, to make it easier for artists to modify properties.

However I can’t seem to figure out how to actually use them within C++.

Lets say I have the following tags to describe the enemy behaviour:


UPROPERTY(EditAnywhere) FGameplayTagContainer SpecialProperties;

Special.Hover
Special.Berserk
Special.MovingAttack

I would like to be able to check which tags were passed into the container, like so:



if(SpecialProperties.HasTag("Hover")) // Run Hover code
if(SpecialProperties.HasTag("Berserk")) // Run Berserk code
etc.


However HasTag… only accept other GameplayTags as parameters?

Is there some other way to supply an FName to see if it’s in the tag? I tried passing in FGameplayTag(“Hover”), but that constructor is private.

Thanks

1 Like

If you want to compare Tchar* to a tag you have use MatchesTag(TEXT(“myTagName”))

I was also messing around with GameplayTags today. What Bruno mentioned works, something else to keep in mind since you mentioned the private constructors is that to make a tag from text like that, you have to include GameplayTagsModule.h, and then use UGameplayTagsManager::Get().RequestGameplayTag(“Special.Hover”). Not sure if that’s the recommended way or not but it worked for me.

Thanks but I can’t seem to get it working.



    FGameplayTag tag;
    tag.MatchesTag(TEXT("myTagName"));

    FGameplayTagContainer tagCont;
    tagCont.HasTag(TEXT("myTagName"));

Both produce errors:
Cannot convert from ‘const wchar_t [10]’ to ‘const FGameplayTag’
No overload matches the argument list, etc.

This seemed like a nice simple solution as I’ll be done a lot of Gameplaytag checks in the Tick, but I don’t see how text could be converted to GameplayTag, unless I’m missing something?

I could’ve sworn that it worked :eek: Meanwhile just use what I mentioned earlier. Make a new ***FGameplayTag TestTag = ******UGameplayTagsManager::Get().RequestGameplayTag(“Special.Hover”); ***and then use that tag for comparison. There are a lot of other useful functions in ***UGameplayTagsManager ***so read through those too.

Yeah I’m going to use that for now. It’ll be great in BeginPlay and such, problem is I’m using it 15+ times per tick on 100+ actors.
(I’m not too sure of the performance of requesting existing tags 1000 times per tick).

If *MatchesTag(TEXT(“myTagName”)) *does in fact work, that could be much easier on performance when ran so many times per tick.

MatchesTag requires a FGameplayTag now.

MatchesTag(TEXT(“Blah”)) is far more inefficient than requesting a tag and storing it. Tags are basically just FNames (so they boil down to an integer compare once created). If you just passed in a string you’d be paying the cost of creating the FName every call.

I’d just put an array of the tag names you want somewhere, run through and request the tags at start up, then just use those whenever you are doing a tag check.

They might have changed things, but as far I remember when I did this it worked:



#define MANAGER UGameplayTagsManager::Get()

MANAGER.AddNativeGameplayTag(TEXT("Character.Status.Buffs"));




UPROPERTY()
FGameplayTagContainer MyTags;

if ( MyTags.HasTag( FGameplayTag(TEXT("Status")) ) ) {
        // do things...
}



They made the FName constructor private:


(was it not like this before?)

Correct, but again the previous call was technically expensive since it had to construct an FName at runtime - so I can see why they wanted to move away from that and force people to cache gameplay tags.

Alrighty, I think I’ve created a decent solution to my initial post. The below code is a 1 time conversion from GameplayTagContainer into a Name Array that can easily and quickly be searched at any time.

It seems pretty good to me, but let me know if there’s actually any big flaws/performance problems with this method.

Here’s what I’m using for an Item Chest where developers can select multiple item tags to spawn in the chest.



UPROPERTY(EditAnywhere, meta = (Categories = "Items")) FGameplayTagContainer ItemTag;
TArray<FName> NTags;



BeginPlay(){

    //Convert GameplayTagContainer into array of names
    for (int8 i = 0; i < ItemTag.Num(); i++) {
        FString tagStr = ItemTag.GetByIndex(i).GetTagName().ToString();
        int8 endDot = tagStr.Find(".", ESearchCase::IgnoreCase, ESearchDir::FromEnd);
        if(endDot > -1) tagStr = tagStr.RightChop(endDot+1);
        NTags.Add(*tagStr);
    }
}


That also puts only the last tag into the list, removing the categories. So:
Items.Potions.MediumPotion
Items.Potions.HighPotion
Items.Bombs.FireBomb

Becomes MediumPotion, HighPotion, FireBomb. Easy to loop through and spawn the items. Also easy to do the quick lookups I was asking about before using:


Tags.Contains("MediumPotion");

For single GameplayTags, you can also easily use the below method to check if it contains a specific tag:



GameplayTag.GetTagName().ToString().Contains("MediumPotion");


This method might be easier to search for single tags than requesting a fully typed out + categorized gameplay tag.
Whether it performs better or worse I don’t know, but might save typing-time in the long run for single lookups.

1 Like

If you go that route than doing ToString().ParseIntoArray(…) is faster and easier:

Oh, good call, thanks! Definitely better than string manipulation.

This is a little outdated but I wanted to post an update for the way ive managed to get this working as of 4.19.1

Just a little helper function I made for the class.



bool AYourActor::CompareTags(FGameplayTagContainer const & EffectTags, FName const & Tag)
{
    FGameplayTag TagRequest = FGameplayTag::RequestGameplayTag(Tag);
    return EffectTags.HasTag(TagRequest);
}


This will error if it doesnt find the tag, and is about as simple as you can get.

2 Likes

This is old, but to anyone finding this, ParseIntoArray is easier. It is definitely not faster. The comments on (almost) all of the C++ functions indicate that it is (relatively) slow and costly.
As a general rule, just because you can write 10 lines of code or call a function (1 line) does not mean the function is faster. Always look under the hood.

This is so old that the need to do all the conversions is completely unnecessary.

There are macros that people can use from NativeGameplayTags.h to create/manage tags directly in native.

UE_DECLARE_GAMEPLAY_TAG_EXTERN(MyTag) // in header

UE_DEFINE_GAMEPLAY_TAG(MyTag, "Special.Tag") // in a cpp

Then when you have a container property, you can do the Container.Matches(MyTag) directly.

These macros were introduced in 4.27.