Ue Gameplay Tags Messaging logo

Ue Gameplay Tags Messaging

Community
quodsoler
ue-gameplay-tags-messaging

Use when defining, querying or replicating Gameplay Tags, or wiring decoupled event messaging between gameplay systems in C++. Also use when the user mentions 'FGameplayTag', 'FGameplayTagContainer', 'FGameplayTagQuery', 'UE_DEFINE_GAMEPLAY_TAG', 'native gameplay tag', 'DefaultGameplayTags.ini', 'GameplayTagList', 'tag redirect', 'MatchesTag', 'HasTagExact', 'RequestGameplayTag', 'UGameplayTagsManager', 'Categories meta', 'tag picker', 'gameplay message', 'message bus', 'event bus' or 'AsyncMessageSystem'. For abilities, effects and cues that consume tags, see ue-gameplay-abilities; for wire cost and RPCs, see ue-networking-replication.

Overview

Publisherquodsoler
Repositoryunreal-engine-skills
Skill nameue-gameplay-tags-messaging
Stars
403
Forks
66
Bundled files
2
LicenseMIT
Links
  • Markdown instructions

    A SKILL.md file the model loads on demand, so it only costs tokens when a request actually matches.

  • Works with any LLM

    AI skills are plain Markdown, not provider-specific code, so this works with GPT, Claude, Gemini, Grok, or a local model.

  • 2 bundled files

    Scripts, templates, and references the model can read while it works. Files are read-only and never executed.

  • Open source

    Published by quodsoler on GitHub. Read the source before you install it.

Installation

Install the Ue Gameplay Tags Messaging AI skill in TypingMind to use it with any LLM, or drop it into another agent that reads SKILL.md.

1

Install in TypingMind

TypingMind installs a skill straight from its GitHub folder — it reads SKILL.md, bundles the resource files, and stores the result locally.

  1. Open the app and go to Plugins → Skills.
  2. Choose "Install from GitHub".
  3. Paste the skill folder URL below and confirm.
    https://github.com/quodsoler/unreal-engine-skills/tree/main/skills/ue-gameplay-tags-messaging
  4. Enable the skill in any chat where you want it available.
Plugins → Skills → Add skill → From GitHub URL, then paste the folder URL and press Continue.
2

Install in another agent

Any agent that reads the Agent Skills format can use this skill — copy the folder into that agent's skills directory.

Claude Code — .claude/skills
git clone --depth 1 https://github.com/quodsoler/unreal-engine-skills.git /tmp/unreal-engine-skills
mkdir -p .claude/skills
cp -r /tmp/unreal-engine-skills/skills/ue-gameplay-tags-messaging .claude/skills/ue-gameplay-tags-messaging
Restart Claude Code after copying so it picks up the new skill.

Use it in TypingMind

Enable Ue Gameplay Tags Messaging in any TypingMind chat and the model takes it from there. Its name and description sit in the system prompt, and the moment a request matches, the model loads the full instructions itself — you never invoke it by hand, and it costs no tokens until it is actually used.

The model loads Ue Gameplay Tags Messaging on its own as soon as a request matches it.

Works with any AI model

AI skills are plain Markdown instructions rather than provider-specific code, so Ue Gameplay Tags Messaging is not tied to the model it was written for. Install it once in TypingMind and use it with GPT-5, Claude, Gemini, Grok, DeepSeek, Mistral, Llama, or a local model you run yourself — all on your own API keys.

  • Loaded only when it is needed

    The system prompt carries just the name and description. The instructions are fetched on the first matching request, so an idle skill costs nothing.

  • Switch models mid-chat

    Because the skill is instructions rather than code, changing model does not break it — the next model reads the same SKILL.md.

Skill instructions

This is the SKILL.md content the model loads. Read it before installing — a skill is instructions your model will follow.

UE Gameplay Tags and Messaging

Target engine: UE 5.8. APIs below are verified against the 5.8 headers; older forms are listed under "Deprecated — do not use".

Gameplay Tags are a built-in runtime module, not a plugin: add "GameplayTags" to PublicDependencyModuleNames in your Build.cs and include GameplayTagContainer.h / NativeGameplayTags.h / GameplayTagsManager.h. This skill owns tag definition (native and ini), FGameplayTag, FGameplayTagContainer, FGameplayTagQuery, UGameplayTagsManager, editor filtering, and decoupled gameplay messaging — including the AsyncMessageSystem plugin (Experimental in 5.8).

