Why you should be using GameplayTags in Unreal Engine

Reading Time: 8 minutes

Last modified date

Comments: 10

You may or may not be familiar with GameplayTags in Unreal Engine. It’s heavily used in Unreal’s Gameplay Ability System, but can be used stand-alone in your game framework. In this article, I will introduce GameplayTags and how they can be used in your game, even if you choose not to use GAS.

What are GameplayTags?

A FGameplayTag is essentially an FName that is defined in the game’s project settings (or natively defined in C++). A major benefit of using these tags is that they allow for easy selection in the Editor’s UI and don’t have to type them out each time which is prone to user error. You also don’t use TArray with FGameplayTag, instead you should always use the FGameplayTagContainer as this has helper functions to match one or multiple tags easily.

GameplayTag selection menu for any FGameplayTag of FGameplayTagContainer variable.
Gameplay Tag Manager in Project Settings > Project > GameplayTags > Manage Gameplay Tags

Another powerful feature is the hierarchy to find exact tags or match based on their parent. This lets you create a tree of tags from broad to very narrow. some examples:

  • Damage.DoT.Fire
  • Location.Planet.Derelict
  • Action.Primary
  • Action.Secondary
  • Effect.Burning
  • Effect.UIHidden
  • DamageType.Energy
  • Attribute.Health
  • Terminal.Engineering

The above is a random selection of tags used in some of my own projects. The neat thing is you can use GameplayTags to specify something is a DamageType, and more specifically a DamageType.Fire, DamageType.Kinetic, etc.

The concept of hierarchies may become more clear once you start thinking of your content in this way and use it in some real-world scenarios. It took me a little bit before I finally understood how to use this feature properly.

How are GameplayTags different from Actor / Component Tags?

It may be confusing that there are multiple tagging systems in Unreal. The basic system of tagging (Actor/Component Tags) works with Strings and has none of the neat features of GameplayTags. Once your content starts to grow in the hundreds of gameplay assets you really don’t want to end up with typos in your handwritten string tags or re-open assets all throughout your content folders to remember how you named that one particular tag since the basic tag systems have no central database. This system also lacks the hierarchy feature. It’ll become a nightmare to manage and compare Tags using this basic system unless you implement this yourself from scratch.

GameplayTags can be tracked in the Reference Viewer

Some practical use-cases

One way to think about tags is having a big library of booleans. You can easily decorate your actors (and most other types of content for that matter) with a wide range of these ‘bools’ for the rest of your game code to read and react to. A major benefit here is when using an ActorComponent to hold these tags is that you don’t need to cast to specific Actor classes as you would with regular bool variables. It’s far more dynamic than if you had to compile 100s of bools into your classes too.

I highly recommend looking into Lyra Starter Game (or the older UE4 ActionRPG) to get some familiarity with the Gameplay Ability System and their use of GameplayTags. It uses a component added to gameplay Actors which you can load up with tags. My own open-source project uses GameplayTags for a custom ability system as well.

Start Ability by Tag

You could start an ability by GameplayTag rather than calling Start() on it directly by holding a hard reference to a specific ability class.

Gameplay Message Router (Lyra Plugin)

Lyra has a plugin dedicated to this called GameplayMessageRouter which I recommend digging into and consider for your gameplay framework. It essentially allows you to broadcast and listen for gameplay events by tag with a custom payload (struct) of choice.

Send a message with tag Event.Module.Disabled for other game systems to listen to, with two parameters wrapped in a struct.
Listen for Event.Module.Disabled event, the struct used here must match the one sent earlier.

GameplayTag Add/Remove Events

Adding GameplayTag “listeners” is invaluable in building an event-driven gameplay framework. Here we listen for the Pawn to go into ADS (Aim down Sights) so we can run some logic in response. (Note: AddGameplayTagListener is a function from my own project – similar functionality can be found in GAS)

You want to wrap your custom AddTag() and RemoveTag() functions so you can broadcast an event/delegate.

I’ll be adding a better example of this to the Action Roguelike sample project, at which time this section will be updated.

Markup for Loot tables

Mark up your loot tables with tags that can block specific items from dropping in the table or tags that are required in order to drop a particular item as loot. Below is an example of a TableRow struct usable in DataTables.

‘Required Tags’ can be used to filter out items from the loot table before rolling the dice. These ‘Tags’ can be grabbed from anywhere in your game code such as those applied to the character, the looted chest, the environment, etc.
USTRUCT(BlueprintType)
struct FItemTableRow : public FTableRowBase
{
  GENERATED_BODY()

public:

  UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "ItemTable")
  FPrimaryAssetId ItemId;

  /* Use -1 weight for guaranteed drop. */
  UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "ItemTable")
  float Weight;


  /* Item is only included if all tags are present when rolling.  */
  UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Tags")
  FGameplayTagContainer RequiredTags;

  /* Item is excluded when the roll includes this tag. */
  UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Tags")
  FGameplayTagContainer BlockedTags;
}

Lock & Keycard Video Example

