Tags and containers
Hierarchical vs exact matching
A gameplay tag is a dot-separated path. Owning a deeper tag satisfies checks against any of its parents:
ts
const fire = requestGameplayTag("Ability.Element.Fire");
const element = requestGameplayTag("Ability.Element");
fire.matchesTag(element); // true
element.matchesTag(fire); // false — parents do not match children
fire.matchesTagExact(element); // false
fire.matchesTagDepth(element); // 2 — shared leading segmentsGameplayTagContainer applies the same rules to sets of tags:
ts
const owned = makeGameplayTagContainer([
"Ability.Element.Fire",
"Character.Class.Mage"
]);
owned.hasTag(requestGameplayTag("Ability")); // true
owned.hasAll(makeGameplayTagContainer(["Ability", "Character"])); // true
owned.hasAnyExact(makeGameplayTagContainer(["Ability"])); // falseMembership checks are O(1): containers index explicit tags and their implicit parents by key.
Filtering
filter keeps tags that hierarchically match the other container; filterExact requires exact hits:
ts
const abilityTags = owned.filter(makeGameplayTagContainer(["Ability"]));
abilityTags.toJSON(); // ["Ability.Element.Fire"]
owned.filterExact(makeGameplayTagContainer(["Ability"])).toJSON(); // []Mutation
ts
owned.addTag(requestGameplayTag("Character.State.Stunned"));
owned.removeTag(requestGameplayTag("Character.State.Stunned"));
owned.appendTags(otherContainer);
owned.removeTags(otherContainer);
// addLeafTag replaces explicit parents the new leaf makes redundant:
const container = new GameplayTagContainer(requestGameplayTag("Note.Status"));
container.addLeafTag(requestGameplayTag("Note.Status.Draft"));
container.toJSON(); // ["Note.Status.Draft"]Containers never hold duplicates and treat tag names case-insensitively, mirroring Unreal's FName semantics.
Serialization
Containers serialize as plain arrays of tag names, so they embed cleanly in any payload:
ts
JSON.stringify({ actorId: "mage-01", gameplayTags: owned });
// {"actorId":"mage-01","gameplayTags":["Ability.Element.Fire","Character.Class.Mage"]}
const restored = requestGameplayTagContainer(saved.gameplayTags, false);The second argument of requestGameplayTagContainer controls whether unknown tags throw (true, default) or are skipped (false).