All skills
quodsoler avatar

/ue-gameplay-abilities

@f3742d7

Use when implementing or debugging the Gameplay Ability System in C++ — abilities, effects, attributes, cues and the AbilitySystemComponent. Also use when the user mentions 'GAS', 'UGameplayAbility', 'ActivateAbility', 'CommitAbility', 'UGameplayEffect', 'UAttributeSet', 'FGameplayAttributeData', 'AbilitySystemComponent', 'InitAbilityActorInfo', 'GiveAbility', 'TryActivateAbility', 'ability task', 'GameplayCue', 'cooldown', 'cost', 'buff', 'debuff', 'damage execution' or 'ASC on PlayerState'. For montage playback, see ue-animation-system; for replication modes and prediction, see ue-networking-replication.

Use this Skill: https://skilld.dev/gh/quodsoler/unreal-engine-skills/ue-gameplay-abilities

This session only. Nothing lands on disk.

referencesgameplay-effect-reference.md

≈3.7k tokens on demand. Your agent reads this file only when SKILL.md points to it.

GameplayEffect Reference

Duration, modifiers, magnitude calculations, executions, stacking, immunity, queries and the UGameplayEffectComponent list. Target engine: UE 5.8. Header: GameplayEffect.h.


Duration policies (EGameplayEffectDurationType)

Value Behaviour
Instant Executes once against the base value; never enters the active container
Infinite Aggregates onto the current value until removed
HasDuration Aggregates for DurationMagnitude seconds, then is removed

Only executions reach UAttributeSet::PostGameplayEffectExecute: Instant effects and each period of a periodic effect (see Periodic effects). Non-periodic duration and infinite effects work through aggregators on the current value, so they surface in PreAttributeChange.


Modifier operations (EGameplayModOp)

EGameplayModOp is a namespaced enum: the type is EGameplayModOp::Type and a stored value is a TEnumAsByte<EGameplayModOp::Type>. Aggregated values are applied as

((BaseValue + AddBase) * MultiplyAdditive / DivideAdditive * MultiplyCompound) + AddFinal
Value Effect
EGameplayModOp::AddBase Adds to the base value first
EGameplayModOp::MultiplyAdditive Multipliers are summed, then multiplied against the running result
EGameplayModOp::DivideAdditive Divisors are summed, then divided against the running result
EGameplayModOp::MultiplyCompound Multiplied into the running result, compounding
EGameplayModOp::AddFinal Added after all multiplication
EGameplayModOp::Override Replaces the computed value

Additive, Multiplicitive (engine spelling) and Division still exist as hidden back-compat aliases for AddBase, MultiplyAdditive and DivideAdditive. Write the modern names.


Magnitude calculations (EGameplayEffectMagnitudeCalculation)

ScalableFloat, AttributeBased, CustomCalculationClass, SetByCaller.

ScalableFloat

An FScalableFloat: a constant, or a UCurveTable row evaluated at the effect level.

AttributeBased

Derives the magnitude from a captured attribute: (Coefficient * (PreMultiplyAdditiveValue + AttributeValue)) + PostMultiplyAdditiveValue.

EAttributeBasedFloatCalculationType picks which value is read:

Value Reads
AttributeMagnitude Final evaluated value, including active modifiers
AttributeBaseValue Base value only
AttributeBonusMagnitude Final minus base
AttributeMagnitudeEvaluatedUpToChannel Evaluation stopped at a chosen channel

CustomCalculationClass

The examples below capture two attributes from a combat attribute set declared the same way as UMyHealthSet: ATTRIBUTE_ACCESSORS_BASIC(UMyCombatSet, AttackPower) generates static FGameplayAttribute GetAttackPowerAttribute(); and ATTRIBUTE_ACCESSORS_BASIC(UMyCombatSet, Armor) generates static FGameplayAttribute GetArmorAttribute();.

// MyDamageMagnitudeCalc.h
#pragma once

#include "GameplayModMagnitudeCalculation.h"
#include "MyDamageMagnitudeCalc.generated.h"

UCLASS()
class MYGAME_API UMyDamageMagnitudeCalc : public UGameplayModMagnitudeCalculation
{
    GENERATED_BODY()

public:
    UMyDamageMagnitudeCalc();

