4.3 Attributes
4.3.1 Attributes定义
Attributes
是由结构体 FGameplayAttributeData
定义的浮点值。这些值可以表示从角色的生命值到角色的等级,再到药水的使用次数等任何游戏相关的数值。如果它是属于 Actor
的游戏相关数值,您应该考虑使用 Attribute
。Attributes
通常应仅由 GameplayEffects
修改,以便ASC可以预测变化。
Attributes
是由 AttributeSet
定义并存在于其中的。AttributeSet
负责复制标记为复制的 Attributes
。有关如何定义 Attributes
,请参阅 AttributeSets
部分。
提示: 如果您不希望某个 Attribute
显示在编辑器的 Attributes
列表中,可以使用 Meta = (HideInDetailsView)
属性说明符。
4.3.2 基础值 vs 当前值(BaseValue vs CurrentValue)
一个 Attribute
由两个值组成 - BaseValue
和 CurrentValue
。BaseValue
是 Attribute
的永久值,而 CurrentValue
是 BaseValue
加上来自 GameplayEffects
的临时修改。例如,您的 Character
可能有一个移动速度 Attribute
,其 BaseValue
为 600 u/s
。由于还没有 GameplayEffects
修改移动速度,因此 CurrentValue
也是 600 u/s
。如果她获得了一个临时的 50 u/s
移动速度增益,BaseValue
保持不变为 600 u/s
,而 CurrentValue
现在是 600 + 50
,总计 650 u/s
。当移动速度增益过期时,CurrentValue
恢复为 BaseValue
的 600 u/s
。
初学者常常会将 BaseValue
误认为是 Attribute
的最大值,并试图将其视为最大值。这是一种错误的方法。可以更改或在能力或UI中引用的 Attributes
的最大值应被视为单独的 Attributes
。对于硬编码的最大值和最小值,可以定义一个带有 FAttributeMetaData
的 DataTable
来设置最大值和最小值,但Epic在结构体上方的注释称其为“正在进行的工作”。有关更多信息,请参阅 AttributeSet.h
。为了避免混淆,我建议将可以在能力或UI中引用的最大值作为单独的 Attributes
,而仅用于限制 Attributes
的硬编码最大值和最小值则定义为 AttributeSet
中的硬编码浮点数。有关 Attributes
限制的讨论,请参阅 PreAttributeChange() 以了解对 CurrentValue
的更改,以及 PostGameplayEffectExecute() 以了解来自 GameplayEffects
的 BaseValue
更改。
对 BaseValue
的永久更改来自 Instant GameplayEffects
,而 Duration
和 Infinite 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 Attribute
在 GameplayEffects
之间没有持久性,并且会被每一个覆盖。Meta Attributes
通常不复制。
Meta Attributes
为诸如伤害和治疗等事物提供了良好的逻辑分离,区分“我们造成了多少伤害?”和“我们如何处理这些伤害?”。这种逻辑分离意味着我们的 Gameplay Effects
和 Execution 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
参数,其中包含 NewValue
、OldValue
和 FGameplayEffectModCallbackData
。注意: 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 Based
或 MMC
Modifiers
。当其依赖的 Attribute
更新时,Derived Attribute
将自动更新。
派生属性上所有 Modifiers
的最终公式与 Modifier Aggregators
的公式相同。如果您需要按特定顺序进行计算,请在 MMC
内部完成。
((CurrentValue + Additive) * Multiplicitive) / Division
注意: 如果在PIE中与多个客户端一起玩,您需要在编辑器首选项中禁用 Run Under One Process
,否则当其独立 Attributes
在第一个客户端之外的客户端上更新时,Derived Attributes
将不会更新。
在此示例中,我们有一个 Infinite GameplayEffect
,其派生 TestAttrA
的值来自 Attributes
,TestAttrB
和 TestAttrC
,公式为 TestAttrA = (TestAttrA + TestAttrB) * ( 2 * TestAttrC)
。每当任何 Attributes
更新其值时,TestAttrA
会自动重新计算其值。