[UE5] GAS 101: The Mechanism That Handles Shared Ability Logic, and Whether to Adopt It

Created: 2026-07-21Last updated: 2026-09-07

Understand the Gameplay Ability System's role through a spell costing 20 MP with a 5-second wait. Explains how Ability, Effect, Attribute, and tags relate, the difference between blocking activation and interrupting, a small hands-on, and criteria for deciding to adopt it.

You built MP costs and cooldowns for a fireball and an ice spear. Next you want "unusable while stunned" and "equipment changes the MP cost". Writing the same checks across your abilities means eventually forgetting to fix one of them.

The Gameplay Ability System (GAS) is the mechanism for assembling this kind of shared ability logic. MP changes, how long effects last, and the conditions that permit activation are handled by shared parts.

Each spell's own behavior alongside the shared mechanism supporting MP cost, wait time, and state checks

Adopting GAS does not finish your game's rules automatically, though. This article organizes what you can delegate to it, then tries the activation conditions for a spell costing 20 MP and reusable after 5 seconds . With that, we consider whether adopting it is worth it for your game.

What You'll Learn

  • Who manages Ability, Effect, Attribute, and tags
  • How MP cost and cooldown are expressed as Gameplay Effects
  • How to confirm what can and cannot activate through a small experiment
  • The axes for deciding between GAS and your own shared logic

The first half explains the mechanism. Running the second half needs Blueprint variables and node work plus the build environment from the C++ introduction.

Sponsored

What to delegate to GAS and what you decide

Abilities have their own behavior, such as "launch a fireball", and shared logic, such as "check whether the required MP is available". GAS provides the foundation handling that shared part.

Mechanism GAS providesWhat you decide
Checking whether a cost is payable and applying itWhat is consumed, how much, and when it is paid
Managing effect durationsHow many seconds, and whether overlaps extend it
Restricting activation based on tagsWhich tags represent stuns and equipment
Aggregating effects on AttributesDamage formulas, HP limits, death handling
Supporting Ability execution over the networkServer and client roles and synchronization settings
Keeping each spell's own behavior while gathering MP, time, and activation conditions into shared parts

Target selection, projectile collision, animation, and UI are built to suit your game. GAS does not decide "where it lands" for you.

You could also gather logic into functions or Components yourself. What you want to compare is not just individually written abilities against GAS but your own shared mechanism against GAS's . Weigh the features your game needs against the learning and maintenance effort.

See five parts through a fireball

Rather than memorizing acronyms, start with "who does what when you cast a spell".

Five parts handling the spell's steps, effects, values, and tags with the ASC as the entry point
PartIts role here
Ability System Component (ASC)The GAS entry point an Actor owns. Manages usable Abilities and applied Effects
Gameplay Ability (GA)The ability logic for "what happens when activated"
Gameplay Effect (GE)Settings for effects such as "reduce MP by 20" or "apply a tag for 5 seconds"
Attribute SetThe container defining the values GAS handles, such as MP and HP
Gameplay TagA hierarchical label representing kinds and states

For a fireball, input asks the ASC to activate. When conditions are met the GA begins, and committing the cost applies the MP consumption and wait-time GEs. The GA holds the "launch the fireball" logic beyond that.

Separating Ability as steps, Effect as effects, and Attribute as values keeps things from getting lost as names pile up. Our MP is called Mana in code and settings fields. Gameplay Tag hierarchies are also covered in the article on classifying enemies with tags.

Attribute and Effect: how do numbers change?

An Attribute is a value GAS handles, such as MP or attack power. In C++ it uses the type FGameplayAttributeData and holds two values, Base and Current .

  • Base : the value that effect calculations are based on. It does not mean a maximum.
  • Current : Base with currently active modifiers applied. The value the game uses as the present strength.

With a Base attack power of 10 and a five-second buff of +5, Current becomes 15. When the buff expires, Current returns to 10. A buff raises an ability and a debuff lowers it.

