DEV Community

Cover image for Native Gameplay Tags in GAS
Marko Petrić
Marko Petrić

Posted on • Originally published at marko-ue.hashnode.dev

Native Gameplay Tags in GAS

What are gameplay tags and why should you use them

Gameplay tags are essentially FName wrappers, meaning they have a small upfront cost when first created but are very cheap to access and compare after that.

Gameplay tags are used as a clean way to handle things like character and actor state, input and ability activation, damage types and resistances, and communication between systems. GAS also has built-in tag requirements for gameplay abilities. Essentially, wherever you'd traditionally use booleans or enums, gameplay tags are generally the better choice when working with GAS.


What are native gameplay tags and why should you use them

Native gameplay tags are tags created in C++, rather than in the editor.

The main reasons for creating native gameplay tags rather than using editor ones is for type safety (autocomplete, no typo risk), which makes native ones safer, faster and easier to access through code.

Note: Any gameplay tag changes made in blueprint reflect to C++ code and vice versa. The Ability System Component does not care about the source of changes.


Storing tags

Before creating native tags, you need a place to store them. A simple way would be to manually create empty .h and .cpp files. For context, my project is called Comply, and I will be using that prefix for my tags, so my files will be named ComplyTags.

After creating these files, you need to use #pragma once at the top of the .h file, and to include CoreMinimal.h and NativeGameplayTags.h.

#pragma once

#include "CoreMinimal.h"
#include "NativeGameplayTags.h"
Enter fullscreen mode Exit fullscreen mode

One way of storing tags, which lets you access specific tags cleanly, is to use namespaces. After creating a base namespace, each other namespace should be named based on the kind of tags it will store - so, for example, you could have a namespace called States for all state related gameplay tags, or Abilities for all ability asset tags. It's also possible to have namespace hierarchies.

This is just one way of storing them, but you can try any approach and see what you'd prefer. In this practical example, namespaces will be used.


Declaring tags

To declare tags, you use the UE_DECLARE_GAMEPLAY_TAG_EXTERN macro, and pass in the name of the tag you wish to use, as shown below:

namespace ComplyTags
{
    namespace ComplyAbilities
    {
        UE_DECLARE_GAMEPLAY_TAG_EXTERN(Primary_Ranger);
        UE_DECLARE_GAMEPLAY_TAG_EXTERN(Utility_Ranger);
    }
}
Enter fullscreen mode Exit fullscreen mode

Defining tags

After declaration, tags need to be defined, and this is handled in the .cpp file for your tags. First, you need to include the .h file for the tags here, which in my case is ComplyTags.h.

#include "AbilitySystem/ComplyTags.h"
Enter fullscreen mode Exit fullscreen mode

In the .cpp file, you need to make sure your namespace names and hierarchies match with the structure created in the .h file.

To define the tags, you use the UE_DEFINE_GAMEPLAY_TAG, and optionally UE_DEFINE_GAMEPLAY_TAG_COMMENT which unlocks a third parameter that lets you add a comment for your tag that can be seen in the editor. For this macro, the first parameter is the tag name (the same one you passed in when declaring the tag), then the tag hierarchy (which is how the tag can be accessed in the editor), and then, optionally, the comment.

namespace ComplyTags
{
    namespace ComplyAbilities
    {
        UE_DEFINE_GAMEPLAY_TAG_COMMENT(Primary_Ranger, "ComplyTags.Abilities.Ranger.Primary", "Primary ability asset tag for ranger")
        UE_DEFINE_GAMEPLAY_TAG_COMMENT(Utility_Ranger, "ComplyTags.Abilities.Ranger.Utility", "Utility ability asset tag for ranger")
    }
}
Enter fullscreen mode Exit fullscreen mode

Accessing and using native tags

In the editor

Any tags created natively appear in the gameplay tag list found in Project Settings, even among any tags you may choose to create in the editor.
An image showing gameplay tags in the editor

They can be assigned in the editor for all purposes.

An image showing the usage of gameplay tags in the editor


In C++

Using native gameplay tags in C++ is where they really prove their worth over ones created in blueprint. When accessing gameplay tags created in the blueprint through C++, you would often have to use FGameplayTag::RequestGameplayTag(FName("Tag.Hierarchy")); which is prone to typos and has a slight overhead.

For accessing native ones in code, you just have to include the .h of where you store your native gameplay tags, which in my case is ComplyTags.h, wherever you need to access them. Then, you type in the name of the base namespace, and use double colons to navigate to different namespaces and to a specific tag.

Examples of using native gameplay tags in code:

// Checking if an actor has a certain tag
FGameplayTagContainer Tags;
Character->GetAbilitySystemComponent()->GetOwnedGameplayTags(Tags);

if (Tags.HasTagExact(ComplyTags::States::State_Aiming)
{
    PlayMontageAndBindDelegates(AbilityActivationMontageIronsights);
}
Enter fullscreen mode Exit fullscreen mode
// Using a native gameplay tag to pass in a set by caller data tag
FGameplayEffectContextHandle ReserveAmmoContextHandle = GetAbilitySystemComponentFromActorInfo()->MakeEffectContext();

FGameplayEffectSpecHandle ReserveAmmoSpecHandle = GetAbilitySystemComponentFromActorInfo()->MakeOutgoingSpec(
ActiveWeapon->ReduceReserveAmmoEffectClass, 1.f, ReserveAmmoContextHandle);

UAbilitySystemBlueprintLibrary::AssignTagSetByCallerMagnitude(
ReserveAmmoSpecHandle, ComplyTags::SetByCaller::SBC_ReduceRifleReserveAmmo,-AmmoSpent);

GetAbilitySystemComponentFromActorInfo()->ApplyGameplayEffectSpecToSelf(
*ReserveAmmoSpecHandle.Data.Get());
Enter fullscreen mode Exit fullscreen mode
// Registering a gameplay tag event by passing in a native tag
GetAbilitySystemComponent()->RegisterGameplayTagEvent(
ComplyTags::States::State_Aiming,EGameplayTagEventType::NewOrRemoved).AddUObject(
this, &AComplyPlayerCharacter::OnAimingTagChanged);
Enter fullscreen mode Exit fullscreen mode

When you would prefer editor-created tags

Native gameplay tags make sense when you are accessing them frequently in code, but editor tags are fine for quick one-off logic, for designers, or for quick prototyping.


Conclusion

Although not required, native gameplay tags are a great alternative to editor-created tags for a multitude of reasons, and I would recommend everyone to try using them.

If you have any questions or feedback, feel free to contact me on LinkedIn, or email me: petric.marko04@gmail.com

Top comments (0)