    virtual float CalculateBaseMagnitude_Implementation(const FGameplayEffectSpec& Spec) const override;

private:
    FGameplayEffectAttributeCaptureDefinition AttackPowerDef;
};
// MyDamageMagnitudeCalc.cpp
#include "MyDamageMagnitudeCalc.h"
#include "MyCombatSet.h"

UMyDamageMagnitudeCalc::UMyDamageMagnitudeCalc()
{
    // Capture AttackPower from the source, evaluated at execution time rather than snapshotted.
    AttackPowerDef = FGameplayEffectAttributeCaptureDefinition(
        UMyCombatSet::GetAttackPowerAttribute(),
        EGameplayEffectAttributeCaptureSource::Source,
        false);

    RelevantAttributesToCapture.Add(AttackPowerDef);
}

float UMyDamageMagnitudeCalc::CalculateBaseMagnitude_Implementation(const FGameplayEffectSpec& Spec) const
{
    FAggregatorEvaluateParameters EvalParams;
    EvalParams.SourceTags = Spec.CapturedSourceTags.GetAggregatedTags();
    EvalParams.TargetTags = Spec.CapturedTargetTags.GetAggregatedTags();

    float AttackPower = 0.f;
    GetCapturedAttributeMagnitude(AttackPowerDef, Spec, EvalParams, AttackPower);

    return AttackPower * 1.5f;
}

RelevantAttributesToCapture comes from UGameplayEffectCalculation and must be filled in the constructor: captures are resolved when the spec is created, not when the calculation runs.

SetByCaller

The GE asset declares a DataTag; the caller supplies the number:

const FGameplayEffectSpecHandle SpecHandle =
    MakeOutgoingGameplayEffectSpec(DamageEffectClass, GetAbilityLevel());
SpecHandle.Data->SetSetByCallerMagnitude(
    FGameplayTag::RequestGameplayTag(TEXT("SetByCaller.Damage")), 75.f);

There is also an FName overload, SetSetByCallerMagnitude(FName DataName, float Magnitude).


Execution calculations (UGameplayEffectExecutionCalculation)

Executions run whenever the effect executes (instant effects and each period of a periodic effect) and can write several attributes from one formula.

// MyDamageExecution.h
#pragma once

#include "GameplayEffectExecutionCalculation.h"
#include "MyDamageExecution.generated.h"

UCLASS()
class MYGAME_API UMyDamageExecution : public UGameplayEffectExecutionCalculation
{
    GENERATED_BODY()

public:
    UMyDamageExecution();

    virtual void Execute_Implementation(
        const FGameplayEffectCustomExecutionParameters& ExecutionParams,
        FGameplayEffectCustomExecutionOutput& OutExecutionOutput) const override;

private:
    FGameplayEffectAttributeCaptureDefinition AttackPowerDef;
    FGameplayEffectAttributeCaptureDefinition ArmorDef;
};
// MyDamageExecution.cpp
#include "MyDamageExecution.h"
#include "MyCombatSet.h"
#include "MyHealthSet.h"

UMyDamageExecution::UMyDamageExecution()
{
    AttackPowerDef = FGameplayEffectAttributeCaptureDefinition(
        UMyCombatSet::GetAttackPowerAttribute(),
        EGameplayEffectAttributeCaptureSource::Source,
        true);

    ArmorDef = FGameplayEffectAttributeCaptureDefinition(
        UMyCombatSet::GetArmorAttribute(),
        EGameplayEffectAttributeCaptureSource::Target,
        false);

    RelevantAttributesToCapture.Add(AttackPowerDef);
    RelevantAttributesToCapture.Add(ArmorDef);
}