An easy way to demonstrate GameplayTags and its hierarchical features is by using DOOM’s locked doors and colored keycards. I have a video tutorial of that which is part of the C++ Course.

GameplayTags as alternatives to Casting

Casting and class references won’t hit you as a problem until much later in the project. You will realize any class hard reference requires the referenced asset/class to be loaded and that may cause a cascade of additional assets to be loaded such as meshes or particle effects. You can (and should) use base classes that don’t reference other assets as much as possible to reduce this problem. GameplayTags can help even more by allowing you to remove direct references between your assets all together.

The best way to check for references is by using the Reference Viewer & Size Map tool. (Both available by right-clicking your asset in the Content Browser)

In the example above I already see some references that shouldn’t be there. We somehow end up referencing several classes such as BaseShip and TurretRotating class (left top) which are 30MB and 11MB in size and should have nothing to do with a Player Pawn. These problems will often occur during development, it’s better to find these early as you may need to adjust your framework design or change your coding habits before it’s too late in the project. This is not just for the final game product either, you are loading in these referenced assets any time you boot up the editor or load in a particular blueprint (and all its references) that you work on.

Any assets referenced here will be loaded when the asset in question is used – unless you use soft references inside said asset. This is a silent killer as you won’t notice until you have already added in a decent chunk of your content at which point it’s expensive to re-design your framework.

Decorating Items with Tags

My game has a large number of “Items” which isn’t just restricted to what you consider items from an Inventory. Even Points of Interest, Characters, Ships, Achievements, etc. could be considered an Item. In practice, it’s a (Primary)DataAsset that holds UI information, ability data, related Actor class, GameplayTags, etc. The GameplayTags can be useful to generically decorate your “Item” with whatever information is desired in its context.

Some of the current item types in WARPSQUAD.

Networking (Replication)

GameplayTags can be replicated more efficiently by Unreal than FNames. There are some options available in Project Settings > GameplayTags. ‘Fast Replication’ is able to replicate tags by Index instead of the full Name, for this the tag list must be identical between client and server.

GameplayTag Stack Container

The default FGameplayTagContainer struct lacks one critical feature. And that’s “Stacking” of tags, or keeping a count. Epic’s Gameplay Ability System solves this through a FGameplayTagCountContainer which stores the number of instances.

Lyra has FGameplayStackContainer which isn’t coupled to the ability system and could be transferred to your own project.

This is incredibly powerful as multiple sources might want to add/remove tags which avoid conflicts as now the tag stays on the target until it reaches zero.

Of course, you could even use this for more conventional counting of resources such as Ammo.

Implementing GameplayTags

With all the reasons why you should be using tags covered, let’s show some actual code!

Enabling GameplayTags in your C++ project.

To enable the use of GameplayTags in C++ you must add the “GameplayTags” module to your MyProject.build.cs. Click here for an example of *.builds.cs with GameplayTags enabled.

Make sure the “GameplayTagsEditor” plugin is enabled (default). Otherwise, you won’t have any of the useful editor windows that make this so useful.

Using GameplayTag in C++

Below is an example of the ActionComponent and Action classes which define a container and individual tag.

ActionComponent holding container for tags:

#include "GameplayTagContainer.h"

UCLASS()
class ACTIONROGUELIKE_API USActionComponent : public UActorComponent
{
  GENERATED_BODY()

  UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Tags")
  FGameplayTagContainer ActiveGameplayTags;
}

Action which adds tags to owner when activated and holds blocked tags which prevent the action from running if present on the owner.

#include "GameplayTagContainer.h"

UCLASS(Blueprintable)
class ACTIONROGUELIKE_API USAction : public UObject
{
  GENERATED_BODY()


  /* Tags added to owning actor ActiveGameplayTags when activated, removed when action stops */
  UPROPERTY(EditDefaultsOnly, Category = "Tags")
  FGameplayTagContainer GrantsTags;