Context

Read .agents/ue-project-context.md if it exists (module names, conventions, enabled plugins, GAS/networking setup). Do not stop if it is missing.

Identify the area from the request and the codebase. Ask only when two plausible readings would produce different code.

Request is about…Go to
Declaring tags in C++, native tag macros, module setupNative Tag Definitions
Declaring tags in .ini, data tables, renaming a tagIni and Data Table Tag Sources
Testing a tag, container membership, parent vs exactMatching Semantics
Multi-tag conditions, AND/OR/NOT, designer-authored rulesTag Queries
Actors exposing tags, gates and states built from tagsTag-Driven Design
Sending tags over the network, tag cost, hot pathsReplication and Performance
Broadcasting events between systems without hard referencesDecoupled Messaging
Tag pickers, Categories filtering, per-team tag filesEditor UX and Filtering

Tag Sources

A tag must be registered before it can be requested. Four sources feed the one global dictionary held by UGameplayTagsManager:

SourceWhereUse for
NativeUE_DEFINE_GAMEPLAY_TAG* in a module .cppTags C++ compares against by name
Default iniConfig/DefaultGameplayTags.ini, +GameplayTagList=Tags authored by designers
Extra iniConfig/Tags/*.ini and plugin config dirsPer-team or per-plugin tag lists
Data tableUDataTable of FGameplayTagTableRow listed in GameplayTagTableListBulk import from a spreadsheet

EGameplayTagSourceType (Native, DefaultTagList, TagList, RestrictedTagList, DataTable, Invalid) names these in the editor. Tags are hierarchical: registering MyGame.State.Stunned implicitly registers MyGame.State and MyGame.

Native Tag Definitions

Declare in a public header, define in exactly one .cpp of the same module. The macros static_assert that they are used from a .cpp.

cpp
// MyGameplayTags.h
#pragma once

#include "NativeGameplayTags.h"

MYGAME_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(TAG_MyGame_State_Stunned);
MYGAME_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(TAG_MyGame_Damage_Fire);
MYGAME_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(TAG_MyGame_Message_ScoreChanged);
cpp
// MyGameplayTags.cpp
#include "MyGameplayTags.h"

UE_DEFINE_GAMEPLAY_TAG_COMMENT(TAG_MyGame_State_Stunned, "MyGame.State.Stunned", "Character cannot act");
UE_DEFINE_GAMEPLAY_TAG_COMMENT(TAG_MyGame_Damage_Fire, "MyGame.Damage.Fire", "Fire damage channel");
UE_DEFINE_GAMEPLAY_TAG_COMMENT(TAG_MyGame_Message_ScoreChanged, "MyGame.Message.ScoreChanged", "Score changed broadcast");
MacroParametersEffect
UE_DECLARE_GAMEPLAY_TAG_EXTERN(TagName)extern FNativeGameplayTag TagName; — goes in the header, prefixed with your _API macro
UE_DEFINE_GAMEPLAY_TAG(TagName, Tag)Defines the variable with an empty dev comment
UE_DEFINE_GAMEPLAY_TAG_COMMENT(TagName, Tag, Comment)Same plus a developer comment shown in the editor
UE_DEFINE_GAMEPLAY_TAG_STATIC(TagName, Tag)static — visible only inside that one .cpp, with no matching declaration in a header

Tag and Comment are bare string literals: the macro wraps Comment in TEXT() itself. Writing TEXT("…") there does not compile.

FNativeGameplayTag registers on static construction and unregisters when the module unloads, so tags from a plugin disappear cleanly with the plugin. It converts implicitly to FGameplayTag and also exposes GetTag():

cpp
const FGameplayTag Stunned = TAG_MyGame_State_Stunned.GetTag();
if (OwnedTags.HasTag(TAG_MyGame_State_Stunned))
{
    return;
}

One definition rule: exactly one UE_DEFINE_GAMEPLAY_TAG (or _COMMENT / _STATIC) per tag variable per binary. Two modules defining the same tag string is fine (the manager reference-counts registrations), but two definitions of the same variable is a link error, and a definition in a header breaks every translation unit that includes it.

To register a tag whose name is only known at runtime, call the manager directly:

cpp
const FGameplayTag Runtime = UGameplayTagsManager::Get().AddNativeGameplayTag(
    FName(TEXT("MyGame.Generated.Slot01")), TEXT("Generated at startup"));

AddNativeGameplayTag after startup invalidates fast replication (see Replication and Performance). Register from CallOrRegister_OnAddNativeTagsDelegate, and run code that consumes tags from CallOrRegister_OnDoneAddingNativeTagsDelegate:

cpp
// MyGameModule.cpp — FDelegateHandle NativeTagsHandle; is a member of FMyGameModule.
#include "MyGameModule.h"
#include "GameplayTagsManager.h"

void FMyGameModule::StartupModule()
{
    NativeTagsHandle = UGameplayTagsManager::CallOrRegister_OnDoneAddingNativeTagsDelegate(
        FSimpleMulticastDelegate::FDelegate::CreateRaw(this, &FMyGameModule::OnTagsReady));
}

void FMyGameModule::ShutdownModule()
{
    UGameplayTagsManager::UnregisterNativeTagDelegate(NativeTagsHandle);
}

If registration already finished, CallOrRegister_OnDoneAddingNativeTagsDelegate runs the delegate immediately and CallOrRegister_OnAddNativeTagsDelegate runs it at the end of the current module load (GameplayTagsManager.h:419-427), so there is no ordering hazard in calling them late.

Ini and Data Table Tag Sources

UGameplayTagsSettings is UCLASS(config=GameplayTags, defaultconfig) deriving from UGameplayTagsList, so Project Settings → Gameplay Tags writes Config/DefaultGameplayTags.ini:

ini
[/Script/GameplayTags.GameplayTagsSettings]
ImportTagsFromConfig=True
FastReplication=False
NumBitsForContainerSize=6
NetIndexFirstBitSegment=16
+GameplayTagList=(Tag="MyGame.State.Stunned",DevComment="Character cannot act")
+GameplayTagList=(Tag="MyGame.Damage.Fire",DevComment="Fire damage channel")
+GameplayTagTableList=/Game/Data/DT_MyGameTags.DT_MyGameTags
+GameplayTagRedirects=(OldTagName="MyGame.State.Stun",NewTagName="MyGame.State.Stunned")
KeyTypeMeaning
ImportTagsFromConfigboolMaster switch for ini-defined tags; read back through UGameplayTagsManager::ShouldImportTagsFromINI()
GameplayTagListTArray<FGameplayTagTableRow>Tag (FName) plus DevComment (FString)
GameplayTagTableListTArray<FSoftObjectPath>Data tables of FGameplayTagTableRow, loaded by LoadGameplayTagTables
GameplayTagRedirectsTArray<FGameplayTagRedirect>OldTagName → NewTagName; inherited from UGameplayTagsList
CategoryRemappingTArray<FGameplayTagCategoryRemap>Remaps an engine Categories filter onto project categories
RestrictedConfigFilesTArray<FRestrictedConfigInfo>Top-level tags only a named owner may edit
InvalidTagCharactersFStringCharacters rejected in tag names, beyond newline and friends

Extra ini sources live in Config/Tags/ (added automatically) and in any directory passed to UGameplayTagsManager::Get().AddTagIniSearchPath(RootDir); RemoveTagIniSearchPath takes them away again. A Game Feature plugin's config directory is registered this way, which is why its tags appear only while the plugin is active.

Renaming a tag requires a redirect. FGameplayTagRedirect is { OldTagName, NewTagName }; FGameplayTagRedirectors::Get().RedirectTag(OldName, OutTag) is what serialization uses (engine-internal: the class is not exported). Without a redirect, every asset that referenced the old name silently loads an invalid tag.

Full ini and data-table details are in gameplay-tag-reference.md.

Matching Semantics

FGameplayTag is one FName. A container keeps the explicitly added tags plus a transient expanded parent list, so parent-aware checks are array lookups, not string parsing.

CallReceiverSemantics{"A.1"} vs A
MatchesTag(TagToCheck)FGameplayTagThis tag or any parent equals TagToCheckA.1.MatchesTag(A) → true; A.MatchesTag(A.1) → false
MatchesTagExact(TagToCheck)FGameplayTagNames equalA.1.MatchesTagExact(A) → false
MatchesAny(ContainerToCheck)FGameplayTagParent-expanded match against any tag in the containerA.1.MatchesAny({A,B}) → true
MatchesAnyExact(ContainerToCheck)FGameplayTagContainer explicitly holds this tagA.1.MatchesAnyExact({A,B}) → false
HasTag(TagToCheck)FGameplayTagContainerExplicit or parent list contains it{A.1}.HasTag(A) → true
HasTagExact(TagToCheck)FGameplayTagContainerExplicit list only{A.1}.HasTagExact(A) → false
HasAny / HasAnyExactFGameplayTagContainerAt least one; false for an empty argument{A.1}.HasAny({A,B}) → true
HasAll / HasAllExactFGameplayTagContainerAll; true for an empty argument{A.1,B.1}.HasAll({A,B}) → true
Filter(OtherContainer)FGameplayTagContainerNew container of tags matching any tag in OtherContainer, parents expanded{A.1,C}.Filter({A}) → {A.1}
FilterExact(OtherContainer)FGameplayTagContainerTrue intersection, exact names{A.1,C}.FilterExact({A}) → {}

The empty-argument asymmetry is deliberate: "any of nothing" is false, "all of nothing" is true. Gates written as RequiredTags.IsEmpty() || Owned.HasAll(RequiredTags) are redundant.

cpp
FGameplayTagContainer MakeStateTags(const FGameplayTagContainer& ExtraTags)
{
    FGameplayTagContainer Owned;
    Owned.AddTag(TAG_MyGame_State_Stunned);    // checks uniqueness and refills parents
    Owned.AddTagFast(TAG_MyGame_Damage_Fire);  // no uniqueness check — only when the tag is known to be new
    Owned.AppendTags(ExtraTags);
    Owned.RemoveTag(TAG_MyGame_State_Stunned);

    if (!Owned.IsEmpty())
    {
        UE_LOG(LogMyGame, Log, TEXT("%d tags, first %s, last %s: %s"),
            Owned.Num(), *Owned.First().ToString(), *Owned.Last().ToString(), *Owned.ToStringSimple());
    }

    return Owned;
}

Parent navigation: FGameplayTag::RequestDirectParent() returns x for x.y, FGameplayTag::GetGameplayTagParents() returns a container holding the tag plus every parent, and FGameplayTagContainer::GetGameplayTagParents() does the same for a whole container. ParseParentTags(TArray<FGameplayTag>&) does it without touching the manager.

AddTagFast skips the Contains check but still fills parents; use it when building a container from data you already know is unique, never in place of AddTag on user input.

Tag Queries

FGameplayTagQuery is a compiled token stream — cheap to evaluate, expensive to build. Build it once, store it, evaluate it many times.

cpp
// MyBurnRule.cpp
#include "GameplayTagContainer.h"
#include "MyGameplayTags.h"

bool ShouldBurn(const FGameplayTagContainer& OwnedTags)
{
    FGameplayTagContainer Elements;
    Elements.AddTag(TAG_MyGame_Damage_Fire);

    // (any element tag) AND NOT Stunned
    FGameplayTagQuery ComplexQuery = FGameplayTagQuery::BuildQuery(
        FGameplayTagQueryExpression()
            .AllExprMatch()
            .AddExpr(FGameplayTagQueryExpression().AnyTagsMatch().AddTags(Elements))
            .AddExpr(FGameplayTagQueryExpression().NoTagsMatch().AddTag(TAG_MyGame_State_Stunned)));

    return ComplexQuery.Matches(OwnedTags);
}

The shortcut form covers the common cases without an expression tree:

cpp
FGameplayTagContainer Blocking;
Blocking.AddTag(TAG_MyGame_State_Stunned);
const FGameplayTagQuery BlockedQuery = FGameplayTagQuery::MakeQuery_MatchAnyTags(Blocking);

Shortcuts on FGameplayTagQuery: MakeQuery_MatchAnyTags, MakeQuery_MatchAllTags, MakeQuery_MatchNoTags, MakeQuery_ExactMatchAnyTags, MakeQuery_ExactMatchAllTags, MakeQuery_MatchTag. Expression verbs on FGameplayTagQueryExpression: AnyTagsMatch, AllTagsMatch, NoTagsMatch, AnyTagsExactMatch, AllTagsExactMatch, AnyExprMatch, AllExprMatch, NoExprMatch, fed by AddTag, AddTags and AddExpr.

Evaluate from either side — Query.Matches(Container) or Container.MatchesQuery(Query). Both call the same evaluator.

Expose a query as a UPROPERTY and designers get the query editor with no extra work:

cpp
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Gating")
FGameplayTagQuery ActivationQuery;

ReplaceTagsFast(Tags) and ReplaceTagFast(Tag) swap the tag dictionary of a cached query without rebuilding the token stream; the new container must be the same size.

Tag-Driven Design

Anything that owns tags should expose them through IGameplayTagAssetInterface so unrelated systems can ask without casting:

cpp
// MyTaggedActor.h
#pragma once

#include "GameFramework/Actor.h"
#include "GameplayTagAssetInterface.h"
#include "GameplayTagContainer.h"
#include "MyTaggedActor.generated.h"

UCLASS()
class MYGAME_API AMyTaggedActor : public AActor, public IGameplayTagAssetInterface
{
    GENERATED_BODY()

public:
    virtual void GetOwnedGameplayTags(FGameplayTagContainer& TagContainer) const override;

protected:
    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Tags", meta = (Categories = "MyGame.State"))
    FGameplayTagContainer OwnedTags;
};
cpp
// MyTaggedActor.cpp
#include "MyTaggedActor.h"

void AMyTaggedActor::GetOwnedGameplayTags(FGameplayTagContainer& TagContainer) const
{
    TagContainer.AppendTags(OwnedTags);
}

HasMatchingGameplayTag, HasAllMatchingGameplayTags and HasAnyMatchingGameplayTags come from the interface for free — they call your GetOwnedGameplayTags. From Blueprint the equivalents are UBlueprintGameplayTagLibrary::DoesTagAssetInterfaceHaveTag, HasAnyMatchingGameplayTags and GetOwnedGameplayTags.

Counted tags: an FGameplayTagContainer is a set, so two sources granting Stunned and one removing it leaves nothing. When several sources can grant the same tag, use FGameplayTagCountContainer (GameplayEffectTypes.h:1102, in the GameplayAbilities plugin; it is what an ASC uses for owned tags). UpdateTagCount(Tag, +1/-1, EGameplayTagReplicationState::None) adjusts the count (the single-tag overload has no default for the third argument; UpdateTagCount(Container, Delta) takes two), GetTagCount(Tag) includes child tags (A.B and A.C added make GetTagCount(A) == 2), GetExplicitTagCount does not, GetExplicitGameplayTags() returns the current set for GetOwnedGameplayTags, and RegisterGameplayTagEvent(Tag, EGameplayTagEventType::NewOrRemoved | AnyCountChange) returns an FOnGameplayEffectTagCountChanged (void(const FGameplayTag, int32 NewCount)) to bind. With GAS in the project, use the ASC's loose tags instead of owning one.

Design rules that hold up:

  • Tags name states and categories, not objects. MyGame.State.Stunned and MyGame.Damage.Fire are good; MyGame.Actor.Goblin03 is a class reference wearing a tag.
  • Depth is the query. Keep the hierarchy shallow enough that HasTag(MyGame.Damage) is a meaningful question.
  • Author the gate, not the branch. Expose FGameplayTagContainer or FGameplayTagQuery properties and let data decide, instead of if (Tag == SpecificTag) chains.
  • Never store a tag as FName or FString on a gameplay type. Store FGameplayTag; it validates, redirects and replicates.

Replication and Performance

An FGameplayTag is a single FName — copy it freely, pass it by value. Cost lives in registration and in serialization.

ConcernRule
LookupRequestGameplayTag walks a map every call. Resolve once into a native tag or a member, never per tick or per hit
Container build-upAddTag refills the parent array each call; AddTagFast skips the uniqueness check; FGameplayTagContainer::CreateFromArray builds parents once for a whole array
Wire formatFGameplayTag::NetSerialize and FGameplayTagContainer::NetSerialize are registered via TStructOpsTypeTraits — a replicated tag property already uses them
FastReplication=TrueReplicates tags by net index instead of name. Client and server dictionaries must be identical, so it breaks if a plugin adds tags on only one side
bDynamicReplication=TruePer-connection index assignment; slightly more expensive than FastReplication but tolerates differing dictionaries. Ignored when FastReplication is on
NetIndexFirstBitSegmentBits in the first index segment; tags listed in CommonlyReplicatedTags get the low indices and fit in one segment
NumBitsForContainerSizeBits used for a replicated container's element count — size it to your real containers

UGameplayTagsManager::Get().DoneAddingNativeTags() flushes native registration; after it, adding more tags is unsafe with fast replication because the net index table has already been built and hashed (GetNetworkGameplayTagNodeIndexHash).

For the wire cost of the properties that carry these containers, and for push-model replication, see ue-networking-replication.

Decoupled Messaging

Pick the lightest mechanism that fits:

NeedUse
A few known listeners inside one gameA dynamic multicast delegate on a UGameInstanceSubsystem or UWorldSubsystem
Tag-addressed, hierarchical, cross-thread, typed payloadsAsyncMessageSystem plugin (Experimental in 5.8)
Ability activation and GAS-side eventsSendGameplayEventToActor / RegisterGameplayTagEvent, owned by ue-gameplay-abilities

A dynamic multicast delegate on a subsystem is often enough, and it costs nothing extra:

cpp
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FMyScoreChanged, AActor*, Scorer, int32, NewScore);

UPROPERTY(BlueprintAssignable, Category = "Score")
FMyScoreChanged OnScoreChanged;

Reach for AsyncMessageSystem when listeners must be addressed by tag hierarchy or must run off the game thread. Enable the plugin in the .uproject and add "AsyncMessageSystem" to PublicDependencyModuleNames; it already depends on GameplayTags. The recipe, verbatim signatures, binding options and endpoints are in gameplay-messaging.md.

cpp
// MyScorePayload.h
#pragma once

#include "StructUtils/InstancedStruct.h"
#include "MyScorePayload.generated.h"

USTRUCT()
struct FMyScorePayload
{
    GENERATED_BODY()

    UPROPERTY()
    int32 NewScore = 0;
};
cpp
// MyScoreComponent.cpp — queue side
#include "AsyncMessageWorldSubsystem.h"
#include "AsyncMessageSystemBase.h"
#include "MyGameplayTags.h"
#include "MyScorePayload.h"

void UMyScoreComponent::BroadcastScore(int32 NewScore)
{
    TSharedPtr<FAsyncMessageSystemBase> Sys = UAsyncMessageWorldSubsystem::GetSharedMessageSystem(GetWorld());
    if (!Sys.IsValid())
    {
        return;
    }

    FMyScorePayload Payload;
    Payload.NewScore = NewScore;
    Sys->QueueMessageForBroadcast(FAsyncMessageId(TAG_MyGame_Message_ScoreChanged.GetTag()),
        FInstancedStruct::Make<FMyScorePayload>(Payload));
}

Not in the engine: UGameplayMessageSubsystem and its GameplayMessageRouter are Lyra sample code, copied into the Lyra project, not shipped in Engine/. Do not #include "GameFramework/GameplayMessageSubsystem.h" — it does not resolve. Port the Lyra files into your own module, or use AsyncMessageSystem.

Editor UX and Filtering

meta = (Categories = "…") restricts a tag picker to one subtree. The metadata keys are Categories and GameplayTagFilter, declared in namespace GameplayTagsManager in ObjectMacros.h:

cpp
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Damage", meta = (Categories = "MyGame.Damage"))
FGameplayTag DamageType;

UFUNCTION(BlueprintCallable, Category = "Damage")
void ApplyElement(UPARAM(meta = (GameplayTagFilter = "MyGame.Damage")) FGameplayTag Element);

UGameplayTagsManager::GetCategoriesMetaFromField reads Categories first and falls back to GameplayTagFilter; GetCategoriesMetaFromFunction(Func, ParamName) is what resolves the filter for a Blueprint pin. Both accept a comma-separated list of roots.

FGameplayTagCategoryRemap (BaseCategory → RemapCategories) in UGameplayTagsSettings redirects an engine-side filter such as a Mover or GAS Categories value onto your own subtree, so engine properties show your tags. UGameplayTagsDeveloperSettings (config=EditorPerProjectUserSettings) holds the per-user FavoriteTagSource and DeveloperConfigName used when adding a new tag from the picker. IGameplayTagsModule::OnGameplayTagTreeChanged and OnTagSettingsChanged let editor tooling refresh when the dictionary changes.

Deprecated — do not use

Do not emitUse in 5.8Source
UGameplayTagsManager::OnLastChanceToAddNativeTags()CallOrRegister_OnAddNativeTagsDelegate, or AddNativeGameplayTag directlyUE_DEPRECATED(5.8) in GameplayTagsManager.h:415
CallOrRegister_OnDoneAddingNativeTagsCallOrRegister_OnDoneAddingNativeTagsDelegateName never existed; GameplayTagsManager.h:429
UGameplayTagsManager::GetSingleTagContainer(Tag)FindTagNode(Tag) or FGameplayTag::GetSingleTagContainer()UE_DEPRECATED(5.4) in GameplayTagsManager.h:472
FGameplayTagContainer::AddParentsForTagParseParentTags or UGameplayTagsManager::ExtractParentTagsUE_DEPRECATED(5.4) in GameplayTagContainer.h:615
UGameplayTagsManager::ShouldClearInvalidTags()Nothing — invalid tags are never clearedUE_DEPRECATED(5.5) in GameplayTagsManager.h:594
ClearInvalidTags ini keyRemove itUE_DEPRECATED(5.5) in GameplayTagsSettings.h:115
+GameplayTagRedirects under [/Script/Engine.Engine]Same key under [/Script/GameplayTags.GameplayTagsSettings]"deprecated location" error, GameplayTagRedirectors.cpp:26-56
EGameplayTagMatchType::Explicit / IncludeParentTagsThe Exact suffix on each call (HasTagExact, MatchesTagExact)Enum does not exist in 5.8
Container.HasTag(Tag, MatchTypeA, MatchTypeB)HasTag(Tag) or HasTagExact(Tag)One-argument forms only, GameplayTagContainer.h:299,316
Container.MatchesAll(Other)HasAll(Other) / HasAllExact(Other)No such member; GameplayTagContainer.h:379,402
Container.Matches(Query)Container.MatchesQuery(Query) or Query.Matches(Container)GameplayTagContainer.h:464,803
MakeQuery_MatchAnyTagsExactMakeQuery_ExactMatchAnyTagsGameplayTagContainer.h:857
DEFINE_GAMEPLAY_TAG / DECLARE_GAMEPLAY_TAG_EXTERN (no UE_)UE_DEFINE_GAMEPLAY_TAG / UE_DECLARE_GAMEPLAY_TAG_EXTERNNativeGameplayTags.h:31,41
IGameplayTagsModule::Get().GetGameplayTagsManager()UGameplayTagsManager::Get()Module exposes only the two change delegates, GameplayTagsModule.h:47-50
UGameplayMessageSubsystem (Lyra sample, not engine)A subsystem delegate, or AsyncMessageSystemNot present anywhere under Engine/Source or Engine/Plugins

Common Mistakes

Requesting a tag before the manager has it: RequestGameplayTag(FName("MyGame.X")) during static init or early module startup ensures and returns an empty tag. Use a native tag variable, or defer the lookup into CallOrRegister_OnDoneAddingNativeTagsDelegate.

Two definitions of the same native tag: UE_DEFINE_GAMEPLAY_TAG in a header, or the same variable defined in two .cpp files, is a duplicate-symbol link error. Declare once with UE_DECLARE_GAMEPLAY_TAG_EXTERN in the header, define once in a .cpp. Use UE_DEFINE_GAMEPLAY_TAG_STATIC when the tag is file-local.

MatchesTag where exactness was meant: Owned.HasTag(TAG_MyGame_Damage_Fire) is true for a container holding MyGame.Damage.Fire.Napalm, and DamageType.MatchesTag(FireTag) is true for any fire subtype. When you need the leaf and only the leaf, use HasTagExact / MatchesTagExact.

Hard-coded tag strings in gameplay code: RequestGameplayTag(FName("MyGame.State.Stunned")) sprinkled through a class survives a rename with no compile error and no redirect. One native tag, referenced everywhere.

Module dependency: Engine.Build.cs lists "GameplayTags" as a public dependency, so any module that depends on Engine already compiles and links tag code without it. Add "GameplayTags" explicitly anyway (public if tag types appear in your public headers, private otherwise) so the dependency survives if Engine is ever dropped and IWYU tools see it. Modules that do not depend on Engine (rare) do need it.

Renaming an ini tag without a redirect: every asset referencing the old name loads an invalid tag and WarnOnInvalidTags at most logs it. Add +GameplayTagRedirects=(OldTagName="Old",NewTagName="New") in the same commit as the rename.

Calling AddNativeGameplayTag late with FastReplication=True: the net index table is already built, so client and server disagree. Register during module startup, or use bDynamicReplication instead.

Building an FGameplayTagQuery per frame: BuildQuery emits a token stream. Build it in the constructor or BeginPlay, store it, and call Matches in the hot path.

Expecting a Lyra message router: #include "GameFramework/GameplayMessageSubsystem.h" does not resolve in a clean project — that subsystem ships with the Lyra sample, not the engine. Use a subsystem delegate or the AsyncMessageSystem plugin.

Related Skills

  • ue-gameplay-abilities — GAS-side tag usage: ability activation gates, granted tags, RegisterGameplayTagEvent and gameplay events
  • ue-state-trees — State Tree conditions and tasks that evaluate tag containers and queries
  • ue-game-features — plugin-scoped tag ini sources and the activation lifecycle that registers them
  • ue-ai-navigation — Smart Object and behaviour tag filtering
  • ue-cpp-foundations — UPROPERTY/UFUNCTION specifiers, delegates, subsystems that host messaging
  • ue-networking-replication — replicating tag properties, push model, RPC cost
  • ue-data-assets-tables — UDataTable assets used as a tag source and for tag-keyed data

Bundled files

The model reads these on demand while the skill is loaded. They are exposed as readable files and are never executed.

Frequently asked questions

What does the Ue Gameplay Tags Messaging AI skill do?

Use when defining, querying or replicating Gameplay Tags, or wiring decoupled event messaging between gameplay systems in C++. Also use when the user mentions 'FGameplayTag', 'FGameplayTagContainer', 'FGameplayTagQuery', 'UE_DEFINE_GAMEPLAY_TAG', 'native gameplay tag', 'DefaultGameplayTags.ini', 'GameplayTagList', 'tag redirect', 'MatchesTag', 'HasTagExact', 'RequestGameplayTag', 'UGameplayTagsManager', 'Categories meta', 'tag picker', 'gameplay message', 'message bus', 'event bus' or 'AsyncMessageSystem'. For abilities, effects and cues that consume tags, see ue-gameplay-abilities; for wir...

Why use Ue Gameplay Tags Messaging on TypingMind?

Because you install it once and use it with any model. Ue Gameplay Tags Messaging is plain Markdown rather than provider-specific code, so the same skill runs on GPT-5, Claude, Gemini, Grok, or a local model — and you can switch model mid-chat without it breaking. TypingMind runs on your own API keys, so you pay providers directly instead of a per-seat subscription, and your skills and chats stay in your own storage.

How do I install Ue Gameplay Tags Messaging in TypingMind?

Open Plugins → Skills → Install from GitHub in TypingMind and paste https://github.com/quodsoler/unreal-engine-skills/tree/main/skills/ue-gameplay-tags-messaging. TypingMind reads its SKILL.md and bundles its files and installs it as a skill you can enable per chat.

Which AI models can use Ue Gameplay Tags Messaging?

Any model you connect in TypingMind. AI skills are plain Markdown instructions rather than provider-specific code, so GPT, Claude, Gemini, Grok, and local models can all load this skill when a request matches it.

How many AI models can I use with Ue Gameplay Tags Messaging?

As many as you like. As long as a model supports skills, you can use Ue Gameplay Tags Messaging with it — GPT, Claude, Gemini, Grok, DeepSeek, Mistral, Llama and more — all on TypingMind with your own API keys.

Is the Ue Gameplay Tags Messaging AI skill free?

Yes. It is published on GitHub by quodsoler under the MIT license. You only pay your own AI provider for the tokens you use.

What are AI skills?

An AI skill is a reusable instruction bundle that teaches an AI model how to do one specific task. It follows the open Agent Skills format: a SKILL.md file with a name and description, plus any scripts, templates or reference files the model may need. The model reads the instructions only when your request matches the skill, so an installed skill costs nothing until it is used.

How are AI skills different from plugins or MCP servers?

A plugin or MCP server gives a model new tools to call — code that runs somewhere and returns a result. An AI skill gives the model knowledge and process instead: how to approach a task, which steps to follow, what good output looks like. Skills are plain Markdown, so they need no server, no API key and no runtime, and they work with any model.

View all

Set up your own AI workspace now

Get notified about new features and future giveaways by subscribing to our newsletter 👇