void UMyDamageExecution::Execute_Implementation(
    const FGameplayEffectCustomExecutionParameters& ExecutionParams,
    FGameplayEffectCustomExecutionOutput& OutExecutionOutput) const
{
    const FGameplayEffectSpec& Spec = ExecutionParams.GetOwningSpec();

    FAggregatorEvaluateParameters EvalParams;
    EvalParams.SourceTags = Spec.CapturedSourceTags.GetAggregatedTags();
    EvalParams.TargetTags = Spec.CapturedTargetTags.GetAggregatedTags();

    float AttackPower = 0.f;
    ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(AttackPowerDef, EvalParams, AttackPower);

    float Armor = 0.f;
    ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(ArmorDef, EvalParams, Armor);

    const float Damage = FMath::Max(1.f, (AttackPower * 2.f) - Armor);

    OutExecutionOutput.AddOutputModifier(FGameplayModifierEvaluatedData(
        UMyHealthSet::GetHealthAttribute(),
        EGameplayModOp::AddBase,
        -Damage));
}

FGameplayModifierEvaluatedData(Attribute, ModOp, Magnitude) takes an optional fourth FActiveGameplayEffectHandle. Add the execution class to the GE asset's Executions array; it replaces per-modifier setup for anything that needs more than one input.


Stacking

EGameplayEffectStackingType Behaviour
None Every application is an independent active effect
AggregateBySource One stack group per applying ASC
AggregateByTarget One stack group on the target, shared by all sources

Read it with UGameplayEffect::GetStackingType(); the public StackingType member is going private. The stack cap is GetStackLimitCount() (0 means unlimited).

EGameplayEffectStackingDurationPolicy Behaviour on a new stack
RefreshOnSuccessfulApplication Duration restarts
NeverRefresh Duration stays as first applied
ExtendDuration The new spec's duration is added to the remaining time
EGameplayEffectStackingExpirationPolicy Behaviour when the duration runs out
ClearEntireStack All stacks removed
RemoveSingleStackAndRefreshDuration One stack removed, duration restarts
RefreshDuration Duration restarts; removal must be explicit
EGameplayEffectStackingPeriodPolicy Behaviour of the period timer on a new stack
ResetOnSuccessfulApplication Timer restarts at the full period
NeverReset Timer keeps running
const int32 StackCount = ASC->GetCurrentStackCount(ActiveHandle);

const FGameplayEffectQuery Query = FGameplayEffectQuery::MakeQuery_MatchAllOwningTags(
    FGameplayTagContainer(PoisonTag));
const int32 TotalStacks = ASC->GetAggregatedStackCount(Query);

ASC->UpdateActiveGameplayEffectSetByCallerMagnitude(
    ActiveHandle,
    FGameplayTag::RequestGameplayTag(TEXT("SetByCaller.StackDamage")),
    NewDamageValue);

Periodic effects

Set on the GE asset: DurationPolicy = HasDuration, DurationMagnitude for the total time, Period for the interval and bExecutePeriodicEffectOnApplication for whether the first tick fires immediately. Each period runs the effect as if it were instant, so it hits the base value and calls PostGameplayEffectExecute.

// Declaration in the listening class:
void HandlePeriodicEffectExecuted(UAbilitySystemComponent* Source,
    const FGameplayEffectSpec& Spec, FActiveGameplayEffectHandle Handle);

// Binding (server side):
ASC->OnPeriodicGameplayEffectExecuteDelegateOnSelf.AddUObject(
    this, &AMyCharacter::HandlePeriodicEffectExecuted);

GameplayEffect components

UGameplayEffect behaviour lives in UGameplayEffectComponent subclasses under GameplayEffectComponents/, added to the asset's GEComponents array:

Component Owns
UAbilitiesGameplayEffectComponent Abilities granted while the effect is active
UAdditionalEffectsGameplayEffectComponent Chained effects: OnApplicationGameplayEffects (conditional) and the OnComplete* arrays
UAssetTagsGameplayEffectComponent The tags identifying the effect asset itself
UBlockAbilityTagsGameplayEffectComponent Ability tags blocked while the effect is active
UCancelAbilityTagsGameplayEffectComponent Abilities cancelled when the effect is applied
UChanceToApplyGameplayEffectComponent Probability gate on application
UCustomCanApplyGameplayEffectComponent Custom UGameplayEffectCustomApplicationRequirement classes
UImmunityGameplayEffectComponent Queries that block incoming effects
URemoveOtherGameplayEffectComponent Other effects removed on application
UTargetTagRequirementsGameplayEffectComponent Tags required on the target to apply, keep or remove
UTargetTagsGameplayEffectComponent Tags granted to the target while active