Attack power's Base staying 10 while Current becomes 15 during a buff and returns to 10 afterwards

GEs that change numbers have these duration types. This table assumes ordinary value modification without a period.

Duration PolicyHow it worksExample
InstantExecutes once and changes BaseSpend 20 MP, take 30 HP damage
Has DurationModifies for a set time and removes the modifier at the endA 5-second attack power boost
InfiniteKeeps modifying until explicitly removedAn attack boost while equipped

Settings also differ between "damage where HP returns after 5 seconds" and "poison chipping HP for 5 seconds". The latter uses a Period to execute the effect at a fixed interval. In that case the reduced Base does not come back when the poison ends.

Gameplay Effects are built as Blueprints with GameplayEffect as the parent and configured mainly in Class Defaults. Effect here means an effect on the game's values and states , not a visual effect like flames or smoke.

MP cost and wait time are Effects too

Cost is what an ability consumes to be used and Cooldown is the wait before it can be used again. GAS expresses both with GEs.

An Instant Cost reducing Mana by 20 and a 5-second Cooldown Effect applying a tag

Our Cost is "an Instant GE adding -20 to Mana". The Cooldown is "a GE applying the tag Cooldown.Fireball for 5 seconds". A cooldown GE does not need any value-changing settings.

Specify each GE in the Ability's Cost and Cooldown fields. When activation is attempted, GAS confirms the cost is payable and that it is not on cooldown.

The actual payment happens when Commit Ability is called inside the Ability. Commit rechecks the cost and cooldown and applies both if they pass. Forgetting to call it ends with only the activation check, leaving MP and wait time unchanged.

Confirming conditions when activation is attempted, then consuming MP and starting the cooldown on Commit

End Ability , which ends the Ability, and the cooldown ending are separate too. Finishing the spell-casting steps immediately still leaves the 5-second GE in place. Calling End Ability does not make it reusable right after.

Tags stop new activations

A stun temporarily restricts action. In GAS, giving the target's ASC State.Stunned and specifying the same tag in the ability's Activation Blocked Tags blocks activation while it lasts.

A fireball with Activation Blocked Tags set not activating while State.Stunned is present

That is a setting that blocks new activations , though. Interrupting an Ability mid-cast needs cancellation logic. It does not automatically stop normal movement or projectiles already in flight.

Ability tag fieldWhat it represents
Asset Tags (formerly Ability Tags)That Ability's kind. For example, Ability.Fireball
Activation Blocked TagsTags that block activation when the activating Actor's ASC has them
Activation Required TagsTags that block activation when the activating Actor's ASC lacks them
Activation Owned TagsTags applied to the activating Actor's ASC while the Ability runs

Registering a tag in the tag dictionary does not put it on an ASC. In our hands-on we apply a GE granting State.Stunned for three seconds. Tags placed in your own Gameplay Tag Container variable do not become ASC tags either.

You can group with parent tags, but decide the range concretely. Blocking plain State also catches other states you add later. Start with State.Stunned alone.

Sponsored

Preparation: build a foundation for trying GAS

From here we try the fireball's activation conditions on a small scale. Activating reports through Print String and we watch the MP change. Projectiles and enemy damage are what you combine with the Projectile Movement article and the like once the conditions work.

When finished, pressing F takes Mana from 100 to 80. Pressing again immediately keeps it at 80. Waiting over five seconds and pressing takes it to 60. We also apply a stun from another key and confirm activation stops.

Mana going 100 to 80 the first time, staying 80 immediately after, and reaching 60 after waiting five seconds

Prepare the project

  1. Use the same CppFirstStep Third Person C++ project as the C++ introduction. Creating a new one with that name is fine.
  2. Enable "Gameplay Abilities" in "Edit → Plugins" and restart the editor.
  3. In PublicDependencyModuleNames.AddRange(...) in Source/CppFirstStep/CppFirstStep.Build.cs , add these three to the existing list. That lets C++ use GAS parts. Keep Core, Engine, and so on and separate entries with commas.
