Gameplay Ability System·Gameplay Abilities·Programming·Unreal Engine

Unreal Engine Gameplay Ability System (GAS): Introduction

GAS is Unreal Engine's framework for abilities, stats and effects. Its six pieces, one attack traced through them, and three rules that prevent most bugs.

When I started learning the Gameplay Ability System ( GAS ), tutorials often felt like they started in the middle. I needed to understand how the pieces connected before looking at each one separately.

GAS is a good way to build abilities and stats in Unreal Engine, but it takes time to learn. Here we will start from the beginning, look at the problems it solves and follow one attack through its parts.

Everything here applies to Unreal Engine 5. When something changed in a specific version, for instance the modular Gameplay Effects of UE 5.3, I will say so.

What is the Gameplay Ability System ( GAS )?

The Gameplay Ability System ( GAS ) is Unreal Engine’s framework for abilities, stats and status effects. It comes with the engine as the Gameplay Abilities plugin, which you enable from Edit -> Plugins. Everything a character can do and everything currently applied to it lives on one replicated component, and GAS handles the networking of all of it, client prediction included.

What problem does GAS solve?

GAS solves the problem of gameplay state written by hand. Without it, abilities, stats and status effects end up as booleans, timers and RPCs spread across your character class, where nothing is reusable and nothing replicates by itself. GAS turns all of that into data owned by one replicated component.

In a small prototype, a float Health, a bIsStunned boolean, cooldown timers and some RPCs can be enough. It gets harder when the systems interact. Poison ticks over time, stun blocks attacks, attacks interrupt blocks, and a second player needs to see all of this too.

Epic built the plugin for these problems in their own games, and their Lyra sample uses it too. It gives us:

  • Abilities and stats become data. An ability is an asset and a stat change is an asset, so designers can create and tune attacks and buffs without touching C++ every time.
  • Stats are replicated and server authoritative. You do not write a health value directly, you apply an effect and the system handles the replication and the clamping.
  • Client prediction is built in. The ability runs immediately on the machine of the player and the server confirms or rejects it afterwards, so the game feels instant on a laggy connection without letting the clients decide the outcome.

In my opinion that last point is the real reason to learn GAS, networked ability prediction takes tons of time to get right if you build it yourself.

The official entry point is Epic’s Gameplay Ability System documentation, and I also keep a list of GAS resources if you want to go deeper after this post.

When should you not use GAS?

GAS is not a good fit for things that are not a replicated stat and are not gated by character state. Pure UI logic does not belong in an ability, and a scripted door that opens once does not need tag requirements and a cooldown. If nothing replicates and nothing is a stat, you simply do not need GAS there.

Once you get used to GAS it can be tempting to use it everywhere. I use it for behavior that changes replicated numbers, depends on character state or needs to feel responsive with latency. For other behavior, I would check whether the extra setup is needed.

GAS Components: Abilities, Effects, Attributes, Tags and Cues

GAS is built from the Ability System Component, Gameplay Abilities, Gameplay Effects, Attributes, Gameplay Tags and Gameplay Cues. Let’s see what each one does.

The Ability System Component

The Ability System Component ( ASC ) is the hub of everything. It is an actor component, UAbilitySystemComponent, that owns the abilities a character has, the effects applied to it, its attributes and its tags. When you are debugging “why did this ability not fire?”, the answer is always in the ASC.

Gameplay Abilities

Gameplay Abilities are the actions of your character, attack, dodge, cast, reload. A UGameplayAbility has its own activation rules, cost and cooldown, and anything inside it that takes time, like playing a montage, runs as an ability task. I wrote before about how ability instancing policies work and about the ability tasks you will use inside them.

Gameplay Effects

Attribute changes go through Gameplay Effects. That includes damage, the stamina cost of a dodge, a fireball cooldown, buffs and poison that ticks every second. We use a UGameplayEffect for something that changes a number or stays on a character over time.

Since UE 5.3, effects are built from modular pieces. I cover those in the Gameplay Effect Components post, and the duration rules in Making Sense of Gameplay Effect Durations.

Attributes and Attribute Sets

Attributes are the stats, Health, Stamina, Strength, MoveSpeed. They live in UAttributeSet objects owned by the ASC, they are replicated, and they have pre change and post change callbacks where you clamp values and react to thresholds. I have a full tutorial on creating your own Attribute Sets.

Gameplay Tags

Gameplay Tags are how you represent state. An FGameplayTag is a hierarchical name like CombatStateStunned, and tags gate everything, if an ability can activate, if an effect can apply, in what state a character is. Technically tags are an engine wide module and not part of the GAS plugin, but GAS uses them for everything. I wrote about using Gameplay Tags to store game state a while ago and it is still one of my favorite patterns.

Gameplay Cues

Gameplay Cues are the cosmetic part, the blood splat, the impact sound, the camera shake. They are triggered through special tags, everything under GameplayCue, and they are fire and forget. A cue should not affect gameplay state, and this is what makes it safe to play them predictively on the clients: if a client mispredicts a particle nothing happens, if it mispredicts your health you have a desync.

How does an attack work in the Gameplay Ability System?