When reading older assets, the legacy monolithic properties map onto these: granted tags to UTargetTagsGameplayEffectComponent, application tag requirements to UTargetTagRequirementsGameplayEffectComponent, immunity tags to UImmunityGameplayEffectComponent.


Conditional effects (FConditionalGameplayEffect)

FConditionalGameplayEffect has EffectClass and RequiredSourceTags; it is applied to the same target as the parent effect when the source tags match. Configure it through UAdditionalEffectsGameplayEffectComponent on the GE asset.


Immunity

Add UImmunityGameplayEffectComponent to the GE and configure its tag queries. While that effect is active on an ASC, incoming effects matching the query are rejected and the ASC broadcasts:

// Declaration:
void HandleImmunityBlock(const FGameplayEffectSpec& BlockedSpec,
    const FActiveGameplayEffect* ImmunityEffect);

// Binding:
ASC->OnImmunityBlockGameplayEffectDelegate.AddUObject(this, &AMyCharacter::HandleImmunityBlock);

Querying and removing active effects

FGameplayEffectQuery Query;
Query.EffectTagQuery = FGameplayTagQuery::MakeQuery_MatchAnyTags(
    FGameplayTagContainer(FGameplayTag::RequestGameplayTag(TEXT("Effect.Type.Buff"))));

const TArray<FActiveGameplayEffectHandle> Handles = ASC->GetActiveEffects(Query);
const TArray<float> Remaining = ASC->GetActiveEffectsTimeRemaining(Query);

ASC->RemoveActiveEffects(Query);
ASC->RemoveActiveEffectsWithGrantedTags(FGameplayTagContainer(SpeedBuffTag));
ASC->RemoveActiveEffectsWithSourceTags(FGameplayTagContainer(PoisonTag));

FGameplayEffectQuery::MakeQuery_MatchAnyOwningTags and MakeQuery_MatchAllOwningTags are the convenience constructors for tag-only queries.


Effect lifecycle delegates

Delegate on the ASC Fires
OnGameplayEffectAppliedDelegateToSelf Any effect applied to this ASC
OnGameplayEffectAppliedDelegateToTarget Any effect this ASC applied to someone else
OnActiveGameplayEffectAddedDelegateToSelf A duration or infinite effect entered the active container
OnPeriodicGameplayEffectExecuteDelegateOnSelf A periodic effect ticked on this ASC
OnImmunityBlockGameplayEffectDelegate An incoming effect was blocked by immunity

The first four are FOnGameplayEffectAppliedDelegate, a three-parameter multicast delegate: (UAbilitySystemComponent* Source, const FGameplayEffectSpec& Spec, FActiveGameplayEffectHandle Handle).

Removal is per-handle rather than global:

// Declaration:
void HandleEffectRemoved(const FGameplayEffectRemovalInfo& RemovalInfo);

// Binding, after the effect was applied:
if (FOnActiveGameplayEffectRemoved_Info* RemovalDelegate =
        ASC->OnGameplayEffectRemoved_InfoDelegate(ActiveHandle))
{
    RemovalDelegate->AddUObject(this, &AMyCharacter::HandleEffectRemoved);
}

FGameplayEffectRemovalInfo carries bPrematureRemoval, bPredictionRejected, StackCount, EffectContext, OwningASC and a const FActiveGameplayEffect* ActiveEffect.

Source: SKILL.md on GitHub

No alerts2d5 checks · Risk SAFE
  • Gen Agent Trust Hub2d

    The skill provides C++ templates and guidance for implementing the Gameplay Ability System in Unreal Engine. It includes a minor risk of indirect prompt injection due to reliance on external project context files.

  • Socket2d

    No alerts

  • Snyk2d

    Risk: LOW · No issues

  • Runlayer6mo

    4 files scanned · No issues

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at f3742d7. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 2 days ago.

Activeupdated 3 days ago
metadata
{
  "version": "2.0.0",
  "engine": "5.8"
}

README badge

README badge for quodsoler/unreal-engine-skills/ue-gameplay-abilities