In this lesson, you will learn how to maintain gameplay balance by dynamically clamping derived attributes—such as movement speed—based on fluctuating primary stats like health or stamina.
The Problem of Interdependency
In GAS, attributes are rarely isolated. Often, you want a character’s performance to degrade as they take damage. For example, a character might move at 100% speed when healthy, but slow down to 50% speed once their Health drops below 25%. If you simply modify the MovementSpeed attribute directly, you risk breaking the logic or creating race conditions where the movement speed doesn't correctly update when health is restored.
Implementing Derived Logic in PostGameplayEffectExecute
To handle this, we utilize the PostGameplayEffectExecute function within our AttributeSet. Because derived statistics (like speed) often depend on the current state of primary attributes (like health), we need to ensure that whenever an effect modifies a primary attribute, we recalculate the dependent one and enforce strict clamping.
Consider this implementation pattern:
void UMyAttributeSet::PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data)
{
Super::PostGameplayEffectExecute(Data);
if (Data.EvaluatedData.Attribute == GetHealthAttribute())
{
// Calculate the impact on MovementSpeed based on health percentage
float CurrentHealth = GetHealth();
float MaxHealth = GetMaxHealth();
float HealthPercentage = CurrentHealth / MaxHealth;
// Logic: Slow movement speed if health is low
float NewSpeed = (HealthPercentage < 0.25f) ? 300.0f : 600.0f;
// Apply and clamp the result to valid bounds
SetMovementSpeed(FMath::Clamp(NewSpeed, 0.0f, 600.0f));
}
}
Why Clamping Matters
Clamping is vital here because gameplay effects might attempt to set an attribute to an extreme value (e.g., a "Berserker" buff might temporarily boost speed while a "Crippled" debuff lowers it). By wrapping your calculation in FMath::Clamp, you ensure that the MovementSpeed never enters an invalid state, such as becoming negative, which could cause physics errors or movement glitches in the Character Movement Component. Always recalculate your derived attributes after the primary source changes to maintain a "Single Source of Truth."
Key Takeaway: Use PostGameplayEffectExecute to reactively recalculate dependent attributes based on primary stat changes, and always use FMath::Clamp to ensure derived values remain within gameplay-safe boundaries.