"GameplayAbilities", "GameplayTags", "GameplayTasks"
  1. Create GASPractice with Actor as the parent from "Tools → New C++ Class". After generation, close the editor and replace the following two files.

We put the MP-owning class and the practice Actor that uses it in the same header. In standard GAS, the Attribute Set corresponding to this MP definition is built in C++. Abilities and GEs on top of it can be configured in Blueprint.

GASPractice.h

The header defines the MP container and three operations callable from Blueprint. CPPFIRSTSTEP_API is a marker derived from the project name. For a differently named project, match the _API name in the generated GASPractice declaration.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "AbilitySystemInterface.h"
#include "AbilitySystemComponent.h"
#include "AttributeSet.h"
#include "GASPractice.generated.h"

class UGameplayAbility;
class UGameplayEffect;

// A container holding MP as a GAS value.
UCLASS()
class CPPFIRSTSTEP_API UPracticeManaSet : public UAttributeSet
{
    GENERATED_BODY()

public:
    UPROPERTY(BlueprintReadOnly, Category = "Practice")
    FGameplayAttributeData Mana;

    GAMEPLAYATTRIBUTE_PROPERTY_GETTER(UPracticeManaSet, Mana)
    GAMEPLAYATTRIBUTE_VALUE_GETTER(Mana)
    GAMEPLAYATTRIBUTE_VALUE_INITTER(Mana)
};

// An Actor placed once in the level to try GAS from key input.
UCLASS()
class CPPFIRSTSTEP_API AGASPractice : public AActor, public IAbilitySystemInterface
{
    GENERATED_BODY()

public:
    AGASPractice();
    virtual UAbilitySystemComponent* GetAbilitySystemComponent() const override;

    UFUNCTION(BlueprintCallable, Category = "Practice")
    void TryUseSpell();

    UFUNCTION(BlueprintCallable, Category = "Practice")
    void ApplyTestStun();

    UFUNCTION(BlueprintCallable, Category = "Practice")
    void ShowMana();

protected:
    virtual void BeginPlay() override;

    UPROPERTY(VisibleAnywhere, Category = "Practice")
    TObjectPtr<UAbilitySystemComponent> AbilitySystem;

    UPROPERTY()
    TObjectPtr<UPracticeManaSet> Attributes;

    UPROPERTY(EditDefaultsOnly, Category = "Practice")
    TSubclassOf<UGameplayAbility> SpellAbility;

    UPROPERTY(EditDefaultsOnly, Category = "Practice")
    TSubclassOf<UGameplayEffect> StunEffect;
};

GAMEPLAYATTRIBUTE_... are macros providing the logic that conveys MP to GAS along with value reading and initialization. Rather than tracing each mechanism, you can treat them as the part that makes Mana selectable from a GE and move on.

GASPractice.cpp

The implementation creates the ASC and MP container, initializes them at Play, and registers the Ability. TryUseSpell, used from the F key, asks the ASC to activate and then displays the current MP.

#include "GASPractice.h"
#include "Abilities/GameplayAbility.h"
#include "GameplayAbilitySpec.h"
#include "GameplayEffect.h"
#include "Components/SceneComponent.h"
#include "Kismet/KismetSystemLibrary.h"

AGASPractice::AGASPractice()
{
    PrimaryActorTick.bCanEverTick = false;
    SetRootComponent(CreateDefaultSubobject<USceneComponent>(TEXT("Root")));
    AbilitySystem = CreateDefaultSubobject<UAbilitySystemComponent>(TEXT("AbilitySystem"));
    Attributes = CreateDefaultSubobject<UPracticeManaSet>(TEXT("Attributes"));
}

UAbilitySystemComponent* AGASPractice::GetAbilitySystemComponent() const
{
    return AbilitySystem;
}

