Built-in Clamping
Clamping of Attribute, to ensure that their values are always within the limits that you set for them, is a common pattern. That's why Blueprint Attributes plugin come with some pre-defined and built-in clamping behavior that you can opt-in.
You can perform Attributes clamping with one of three methods, two of which are the built-in clamping features coming with the plugin.
- with DataTable - To initialize the Attributes, and using the MinValue and MaxValue columns. The Attribute Set Blueprint Base class is able to handle those Min/Max column values, and execute a built-in clamping behavior.
- with Gameplay Clamped Attribute Data properties - A customizable clamping using a special Gameplay Clamped Attribute Data property (a child of FGameplayAttributeData) with float or Attribute-based clamping.
- Manual Clamping - You'd implement the clamping behavior yourself, with your own rules with the BP exposed method / events of Attribute Sets (PreAttributeChange(), PreAttributeBaseChange() or PostGameplayEffectExecute())
UGBAAttributeSetBlueprintBase::PostGameplayEffectExecute()
is the method responsible of clamping, and the order for those is the following:
- Manual Clamping (if done in PostGameplayEffectExecute BP Event)
- Clamping via Clamped Properties (if the Attribute is implemented as a
FGBAGameplayClampedAttributeData
). - Clamping via DataTables Min and Max value columns (if the Set was initialized with a DataTable, and if
MinValue < MaxValue
).
To better illustrate the clamping behavior in-game, we'll use Gameplay Effects with Infinite duration to setup a regen and degen mechanic for the Health Attribute.
You could also use Gameplay Abilities activated on Input, to apply an Instant Gameplay Effect to either damage or heal the Character (by adding or removing a set of amount of Hit Points from the Health Attribute).
This demonstration setup can be found in the Content Examples project in the 02_Builtin_Clamping
map.
Also, most of the assets specific to this example are prefixed with GE_Clamping_
to easily find them in the Editor with Ctrl+P
.
For these instructions, I'm using an AttributeSet made just for this clamping demonstration, with a single Health Attribute variable.
GBA_Clamping_Example_HealthSet
And two Gameplay Effects, one to continuously add to the Health Attribute, one to continuously substract from the Health Attribute.
GE_Clamping_HealthRegen
GE_Clamping_HealthDegen
Both are Infinite Gameplay Effects, with a Period duration of 0.1
, and either adds or remove 10
from the Health Attribute every 100ms.
GE_Clamping_HealthRegen
is configured to only be active if the Owner Character doesn't have GameplayEffect.Clamping.Degen
Gameplay Tag, which is added by GE_Clamping_HealthDegen
whenever it's applied.
Next, we need to ensure the Regen Gameplay Effect is applied. Since it's an Infinite Effect, it'll be active indefinitely (or until explicitly removed).
You can simply apply the effect programmatically on Begin Play (or Possesed) event, or using the list of Gameplay Effects to apply your GAS bakend could provide to you (GAS Companion's ASC Granted Effects, or Lyra's Ability Sets and Granted Gameplay Effects)
Without any clamping applied, the Health Attribute value will go up indefinitely.
And similarly go down if we apply the Degen effect (which is done in the Content Examples project by stepping onto the Button Pad Blueprint).
With DataTable
This is the simplest and quickest way to get clamping done for Gameplay Attributes.
This implies you're using a DataTable to initiliaze your Attribute values (eg. Data Tables section).
If we build upon our previous Attribute Set example, with a single Health Attribute, we can tweak the DataTable used for initialization and set the Min and Max value columns to arbitrary values. In the following example, we'll init the value to 0, but configure the clamping to ensure the value will be between 10 and 100.
If we launch the game in PIE, we can see the value for the Health Attribute initialized to 10 (started at 0, then clamping took place and set it to 10)
To test lower and higher bound limits we configured for the clamping, we can use the Regen / Degen effect described in the Test Setup section.
With Gameplay Clamped Attribute Data properties
Add a new Attribute for your set, but this time using a FGBAGameplayClampedAttributeData
. You can quickly do so by clicking on the "Add Attribute" button in the Blueprint Editor Toolbar, and choosing the appropriate type.
Once again, you can find the implementation described here as part of the Content Examples project in the 02_Builtin_Clamping
map.
This time, we are going to implement an Endurance
Attribute (a classic! sometimes also called Stamina) that could act as a resource cost for a given set of actions. In this simple example, we'll reuse the Regen mechanic and temporarily increase the higher clamp limit when we step onto a Button Pad.
If you then compile the Blueprint and select the newly added variable, you'll see in the Details panel a slightly different display.
FGBAGameplayClampedAttributeData
is a child struct of FGameplayAttributeData
, it contains the usual Base Value, along with two new FGBAClampDefinition
properties: a MinValue
and a MaxValue
.
The Clamp definition for both of these provides a Clamp Type
behavior:
- Float Based (default): Uses a simple, static float for the clamping.
- Attribute Based: Performs a clamping based upon another attribute.
- None: Clamping is disabled for this definition.
We need another Attribute to demonstrate the Attribute Based clamping type for our Endurance
attribute called MaxEndurance
. Let's quickly create the new attribute.
You can then give it an arbitrary value (and / or use an initialization DataTable to set its value):
We can then go back to the clamped definitions of Endurance
, by selecting the variable or using the Class Defaults view, and adjust the clamping definition for MaxValue
to be based on the MaxEndurance
attribute.
We also changed the initial Base Value to be at 100 to make it start from the higher limit.
Take note that only the Attributes belonging to the same Attribute Set are intentionally selectable. This deliberate design choice serves to prevent interdependence between different Attribute Set classes and to maintain the principle of encapsulation.
Let's quickly create a few Gameplay Effects to play around our new Endurance
attribute value. We'll need:
- A regen Gameplay Effect to continuously add a certain amount of
Endurance
point. - A Gameplay Effect to arbitrarely change the value of
MaxEndurance
attribute (imagine equipping an item that increase the total amount of Endurance available) which is going to indirectly affect the maximum value for theEndurance
attribute.
The few following Blueprints are set to "Show Only Modified Properties"
GE_Clamping_EnduranceRegen
This is an infinite Gameplay Effect
GE_Clamping_MaxEnduranceBuff
This is a duration Gameplay Effect, meaning it will only last for 2 second, and increase MaxEndurance
by 100%.
Don't forget to apply GE_Clamping_EnduranceRegen just like we did for the HealthRegen as one of the startup effects to apply.
Time to PIE test. Here we used the AbilitySystem.DebugAttribute Endurance MaxEndurance
command once to display their value as an additional overlay.
By stepping onto the Pad Button, we apply the Gameplay Effect buff to increase temporarily our MaxAttribute
value for 2s (or removed when we step out of the Pad).
Manual Clamping
Manual clamping resort to your own implementation using some of the Blueprint exposed methods of UAttributeSet you can implement or override.
Using PreAttributeChange() and PreAttributeBaseChange()
Click on the "Override" dropdown in the functions tab to override the function PreAttributeBaseChange
in Blueprint.
Same for PreAttributeChange
function.
And we'll implement both exactly the same way (you could create an helper function in your Blueprint to handle the clamping, and call that method from both of these places).
Switch on Gameplay Attribute
K2 Node is a custom node implemented by the plugin (originally implemented in GAS Companion, and ported over here in Blueprint Attributes). This is one of the few features both plugins share and each implement their own identical version of the node.
In Value
float represents the initial Base or Current value (Base for PreAttributeBaseChange
method, Current - sometimes also called Final - Value for the PreAttributeChange
).
Both of these methods must return an Out Value
float which is going to be new clamped value for the Attributes we care about (here we only deal with the Shield Attribute). For any other attributes that doesn't need clamping, we simply return the initial In Value
.
Using PostGameplayEffectExecute()
When the AttributeSet Blueprint is first created, the event is implemented by default but disabled.
If the event the node is missing or you previously deleted it, you can click on the "Override" dropdown again in the functions tab to implement the PostGameplayEffectExecute
event in Blueprint.
As for the implementation, you can implement the clamping using ClampAttributeValue
helper of UGBAAttributeSetBlueprintBase
(displayed Attribute Set Blueprint Base
) parent class.