4、GAS相关概念4.3 Attributes

4.3 Attributes

4.3.1 Attributes定义

Attributes 是由结构体 FGameplayAttributeData 定义的浮点值。这些值可以表示从角色的生命值到角色的等级,再到药水的使用次数等任何游戏相关的数值。如果它是属于 Actor 的游戏相关数值,您应该考虑使用 AttributeAttributes 通常应仅由 GameplayEffects 修改,以便ASC可以预测变化。

Attributes 是由 AttributeSet 定义并存在于其中的。AttributeSet 负责复制标记为复制的 Attributes。有关如何定义 Attributes,请参阅 AttributeSets 部分。

提示: 如果您不希望某个 Attribute 显示在编辑器的 Attributes 列表中,可以使用 Meta = (HideInDetailsView) 属性说明符。

4.3.2 基础值 vs 当前值(BaseValue vs CurrentValue)

一个 Attribute 由两个值组成 - BaseValueCurrentValueBaseValueAttribute 的永久值,而 CurrentValueBaseValue 加上来自 GameplayEffects 的临时修改。例如,您的 Character 可能有一个移动速度 Attribute,其 BaseValue600 u/s。由于还没有 GameplayEffects 修改移动速度,因此 CurrentValue 也是 600 u/s。如果她获得了一个临时的 50 u/s 移动速度增益,BaseValue 保持不变为 600 u/s,而 CurrentValue 现在是 600 + 50,总计 650 u/s。当移动速度增益过期时,CurrentValue 恢复为 BaseValue600 u/s

初学者常常会将 BaseValue 误认为是 Attribute 的最大值,并试图将其视为最大值。这是一种错误的方法。可以更改或在能力或UI中引用的 Attributes 的最大值应被视为单独的 Attributes。对于硬编码的最大值和最小值,可以定义一个带有 FAttributeMetaDataDataTable 来设置最大值和最小值,但Epic在结构体上方的注释称其为“正在进行的工作”。有关更多信息,请参阅 AttributeSet.h。为了避免混淆,我建议将可以在能力或UI中引用的最大值作为单独的 Attributes,而仅用于限制 Attributes 的硬编码最大值和最小值则定义为 AttributeSet 中的硬编码浮点数。有关 Attributes 限制的讨论,请参阅 PreAttributeChange() 以了解对 CurrentValue 的更改,以及 PostGameplayEffectExecute() 以了解来自 GameplayEffectsBaseValue 更改。

BaseValue 的永久更改来自 Instant GameplayEffects,而 DurationInfinite GameplayEffects 更改 CurrentValue。周期性 GameplayEffects 被视为即时 GameplayEffects 并更改 BaseValue

4.3.3 元属性(Meta Attributes)

一些 Attributes 被视为用于与 Attributes 交互的临时值的占位符。这些被称为 Meta Attributes。例如,我们通常将伤害定义为一个 Meta Attribute。而不是让 GameplayEffect 直接改变我们的生命值 Attribute,我们使用一个名为伤害的 Meta Attribute 作为占位符。这样,伤害值可以在 GameplayEffectExecutionCalculation 中通过增益和减益进行修改,并且可以在 AttributeSet 中进一步操作,例如从当前护盾 Attribute 中减去伤害,然后再从生命值 Attribute 中减去剩余部分。伤害 Meta AttributeGameplayEffects 之间没有持久性,并且会被每一个覆盖。Meta Attributes 通常不复制。

Meta Attributes 为诸如伤害和治疗等事物提供了良好的逻辑分离,区分“我们造成了多少伤害?”和“我们如何处理这些伤害?”。这种逻辑分离意味着我们的 Gameplay EffectsExecution Calculations 不需要知道目标如何处理伤害。继续我们的伤害示例,Gameplay Effect 确定伤害量,然后 AttributeSet 决定如何处理该伤害。并非所有角色都可能具有相同的 Attributes,特别是如果您使用子类化的 AttributeSets。基础 AttributeSet 类可能只有一个生命值 Attribute,但子类化的 AttributeSet 可能会添加一个护盾 Attribute。具有护盾 Attribute 的子类化 AttributeSet 会与基础 AttributeSet 类不同地分配收到的伤害。

虽然 Meta Attributes 是一种良好的设计模式,但它们并不是强制性的。如果您只使用一个 Execution Calculation 处理所有伤害实例,并且所有角色共享一个 Attribute Set 类,那么您可能可以在 Execution Calculation 内部进行健康、护盾等的伤害分配,并直接修改这些 Attributes。您只会牺牲灵活性,但这对您来说可能是可以接受的。

4.3.4 响应Attribute变化

要监听 Attribute 变化以更新UI或其他游戏玩法,请使用 UAbilitySystemComponent::GetGameplayAttributeValueChangeDelegate(FGameplayAttribute Attribute)。此函数返回一个委托,您可以绑定到该委托,该委托将在 Attribute 变化时自动调用。委托提供一个 FOnAttributeChangeData 参数,其中包含 NewValueOldValueFGameplayEffectModCallbackData注意: FGameplayEffectModCallbackData 仅在服务器上设置。

AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(AttributeSetBase->GetHealthAttribute()).AddUObject(this, &AGDPlayerState::HealthChanged);
virtual void HealthChanged(const FOnAttributeChangeData& Data);

示例项目绑定到 GDPlayerState 上的 Attribute 值更改委托,以更新HUD并在生命值降至零时响应玩家死亡。

示例项目中包含了一个将其包装到 ASyncTask 中的自定义蓝图节点。它用于 UI_HUD UMG Widget 中以更新生命值、法力值和耐力值。此 AsyncTask 将永久存在,直到手动调用 EndTask(),我们在UMG Widget的 Destruct 事件中执行此操作。请参阅 AsyncTaskAttributeChanged.h/cpp

监听属性变化蓝图节点

4.3.5 派生属性(Derived Attributes)

要创建一个部分或全部值从一个或多个其他 Attributes 派生的 Attribute,请使用一个 Infinite GameplayEffect,其中包含一个或多个 Attribute BasedMMC Modifiers。当其依赖的 Attribute 更新时,Derived Attribute 将自动更新。

派生属性上所有 Modifiers 的最终公式与 Modifier Aggregators 的公式相同。如果您需要按特定顺序进行计算,请在 MMC 内部完成。

((CurrentValue + Additive) * Multiplicitive) / Division

注意: 如果在PIE中与多个客户端一起玩,您需要在编辑器首选项中禁用 Run Under One Process,否则当其独立 Attributes 在第一个客户端之外的客户端上更新时,Derived Attributes 将不会更新。

在此示例中,我们有一个 Infinite GameplayEffect,其派生 TestAttrA 的值来自 AttributesTestAttrBTestAttrC,公式为 TestAttrA = (TestAttrA + TestAttrB) * ( 2 * TestAttrC)。每当任何 Attributes 更新其值时,TestAttrA 会自动重新计算其值。

派生属性示例