void AGASPractice::BeginPlay()
{
    Super::BeginPlay();
    AbilitySystem->InitAbilityActorInfo(this, this);
    Attributes->InitMana(100.0f);

    if (HasAuthority() && SpellAbility)
    {
        AbilitySystem->GiveAbility(FGameplayAbilitySpec(SpellAbility, 1, INDEX_NONE, this));
    }
    else if (!SpellAbility)
    {
        UE_LOG(LogTemp, Warning, TEXT("GASPractice: Set Spell Ability in Class Defaults."));
    }
    ShowMana();
}

void AGASPractice::TryUseSpell()
{
    if (HasAuthority() && SpellAbility)
    {
        AbilitySystem->TryActivateAbilityByClass(SpellAbility, false);
    }
    ShowMana();
}

void AGASPractice::ApplyTestStun()
{
    if (!HasAuthority() || !StunEffect)
    {
        return;
    }
    FGameplayEffectContextHandle Context = AbilitySystem->MakeEffectContext();
    Context.AddSourceObject(this);
    AbilitySystem->ApplyGameplayEffectToSelf(
        StunEffect.GetDefaultObject(), 1.0f, Context);
    UKismetSystemLibrary::PrintString(this, TEXT("Stun: 3 seconds"),
        true, true, FLinearColor::Yellow, 5.0f);
}

void AGASPractice::ShowMana()
{
    UKismetSystemLibrary::PrintString(this,
        FString::Printf(TEXT("Mana: %.0f"), Attributes->GetMana()),
        true, true, FLinearColor::Cyan, 5.0f);
}
GASPractice owning the ASC and Mana container, initializing them, then registering the Ability

InitAbilityActorInfo(this, this) 's two arguments are the Actor owning GAS and the Actor actually using Abilities. Both are GASPractice itself here. An Attribute Set created on the same Actor is discoverable from the ASC.

GiveAbility registers it as "an ability this Actor can use". Registering alone does not activate it; TryActivateAbilityByClass requests activation.

With the editor closed, build the project in Visual Studio with "Development Editor / Win64". Reopen the editor once it succeeds. The C++ so far is the practice foundation; next we configure the effects and ability.

Hands-On: try 20 MP and a 5-second wait

1. Prepare the tags and three Effects

Register Cooldown.Fireball and State.Stunned from "Edit → Project Settings → GameplayTags". The registration steps are the same as in the Gameplay Tag article.

In the Content Browser, choose GameplayEffect from "Blueprint Class → All Classes" and create these three assets. Open each and configure Class Defaults.

AssetDuration PolicySettings
GE_Cost_FireballInstantOne entry in Modifiers. Attribute=PracticeManaSet.Mana, Modifier Op=Add, Magnitude's Scalable Float=-20
GE_Cooldown_FireballHas DurationDuration Magnitude's Scalable Float=5.0. Grants Cooldown.Fireball
GE_Stun_PracticeHas DurationDuration Magnitude's Scalable Float=3.0. Grants State.Stunned

A Modifier is how the value changes and Magnitude is by how much. We enter fixed values in the Scalable Float field rather than using formulas or tables. Period stays 0.

Three Effect settings: Cost adding -20 to Mana, Cooldown a 5-second tag, and Stun a 3-second tag

On the two tag-granting GEs, add Grant Tags to Target Actor under "Components" and specify the tag in the "Add Tags" field. That is the part which "applies a tag to the target's ASC while the effect lasts". "Asset Tags" classifies the GE itself and is not a substitute.

Explanations for UE5.2 and earlier show a screen setting Granted Tags directly. This article assumes the UE5.3-and-later screen using Components.

2. Assign Cost and Cooldown to the Ability

Create a Blueprint with GameplayAbility as the parent and name it GA_Fireball . Configure Class Defaults like this.

SettingValue
Cost Gameplay Effect ClassGE_Cost_Fireball
Cooldown Gameplay Effect ClassGE_Cooldown_Fireball
Activation Blocked TagsState.Stunned
Instancing PolicyInstanced Per Actor
Net Execution PolicyServer Only

Instanced Per Actor keeps one Ability instance per using Actor. This exercise reuses it on every activation. Server Only runs our experiment on the side that decides game logic, and we test it in single-player Play below.

