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 "配置化思想" 的核心体现。