For this light attack, the flow is input → ASC → ability → effect → attribute → cue, with tags gating each step. Starting from the button press:

  1. Input. The player presses attack. Your input layer sends a gameplay event to the ASC of the character, or activates the ability directly, both are common.
  2. The ASC checks the gate. TryActivateAbility runs CanActivateAbility on your melee ability. Are there blocking tags present ( ex: a stun )? Can the cost be paid? Is it on cooldown? These checks are answered with tags, attributes and effects.
  3. The ability runs. It commits its cost and cooldown, which are two Gameplay Effects, and then plays the attack montage.
  4. The hit becomes an effect. When the swing connects, the ability builds a damage effect spec ( an FGameplayEffectSpec ) and applies it to the ASC of the victim.
  5. The effect changes an attribute. The damage calculation reads the attributes of the attacker and the victim, computes the final number and lowers the Health of the victim on the server. The new value replicates to every client.
  6. A cue plays the feedback. The damage effect fires a cue tag like GameplayCueCombatImpact, and the notify listening for that tag plays the VFX and the sound.

Heals, fireballs, staggers and buff pickups follow variations of this flow too.

That being said, the pieces do not only flow in one direction. Effects can grant abilities ( ex: a burning status that gives you a Panic ability while it lasts ), attribute callbacks can apply more effects, and tags granted by effects gate other abilities. So more than a pipeline it is a web, the flow above is simply the most common path.

Where should the Ability System Component live?

The UAbilitySystemComponent lives either on the pawn or on the PlayerState, and the difference is what survives death. A pawn owned ASC is destroyed with the pawn, a PlayerState owned one keeps its buffs, cooldowns and granted abilities through a respawn.

On the pawn, the setup is one InitAbilityActorInfo(this, this) call, players and AI run through the same code path, and everything is simple. The catch is that everything is gone when you respawn.

On the PlayerState, which is what Lyra does, buffs and cooldowns carry over. It costs a little bit of complexity, as the owner and the avatar are now different actors and you have to re-point the avatar on every possession.

For my action games, death is a hard reset, so I put the ASC on the pawn. AI enemies have no PlayerState, and this keeps them on the same code path as players.

If loadouts and cooldowns should survive fast respawns, I would use the Lyra setup instead. Either can work; decide which state needs to survive death before choosing, or buffs may disappear when you did not expect them to.

What are the three rules that prevent most GAS bugs?

Most GAS bugs come from breaking one of these rules: the server decides, attributes change only through Gameplay Effects, and states are tags instead of booleans.

The first one is that the server is authoritative. Gameplay changing logic runs on the server, clients predict to hide the latency but they do not decide. If you catch yourself changing a real value on a client and hoping that it sticks, you are fighting the framework.

That being said, this evolves a little bit in production. I started strictly server authoritative, which is the textbook model. Over time, working on coop games, I have moved more authority to the client, as it makes networked combat feel more responsive, and in coop the players are on the same team so cheating matters much less than in PvP. But that is a trade you make later. While you are learning GAS, stay server authoritative.

The second rule is that attributes change only through Gameplay Effects. Do not write Health = X from gameplay code:

// This works in standalone and desyncs in multiplayer:
HealthSet->SetHealth(HealthSet->GetHealth() - 25.f);

// This is the GAS way: build an effect spec and apply it.
FGameplayEffectSpecHandle Spec =
    ASC->MakeOutgoingSpec(DamageEffectClass, /*Level=*/1.f, ASC->MakeEffectContext());
ASC->ApplyGameplayEffectSpecToTarget(*Spec.Data, TargetASC);

What you apply is not the effect asset by itself, it is an FGameplayEffectSpec, the effect plus a level plus the context of who applied it. The level is what lets a single damage effect scale from a level 1 hit to a level 20 hit without a second asset.

Effects handle replication, prediction, clamping and stacking. A raw write skips these, so the value can snap back or desync in multiplayer.

The third rule is that tags gate everything. “Is the player stunned?” is not a boolean on your character class anymore, it is a question you ask the ASC:

if (ASC->HasMatchingGameplayTag(
        FGameplayTag::RequestGameplayTag(TEXT("Combat.State.Stunned"))))
{
    return;
}

The stun effect grants the tag and removes it when the effect expires. Each ability that should fail while stunned lists CombatStateStunned in its blocked tags. Other systems do not need to manage a separate stun variable.

Recap

GAS gives us data driven abilities and stats, server authoritative replicated attributes and client prediction. For something that does not involve a replicated number, character state or latency, it may be more setup than we need.

The attack we followed goes from input to ASC, ability, effect, attribute and cue, with tags controlling the checks along the way. Keep the server authoritative while learning, change attributes through Gameplay Effects and represent state with tags. These rules avoid most GAS bugs. Also decide where the ASC lives early, since that determines what survives a respawn.

Keep reading

PlayMontageAndWait in GAS: Montage Replication

PlayMontageAndWait in GAS: Montage Replication

Montage_Play works on your screen and nowhere else. The ASC is the one that tells everybody else.
GiveAbilityAndActivateOnce: One-Shot Abilities Without the Cleanup

GiveAbilityAndActivateOnce: One-Shot Abilities Without the Cleanup

Grant an ability, activate it once, and let GAS remove the spec by itself.
Learning about Ability Instancing Policy in GAS

Learning about Ability Instancing Policy in GAS

So many instancing types!