Assigning Cost, Cooldown, and Blocked Tags to GA_Fireball

We specify the Ability by class here, so the Ability's own Asset Tags can be empty. Cooldown.Fireball is the tag the cooldown GE applies to the target.

3. Display activation only when Commit succeeds

Add Event ActivateAbility in GA_Fireball's Event Graph and wire the white exec line into Commit Ability. Commit's exec output goes into a Branch and its red Return Value into the Branch's Condition. Commit's Target is Self.

Running Commit Ability from Event ActivateAbility and branching on the bool Return Value

Wire the Branch's True side into Print String with In String Fireball! and Duration 5.0. Expand the detail pins and turn on Print to Screen and Print to Log. Then call End Ability. The False side goes to End Ability without displaying anything. End Ability's Target is Self too.

Displaying Fireball! and ending on a successful Commit, and simply ending on failure

A and B in the diagram continue the True and False exec lines. Connect them in the same Event Graph; there is no need to create nodes named A or B.

Return Value is Commit's result. A bool represents the two options True (success) and False (failure). Commit Ability is not a node with separate success and failure exec outputs. Confirm against the diagram that you branch by passing the bool result into a Branch .

Put End Ability on both paths. Leaving the Ability running under Instanced Per Actor is a cause of the next activation not being accepted.

4. Place the practice Actor and call from keys

Create a child Blueprint BP_GASPractice from the C++ Classes' GASPractice and choose the following under "Practice" in Class Defaults.

ItemValue
Spell AbilityGA_Fireball
Stun EffectGE_Stun_Practice

Place exactly one BP_GASPractice in the level. It has no visuals, but being selectable in the Outliner is enough. Ability registration and MP initialization in BeginPlay live in C++, so no equivalent logic is needed in the BP.

Select the placed instance in the Outliner and open "Blueprints → Open Level Blueprint". Right-click to create a reference to that instance and build these inputs. Functions can be found by dragging from the reference's output.

  • F Pressed → Try Use Spell
  • G Pressed → Apply Test Stun
  • M Pressed → Show Mana

Connect the same BP_GASPractice reference into each call's Target. We do not use Released.

Calling the placed BP_GASPractice's Try Use Spell from F Pressed

Wire G and M to the same placed Actor as Target as well.

Calling Apply Test Stun with G and Show Mana with M

5. Confirm MP and wait time

Compile and save, then set Play to "Number of Players=1" and "Net Mode=Play Standalone". After Play, click the game screen so it accepts input.

ActionExpected display
Start PlayMana: 100
Press F onceFireball! and Mana: 80
Press F again within 5 secondsNo new Fireball!, Mana: 80
Wait over 5 seconds since the last activation and press FFireball! and Mana: 60
Waiting over 5 seconds each, activate five times totalMana: 0
Wait over 5 seconds more and press FNo new Fireball!, Mana: 0

When a previous Fireball! is still on screen, watch for a new line appearing. You can also read it in the Output Log. Judge by combining the MP change with the success display.

6. Stop activation with a stun

Stop and Play again to return to MP 100. Press G once first and immediately press F. No Fireball! appears while stunned and Mana stays at 100. Pressing G, waiting over three seconds, and pressing F takes Mana to 80 and activates.

G applying a 3-second State.Stunned, F consuming no MP during it, and activation working once it clears

We start the stun experiment in a separate Play so the cause is not mixed with cooldown or insufficient MP. We built no movement-stopping logic here, so the Third Person character still walking is not a contradiction.

When it does not behave as expectedWhere to check
Mana: 100 does not appear at the startBP_GASPractice's placement, the build, the log during Play
Pressing F does not even show ManaThe Level BP's Pressed and Target, focus on the game screen
Mana appears but Fireball! never doesSpell Ability, GiveAbility, the GA's Cost and tag settings
Fireball! appears but MP does not dropThe Cost assignment, Mana's Modifier, the Commit connection
Mashing reduces MPThe Cooldown GE assignment, the 5-second Duration, the granted tag setting
It works once and then never againEnd Ability on both True and False paths, remaining MP
It activates even after GStun Effect, Grant Tags to Target Actor, the GA's Activation Blocked Tags

