Practical Implementation (How-To)
Now that you understand the core concepts and have configured your tags and stats in the Editor Dashboard, it is time to build a working combat loop.
This guide will walk you through the foundational steps of GodotGAS: Setting up a Character, creating a Fireball Ability, configuring its Costs/Cooldowns, applying a Damage Effect, and writing complex Execution Calculations.
1. Setting up a Character
Any entity that engages in combat (Player, Enemy, Boss, or Destructible Prop) requires an AbilitySystemComponent (ASC) and an AttributeSet.
Step 1: Add the ASC
- Open your character’s scene (e.g.,
player.tscn). - Add a new child node to the root and search for AbilitySystemComponent.
- Name it exactly
AbilitySystemComponent(this is a standard convention, though not strictly required).
Step 2: Link an Attribute Set
- In the Inspector for your new ASC, locate the
Attribute Setsarray. - Add an element to the array.
- Click the empty slot and select New [YourGeneratedSetName] (e.g.,
New PlayerStats).- Note: If you do not see your custom stats, ensure you generated the script via the GodotGAS Dashboard!
Your character is now fully initialized and ready to receive buffs, take damage, and cast abilities.
2. Creating an Ability (Fireball)
Abilities are discrete Nodes that handle input, animation, and logic.
Step 1: The Script
Create a new script called ga_fireball.gd and have it extend GameplayAbility.
extends GameplayAbility
@export var projectile_scene: PackedScene
@export var damage_effect: GameplayEffect
func _activate_ability() -> bool:
# 1. Pay the mana cost and trigger the cooldown automatically
commit_ability()
# 2. Trigger the casting audio/visuals via the Cue Manager
execute_cue(GameplayTags.Cue_SFX_Fireball_Cast)
# 3. Spawn the physical projectile
var fireball = projectile_scene.instantiate()
fireball.global_position = owner_asc.get_parent().global_position
# Pass the ability reference and the damage data down to the fireball
fireball.setup(self, damage_effect)
get_tree().current_scene.add_child(fireball)
return true
Step 2: Granting the Ability
- Create a new Node in your project, attach your
ga_fireball.gdscript, and save it asga_fireball.tscn. - To give this ability to your player, you must grant it to their ASC. You can do this in the player’s
_ready()function:
@onready var asc = $AbilitySystemComponent
var fireball_ability = preload("res://abilities/ga_fireball.tscn").instantiate()
func _ready():
asc.grant_ability(fireball_ability)
3. Configuring Costs and Cooldowns
In GodotGAS, you do not write hardcoded timers for cooldowns, nor do you manually subtract mana in your scripts. Both of these mechanics are driven purely by GameplayEffect resources. When your script calls commit_ability(), the ASC automatically applies these effects to the caster.
Creating the Cost Effect
- Create a new
GameplayEffectresource and name itge_fireball_cost.tres. - Set the Duration Policy to
Instant. - Under Modifiers, add a new modifier.
- Set the Attribute Name to
mana(or whichever resource your spell uses). - Set the Operation to
Add, and the Magnitude to-20.0.- How it works: Before casting, the ASC automatically projects this math. If subtracting 20 drops the player’s mana below 0, the cast is blocked and an
ability_activation_failedsignal is sent to your UI!
- How it works: Before casting, the ASC automatically projects this math. If subtracting 20 drops the player’s mana below 0, the cast is blocked and an
Creating the Cooldown Effect
- Create another
GameplayEffectresource and name itge_fireball_cooldown.tres. - Set the Duration Policy to
Duration. - Set the Duration to
5.0(seconds). - Under State Management -> Granted Tags, add a tag to represent the cooldown, such as
State.Cooldown.Fireball.- How it works: The ASC applies this effect and grants the tag for exactly 5 seconds. If the player tries to cast Fireball again, the framework sees the
State.Cooldown.Fireballtag and blocks the cast until the 5 seconds are up.
- How it works: The ASC applies this effect and grants the tag for exactly 5 seconds. If the player tries to cast Fireball again, the framework sees the
Linking them to the Ability
Select your ga_fireball.tscn node. In the Inspector, under Ability Mechanics, drag and drop your ge_fireball_cost.tres into the Cost Effect slot, and ge_fireball_cooldown.tres into the Cooldown Effect slot.
4. Building the Damage Effect
When the Fireball hits an enemy, it needs to apply a separate GameplayEffect to deal damage.
Step 1: Create the Resource
- Create a new
GameplayEffectresource and save it asge_fireball_damage.tres.
Step 2: Configure the Math
- Open the resource in the Inspector.
- Set Duration Policy to
Instant(Damage happens immediately). - Under Modifiers, add a new
GameplayEffectModifier. - Set the Attribute Name exactly as it appears in your Attribute Set (e.g.,
health). - Set the Operation to
Add. - Set the Magnitude to
-50.0(Negative numbers subtract!).
Note: For complex, scaling damage (e.g., Damage = Attacker.Magic - Target.FireResist), you would leave Modifiers empty and instead assign a custom GameplayExecutionCalculation script.
5. The Projectile Payload (TargetData)
How does the physical fireball projectile actually pass the ge_fireball_damage.tres to the Enemy’s ASC? It uses the framework’s TargetData architecture.
Here is an example of what the script on your FireballProjectile.tscn (an Area2D or Area3D) should look like. Notice how it receives the data from the ability in the setup() function, and then packages the enemy it hits into a GameplayAbilityTargetData object:
extends Area2D
var owning_ability: GameplayAbility
var damage_effect: GameplayEffect
# Called by ga_fireball.gd right before adding this projectile to the scene
func setup(ability: GameplayAbility, effect: GameplayEffect) -> void:
owning_ability = ability
damage_effect = effect
func _on_body_entered(body: Node) -> void:
# 1. Package the entity we just hit into a standard payload
var target_data = GameplayAbilityTargetData.new()
target_data.append_node(body)
# 2. Tell the original ability to apply the effect to whoever we hit!
if owning_ability and damage_effect:
owning_ability.apply_effect_to_targets(damage_effect, target_data)
# 3. Destroy the projectile
queue_free()
The apply_effect_to_targets function is a built-in framework helper. It automatically grabs the enemy’s ASC, wraps your GameplayEffect in a live GameplayEffectSpec, securely passes along who cast it, and executes the math safely.
6. Advanced Math: Execution Calculations
In Step 4, we used a simple Modifier to deal exactly 50 damage. But what if your game is a complex RPG where Damage = (Attacker.AttackPower * 1.5) - Target.Armor?
You cannot do this with static Modifiers because the Fireball needs to read stats from two different characters at the exact moment of impact.
This is where Execution Calculations (ExecCalcs) come in.
Writing an ExecCalc Script
Create a new script named calc_physical_damage.gd that extends GameplayExecutionCalculation:
class_name CalcPhysicalDamage
extends GameplayExecutionCalculation
func execute(spec: GameplayEffectSpec, target_asc: AbilitySystemComponent) -> Dictionary:
var deltas: Dictionary = {}
# 1. Get the Attacker's ASC from the Context payload
var instigator = spec.context.instigator
var source_asc = instigator.get_node_or_null("AbilitySystemComponent")
if not source_asc or not target_asc:
return deltas
# 2. Read the live attributes
var attack_stat = source_asc.get_attribute("attack")
var defense_stat = target_asc.get_attribute("defense")
var attack_power = attack_stat.current_value if attack_stat else 0.0
var armor = defense_stat.current_value if defense_stat else 0.0
# 3. Perform the dynamic math
var raw_damage = (attack_power * 1.5) - armor
# Make sure we don't accidentally "heal" them if their armor is incredibly high!
var final_damage = maxf(1.0, raw_damage)
# 4. Return the exact attribute we want to modify, and the flat amount
deltas["health"] = -final_damage
# Optional: Inject dynamic tags for the UI to read later!
if final_damage > 100:
spec.inject_tag(GameplayTags.Example_Event_Damage_Critical)
return deltas
Applying the ExecCalc
- Go back to your
ge_fireball_damage.tresfile. - Delete the simple Modifier you made in Step 4.
- Under the Attribute Modifiers -> Executions array, add a new element.
- Click the dropdown and select New CalcPhysicalDamage.
Now, whenever the Fireball hits, GodotGAS will automatically route the payload into your custom math formula before touching the enemy’s health!