In this post, I want to discuss ways to deal with Damage Types using Gameplay Tags and Gameplay Effects, how to detect damage types and play the corresponding Gameplay Cue based on the damage that was dealt. We will also cover things like resistances to certain damage types, whilst utilizing as little Attributes as possible.
One thing i see a lot and asked a lot is how to do damage types, how to do resistances, etc. I see many people having 10-20 attributes for different resistances, damage buffs, etc. For example:
As you can see above, we hit a bit of a problem, if we want to now handle another damage type, we have to add more Attributes, this can be quite a lot if you have a complex game.
Using a Gameplay Tag to determine your damage, is a simple and clean way to handle multiple damage types, without needing multiple attributes. We can have one Attribute for Resistance and can pull the values we need by utilizing the AssetTags on the Gameplay Effect and Owned Tags on the Source or Target (Discussed further down). A simple tag heirachy could be:
As you can see we have 4 different Damage Types here, represented just by a Gameplay Tag.
Gameplay Effects, along with Gameplay Tags, is a very powerful combination. You can do a lot of things with Gameplay Effects, block things with specific tags, only apply things with specific tags. A few key things (we will be using for this example) are below. I will do a more thorough write up of Gameplay Effects in another post, but for now, we will focus on just what we need for this example.
Attribute Modifier Restrictions
You can make a Gameplay Effect only evaluate and use an Attribute if Tag Requirements are met, take this example:
From the above, this is a Duration based Gameplay Effect, that has an Attribute Modifier for the DamageResistance attribute, which adds a Damage Resistance value (based on the level). But this attribute modification has a special requirement, it requires the Source to have provided the DamageType.Elemental.Fire for this Attribute modification to happen. This is the important bit, if say we through a fireball at the player, that fireball applies a Gameplay Effect that has the DamageType.Elemental.Fire tag. This would mean this attribute would be evaluated in the Damage Calculation, and the resistance would be 25 (if the Gameplay Effect was at level 3 as the example above). If they had a snowball thrown at them, which has the DamageType.Elemental.Ice, then this resistance would not be in the calculation, and the resistance would be 0 (if no resistance of the ice type was applied from another GameplayEffect).
Gameplay Effect Asset Tags
Effect Asset Tags are very powerful, we can use them to describe what this Gameplay Effect is, what it does, how things should react to it (Attributes), etc. Lets show an example, and i will explain a bit more.
So here is a very simple Damage Gameplay Effect, that is Instant, applies 26 Damage at Level 4. The crucial part here, is the Gameplay Effect Asset Tags.
As you can see we have 2 tags, the Damage Type, and the Effect Type, the latter can be used for further details about the GameplayEffect, but our focus will be on the Damage Type for this example.
When a GameplayEffect is applied, the tags from the source and target are read, aggregated, and passed into the evaluators. This includes tags from Granted Tags and Effect Assets Tags as well as any Tags applied to the Source/Targets ASC.
Damage Execution Calculation
We need to touch on the Damage Execution calculation and how we can utilize what we did above, to produce a final damage value. This is a very crude (and should be taken as example) calculation.
The first thing we need to do in our Damage Calculation is grab the tags from the source and the target, and put them in a special struct to pass to the evaluators.
//Grab the tags from source and target
const FGameplayTagContainer* SourceTags = Spec.CapturedSourceTags.GetAggregatedTags();
const FGameplayTagContainer* TargetTags = Spec.CapturedTargetTags.GetAggregatedTags();
//Setup our evaluate params
EvaluateParameters.SourceTags = SourceTags;
EvaluateParameters.TargetTags = TargetTags;
This struct EvaluateParameters will be passed into the evaluators for the attribute we need, and return the value. Example below:
//Grab any damage resistances from the target.
float DamageResistance = 0.0f;
ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(AresDamageStatics().DamageResistanceDef, EvaluateParameters, DamageResistance);
//Calculate the Outgoing Ability Damage.
float AbilityDamage = 0.0f;
ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(AresDamageStatics().OutgoingAbilityDamageDef, EvaluateParameters, AbilityDamage);
As you can see above, we are getting the Damage Resistance from the target and passing in our evaluate struct, which has our Source and Targe Tags, this will return our Damage Resistance. We are also grabbing the Ability Damage (which we set in the Gameplay Effect).
Reading the Damage Resistance for a Specific Damage Type
One thing you may be asking, is how can i read the Damage Resistance for a specific type, if its a single attribute? Well fear not, Gameplay Ability System has you covered.
This node will allow you to evaluate an attribute and return the float value, based on the tags provided, for example:
This will show the Damage Resistance to Fire Elemental Damage. Useful for things like UI, etc.
Putting it all together
We will now put it all together, and do a test, and ill show you some IDE outputs for different test scenarios, showing the power of Gameplay Tags for your Damage Type.
In this example, i will apply two Gameplay Effects at level 1, one with Ice Resistance and one with Fire Resistance:
As you can see, they both have different values at level 1, Fire is 5 resistance, Ice is 3 resistance.
The damage GE will apply Fire Damage
As seen in the AbiltySystem Debugger
We have our two resistances applied, but as you can also see they require a source tag. You can also see our DamageResistance is 0 because nothing is telling those modifiers to apply.
Now when we apply our Damage Gameplay Effect, our execution will fire, and will grab the tags relevant for that GE execution:
As you can see, our Source Tags do indeed contain our Elemental.Fire Damage Type. This is because it was in the Gameplay Effects Asset Tags.
Now we get on to the Damage Resistance Calculation
As you can see, when we ran the evaluator, we got the correct Damage Resistance of 5, not 8 and not 0. This is because it matched the tags, and only returned the relevant attribute value.
I really hope this helps with understanding how to do this, and hopefully you can see the power in it, and how powerful Gameplay Tags are with Gameplay Effects.
If you need more help, have questions, or would like me to focus on other topics, you can see my contact details in the About Me page.