Compare by changing only the numbers

Stop and change the Cost's -20 to -35. Play again and press F waiting over five seconds each time: MP goes 100 → 65 → 30. Next it cannot pay 35 and stays at 30. We added no "is MP at least 35" condition to the GA's graph.

Then return it to -20 and change the Cooldown's 5 seconds to 2. Being able to change effect settings and activation logic separately is material for judging how GAS feels to use.

Sponsored

Adoption is not decided by ability count alone

The same experiment can be built yourself with an MP variable and a Timer. GAS's value lies less in building this one spell than in continuing to handle effects and conditions through a shared mechanism.

Your situationDirection of the decision
A few abilities run independently with simple conditionsStart from gathering them into your own functions or Components
Cost, wait time, and status ailments are shared across many abilitiesBuild a small GAS prototype and compare configurability and maintenance
Effect stacking and networked ability design are centralTreat GAS as a strong candidate and verify the features you need early
Your own implementation is already stableWeigh what migration gains against how much you rebuild
Deciding adoption by comparing your own shared logic with GAS across required features and maintenance effort

Many abilities do not make it mandatory and few do not make it unnecessary. Some games have few abilities with complex effect combinations, and others have many that differ only by simple data.

When in doubt, build one or two representative abilities in a small project separate from your main one. Include what is difficult in your game, such as MP costs, state-based restrictions, and stacking effects. Comparing "what changed for the second one" and "could you trace a cause when it broke" gives you a picture of post-adoption work.

You can adopt it later, but migration grows when HP, abilities, saves, and UI connections all change. Prototyping before things get large reduces the odds of rebuilding your main game just to make the decision.

Bonus: good to know up front

HP limits and death handling are yours to build

GAS does not clamp something to 0 through max HP just because it is named Health. Logic for falling when HP reaches 0 is built on the game side. Our Mana is limited to an initial 100 and a payable Cost.

Extending seriously means using an Attribute Set's PreAttributeChange, PostGameplayEffectExecute, and the like to decide which limits apply to which values. Gathering effect-driven value changes into GEs makes them easier to trace, though direct value changes are not uniformly forbidden.

Consider what survives after the Actor is destroyed

Our ASC belongs to the practice Actor, so it ends with that Actor. To keep abilities and states when a character dies and is rebuilt, a design placing the ASC on the PlayerState is an option.

Separating the ASC's owner from the Avatar actually using abilities also requires rebinding to the regenerated character. Decide what you want to keep together with the Game Framework roles.

Extending to networking and presentation

This code is single-player practice. Extending to networking means designing Attribute Replication and Actor Info initialization timing. Replication conveys server-side state to clients. Changing one GAS setting does not complete all synchronization.

For visuals, you can use Gameplay Cue , the mechanism for invoking audio, particles, and other presentation in response to abilities and effects. Ability Task helps advance an Ability's logic while waiting for animation ends and events.

The official Lyra sample is study material for reading real examples. It contains plenty of design beyond the foundation, so looking for where our Cost, Cooldown, and Tags correspond makes it easier to decide where to start reading.

Summary

  • Ability is "what to do", Effect is "what changes", Attribute is "the value", and tags are "the current state"
  • MP cost and cooldown are both expressible as Gameplay Effects
  • Reasons activation fails split into cost, cooldown, or tags
  • Ability count alone does not decide whether to adopt it

The question to ask when deciding is "is adding and removing effects likely to grow later?" If it will, GAS; if fixed choices suffice, your own shared logic is enough.

Handling states with tags is in Gameplay Tags and simple damage handling in dealing damage.

Reference: Gameplay Ability System, Gameplay Abilities, Gameplay Effects, Commit Ability.

Unreal Engine Notes in this section98