GameplayTags:标签即状态

为什么用标签而不用枚举

第 1 章说过"是否眩晕 = 是否有 State.Stunned 标签"。为什么这样设计?

枚举的问题在于封闭:加一个状态要改枚举定义、重编译、所有检查点都要重新考虑。标签是开放的树形命名空间:

State.Stunned          (眩晕)
State.Burning          (燃烧)
State.Silenced         (沉默)
Cooldown.Skill.Fireball.3s    (冷却槽位)
DamageType.Fire        (伤害类型)
Effect.Buff.Shield     (正面护盾)

新加一个状态 = 加一个标签,不动任何代码。而且标签之间天然有层级关系:

  • 查 State.Stunned → 命中 State.Stunned.Level1、State.Stunned.Level2……(子标签自动匹配父标签查询)
  • 查 State(通配)→ 命中所有 State 下的标签

标签查询:Has/Blocked/Required 三件套

GE 与 GA 的配置里最常见三组标签设置:

字段 含义 典型用法
GrantedTags 应用后授予目标的标签 中眩晕 → 给目标加 State.Stunned
RequiredTags 必须有这些标签才能应用 只对"流血"状态的目标生效
BlockedTags 有这些标签就拒绝应用 已有燃烧就不能再点燃(互斥)

这仨配上,"燃烧互斥""只驱散负面""沉默时不能施法"这类需求在配置界面里完成,不需要写一行代码。

在代码里查标签

// 有任意一个父标签下的子标签
ASC->HasMatchingGameplayTag(FGameplayTag::RequestGameplayTag("State.Stunned"));

// 严格相等
ASC->HasTagExact(...);

// 查询"是否有任意 State 标签"
FGameplayTagContainer QueryTags;
QueryTags.AddTag(FGameplayTag::RequestGameplayTag("State"));
bool bHasState = ASC->GetGameplayTags().MatchesAny(QueryTags);   // 父标签匹配子标签

对比 if-else 写法

手写状态机最常见的代码:

if (bIsStunned) return;
if (bIsSilenced && AbilityType == EAbilityType::Magic) return;
if (CooldownTimer > 0) return;
// ...

每加一个状态,这些 if 就要跟着长。GAS 的写法是:GE 授予标签 + GA 的"激活阻挡标签"(Activation Blocked Tags)——一个配置字段终结所有 if-else。

本章小结

  • 标签是开放树形状态语言:新增状态零代码、天然支持层级匹配与通配。
  • 三件套:GrantedTags(授予)、RequiredTags(必需)、BlockedTags(阻挡)。
  • 状态检查从 if-else 变成配置字段,是 GAS "配置化思想" 的核心体现。