  /* Action can only start if OwningActor has none of these Tags applied */
  UPROPERTY(EditDefaultsOnly, Category = "Tags")
  FGameplayTagContainer BlockedTags;

Starting the action will add the tags to the owner.

void USAction::StartAction_Implementation(AActor* Instigator)
{

  USActionComponent* Comp = GetOwningComponent();
  // AppendTags() for adding containers and AddTag() for single tags.	
  Comp->ActiveGameplayTags.AppendTags(GrantsTags);

}

CanStart checks for any “illegal” tags present on the owner.

bool USAction::CanStart_Implementation(AActor* Instigator)
{

  USActionComponent* Comp = GetOwningComponent();
	
  if (Comp->ActiveGameplayTags.HasAny(BlockedTags))
  {
    return false;
  }

  return true;
}

Defining Native GameplayTags

Since 4.27 it’s much easier to define GameplayTags directly in C++. This can be helpful if your framework requires certain tags to be present without having to define them elsewhere in your INI files. You then don’t need to use the RequestGameplayTag() function from earlier so long as you defined this tag in code and not the project settings.

// Macro in your CPP file, naming style is an example. First param is what you use to access this Tag in your C++.
UE_DEFINE_GAMEPLAY_TAG(TAG_Attribute_Health, "Attribute.Health");

// In the Header file.
UE_DECLARE_GAMEPLAY_TAG_EXTERN(TAG_Attribute_Health);

// -- Alternative Macro is available: --- //

/**
 * Defines a native gameplay tag such that it's only available to the cpp file you define it in.
 */
UE_DEFINE_GAMEPLAY_TAG_STATIC(TAG_Attribute_Health, "Attribute.Health");

With the tag defined above you can use the TAG_Attribute_Health elsewhere in your code which represents an FGameplayTag filled with “Attribute.Health”.

Project Example

Sometimes the best way to learn is by example. My open-source Action Roguelike project uses GameplayTags for Actions and Buffs. For example, while Sprinting a tag is applied that prevents the player from attacking. You can find this in the USActionComponent class and the USAction class.

This project is part of my new Unreal Engine C++ Course where I talk more in depth about GameplayTags and tons of other essential skills for Unreal C++ Game Programming!

Closing

I hope this intro helped you understand how GameplayTags may be used in your project and will trigger you to continue researching and experimenting. Sometimes you just need to be made aware something exists as you won’t go looking for it yourself.

As always don’t forget to follow me on Twitter and subscribe to the mailing list!

References

10 Responses

  1. Hi Tom,
    How does your Add Gameplay Tag Listener node work under the hood? This part interests me the most, but I cannot find any information anywhere on how to effectively “listen” to a gameplay tag container. Surely you are not polling the gameplay tag container with a timer. So I assume your Action Component store all the “listeners” and checks each one every time the gameplay tag container changes, firing off their respective events if required? However, what happens if a “listener” is no longer relevant (i.e the listening actor has been destroyed)? The Action Component keeps checking it and firing an event that goes to nobody?
    Also, how do you listen for stateless changes? For example, a light actor that should turn on when some other button actor is pressed. Pressing the button does not cause a change in state as the button is a one-off event. Therefore the light has nothing to actually “listen” for as the button’s gameplay tag container wouldn’t change at all. You wouldn’t want to have to create a hard reference between the light and the button to solve this?

    • I do indeed keep a list (or “map/dictionary”) of events along with the Tag its assigned to.

      As far as cleanup I don’t really fully clean the list YET, although since these kind of events are weak references they shouldn’t be causing issues even when the listener itself is gone (the list just remains unnecessarily large unless I start cleaning it myself).

      For stateless changes I use ‘GameplayEvents’ as similar concept, but a bit like multicast for networking, in that we just one-off trigger an event that others can respond to in the moment. To identify the event I still use a GameplayTag.

  2. Hey Tom,

    A question about this part of the code here:

    Subsystem->GetRandomFloat(0, 99.f, “CritDamage”) < FMath::Abs(CritChancePerc)

    what if the randomly generated float is equal to the CritChancePerc, shouldn't that be considered a valid crit?

  3. Tom – Looking at your practical use cases description, you show a blueprint node named “Add GameplayTag Listener” and below that “Add Listener” which takes a TAG for AI. I can’t find these nodes anywhere and am lost on where these are?

  4. Tags are interesting but is there a way to search which blueprint is using which tag? (Search for tags in blueprints)

  5. I was kinda expecting this to show us how to implement the basics with an example and a use case to learn the flow and syntax easily. This felt more like an ad. I guess from now on it’s going to be “buy my course to see me talk more about it!”
    I guess when you got the MegaGrant and said you were going to do intermediate/advanced level tutorials you meant only the topic, not the tutorial as well. rip

    Can’t even zoom in on the pictures to see your DataAsset example

    • You can browse the open source in SActionComponent class and the Blueprint Actions in content (blackhole, dash) etc. to see an example and syntax:

      https://github.com/tomlooman/ActionRoguelike/blob/master/Source/ActionRoguelike/Public/SActionComponent.h

      The SAction.h and SActionEffect.h use the FGameplayTag(Container) to prevent an Action from running when a certain Tag is applied.

      Fixed the DataAsset image by the way, you can now click to zoom. Thanks for pointing that out.

      • Thanks but the source code of a project isn’t exactly the best way to start since it’s bloated with other code and you kind of have to be familiar with how the Tag system works overall to understand it better. Your save tutorial was great for example but I understand wanting to advertise your course too.

        • Agreed. The point of tutorials is to contextualize and provide concrete examples, such that further endeavors might abstract in a domain-specific way. Simply linking source code is insufficient, as it takes an unnecessary amount of time to contextualize concepts and dig through cruft. A concrete example would have likely been enough to give enough context so as to springboard individual needs.

          Given the aforementioned, this is not a tutorial, but a simple nod at the existence of gameplay tags. Therefore, I don’t see the point of this post. Tom has written better articles, and he should be judged on the merits of previous articles; they set the standards that this article does not match.

Leave a comment on this post!