Constants for Gameplay tags

Hello!

I’m getting started with Gameplay tags (and Unreal as a whole to be honest) and it seems a pretty useful system. I have a mix of C++ and Blueprint classes and I was looking for a way of sharing gameplay tags between them. This might be my experience with other languages and practices speaking but the idea of using “magic strings” to query or refer to a tag sounds… not optimal and error prone. Specially when you compare with Blueprints and all the tools you have using Unreal editor.

Is there a way to define gameplay tags using enums or constants, but that can also be read by the editor and consumed by blueprints?

I once had to solve the same problem for very similar reasons, so let me share the solution that I’ve been using ever since.

First - no, there is no way to have gameplay tag constants because gameplay tags aren’t known at compile time. They’re read from config files so the engine only knows about them after those files have been processed at game startup. Trying to define a const FGameplayTag variable will just result in an empty tag.

What you can do instead is declaring gameplay tag variables and filling them with the actual values at a convenient time. See also the handy game flow chart in the docs. I decided to do that in UGameInstance::Init(), there’s probably tons of other valid ways.

Here is an example from an actual game:

// AbilityTag.h

// ability tags are tags that identify gameplay abilities;
// the 'tag' tag is the tag that holds the parent in order
// to have a catch-all tag when needed
struct FAbilityTag
{
	static FGameplayTag Tag;
	static FGameplayTag GetDown;
	static FGameplayTag GetUp;
	static FGameplayTag PickUp;
	static FGameplayTag Talk;
	...
};
// UCustomGameInstance.cpp

FGameplayTag FAbilityTag::Tag;
FGameplayTag FAbilityTag::GetDown;
FGameplayTag FAbilityTag::GetUp;
FGameplayTag FAbilityTag::PickUp;
FGameplayTag FAbilityTag::Talk;

...

void UCustomGameInstance::Init()
{
	Super::Init();

	FAbilityTag::Tag     = FGameplayTag::RequestGameplayTag(TEXT("Ability"));
	FAbilityTag::GetDown = FGameplayTag::RequestGameplayTag(TEXT("Ability.Posture.Down"));
	FAbilityTag::GetUp   = FGameplayTag::RequestGameplayTag(TEXT("Ability.Posture.Up"));
	FAbilityTag::PickUp  = FGameplayTag::RequestGameplayTag(TEXT("Ability.PickUp"));
	FAbilityTag::Talk    = FGameplayTag::RequestGameplayTag(TEXT("Ability.Talk"));
	...
}

With this setup you can make use of the static variables anywhere in the code, like so:

if (Ability->AbilityTags.First() == FAbilityTag::GetDown)
{
	something something
}

If you decide to change a tag name, there’s no hunting it down throughout the code base. Just the .INI file and the game instance and you’re done.

If you want to extend this method to Blueprint, you have to do a little more work, though. Blueprint doesn’t support static UPROPERTY variables, so you can’t just declare

UPROPERTY(BlueprintReadOnly)
static FGameplayTag MyImportantTag;

What you have to do is create something like a UGameplayTagProvider class as a singleton that you can call from Blueprint. The tags can then be declared as regular UPROPERTYs and called from BP.

2 Likes

I would look into FGameplayTagNativeAdder.

I wrote a blog post about it here:

Check it out, its better than using a load of statics.

3 Likes

Thanks @and-rad and @TheKaosSpectrum!

You know, I noticed in the editor that you can define other sources of tags (other than the .ini file) and I was expecting them to have some class/interface I could implement/extend, which seems to be what FGameplayTagNativeAdder is doing.

@TheKaosSpectrum One thing that is not clear to me is how do you make sure that your struct will be constructed (and has the chance to bind the delegate)? Also, using that approach, is there a way to expose those tags to the editor (maybe using UPROPERTY)?

You would not expose those properties to editor, they are just for using Gameplay Tags inside your C++.

Regarding construction of the struct, its done automatically, you need do nothing more than whats in my blog. These tags are also added to the Gameplay Tag database, meaning they can be used in blueprint.

Other sources is not relevant to using Gameplay tags inside C++.

Epic improved this workflow in 4.27 with the addition of NativeGameplayTags.

Found out about it right after posting this -.-

4 Likes

DAFUQ! That is certainly nice.

nvm my bad, yeah not on 4.27 so cant use them, have been keeping my eye on them for when we upgrade in a few weeks from 4.25 :wink:

One problem i see with these are, is the amount of define/declares you need, and will look a bit ugly for lots of tags. For one off tags in a specific class, sure these look great, but not really good for “bulk” native tags…

1 Like

Thanks @Jambax that was exactly what I was looking for!

Hi!

I would just make a blueprint exposed data asset that holds tags for certain things. That way BP can access them and so can code.

For example, say you have a gameplay tag called State.Stunned. Take the data asset I mentioned, make a variable on it like so:
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
FGameplayTag StunnedTag;

Now you can access StunnedTag anywhere in code - doesn’t even matter what the actual tag is. And you can access it in blueprint anywhere too. And the only place you actually need to set the State.Stunned is on the data assets variable in editor.

This pattern is incredibly useful. Say you actually wanted StunnedTag to be a collection of gameplay tags that all get applied to you when you’re stunned. Just change the variable to an FGameplayTagContainer and in that data asset, add any tags you want to have on a character when they’re stunned. Only one place to go when you need to change or edit the stunned tags. Instead of on a data asset, this could be on a UObject on the game state - or wherever makes sense.

But if you need the tags referenced in like a constructor of something, you might need to use the native or static tags mentioned elsewhere.

Yeah I agree on that - I think the only reason to use these over the other methods is if you have tags declared in different modules or something, I think defining tags in different modules with different loading orders (especially if you want to use them in constructors or something) can create issues.

Actually, I just this week wrote a plugin that has its own dedicated gameplay tags. It actually worked really well. I just had the Build.cs file copy the .ini with its tags into the projects Config/Tags folder in the project directory and then had it write in a whitelist config file line in the staging section of the DefaultGame.ini. so as soon as the plugin builds for the first time, it injects its tags into the correct folder, and voila - the engine can recognize the tags.

I originally tried having the plugin’s module file inject the tags by calling a couple different methods I found in the gameplay tags editor module and the gameplay tags manager but that ended up being a mess because the engine had a bug in that path that I had to fix - and having a plugin that relies on a custom engine fix kinda defeats the purpose of a plugin. Plus I had to make a function in the gameplay tags manager public. I would have preferred to inject inject tags during the PreDefault module phase but it doesn’t seem like there’s much engine support for plugins to do that right now. (This is in UE5)