UE Gameplay Ability System
Target engine: UE 5.8. APIs below are verified against the 5.8 headers; older forms are listed under "Deprecated — do not use".
GAS ships as the GameplayAbilities plugin (Engine/Plugins/Runtime/GameplayAbilities), which is production-ready, not Experimental. Enable it in the .uproject, then add "GameplayAbilities", "GameplayTags" and "GameplayTasks" to PublicDependencyModuleNames in your module's Build.cs. This skill covers UAbilitySystemComponent, UGameplayAbility, UGameplayEffect, UAttributeSet, UAbilityTask and Gameplay Cues.
Context
Read .agents/ue-project-context.md if it exists (module names, conventions, enabled plugins, GAS/networking setup). Do not stop if it is missing.
Identify the area from the request and the codebase. Ask only when two plausible readings would produce different code.
GAS Architecture
Gameplay tags thread through all of them as requirements, grants and blockers.
ASC Ownership and Initialization
Every actor that owns or exposes an ASC implements IAbilitySystemInterface:
InitAbilityActorInfo(OwnerActor, AvatarActor) must run on both server and client. For a pawn-owned ASC that is BeginPlay with (this, this). For a PlayerState-owned ASC it is PossessedBy on the server and OnRep_PlayerState on the client, with (PlayerState, Character). Full dual-path code, respawn handling and the AI variant are in gas-setup-patterns.md [blocked].
Mixed sends full GE data to the connection that owns the ASC's owner actor (GameplayEffect.cpp:5240), so it works for a PlayerState-owned ASC and for a pawn-owned ASC once a PlayerController possesses the pawn. Choosing Full over Mixed is a bandwidth decision, not a correctness one. Minimal does not work for player-owned ASCs (AbilitySystemComponent.h:83). See ue-networking-replication for what each mode costs on the wire.
Gameplay Abilities
Constructor sets the policies and the ability's own tags:
SetAssetTags is constructor-only; read them back with GetAssetTags().
Split the commit when the two halves must happen at different times: CommitAbilityCost(Handle, ActorInfo, ActivationInfo) and CommitAbilityCooldown(Handle, ActorInfo, ActivationInfo, ForceCooldown) — note the cooldown overload takes the extra const bool ForceCooldown.
Inside an instanced ability, GetAbilitySystemComponentFromActorInfo_Ensured() returns the ASC and logs an ensure instead of crashing when actor info is not set up.
Granting and activating, from the server:
Ability Tasks
Ability tasks make an ability span frames. The pattern is always: static factory → bind delegates → ReadyForActivation() (inherited from UGameplayTask). Task delegates are dynamic, so each handler needs UFUNCTION():
Full factory signatures, delegate names, the custom-task template and the task pitfalls are in ability-task-reference.md [blocked].
Gameplay Effects
Modifier ops live in EGameplayModOp (a namespaced enum, so values are TEnumAsByte<EGameplayModOp::Type>). The aggregation equation is
((BaseValue + AddBase) * MultiplyAdditive / DivideAdditive * MultiplyCompound) + AddFinal.
Behaviour that used to be monolithic properties on UGameplayEffect now lives in UGameplayEffectComponent subclasses under GameplayEffectComponents/. Stacking, magnitude calculations, UGameplayEffectExecutionCalculation, periodic effects, immunity, queries, lifecycle delegates and the full component list are in gameplay-effect-reference.md [blocked].
Attribute Sets
ATTRIBUTE_ACCESSORS_BASIC generates GetHealthAttribute(), GetHealth(), SetHealth() and InitHealth(). Projects that want extra accessors define their own macro chaining GAMEPLAYATTRIBUTE_PROPERTY_GETTER, GAMEPLAYATTRIBUTE_VALUE_GETTER, GAMEPLAYATTRIBUTE_VALUE_SETTER and GAMEPLAYATTRIBUTE_VALUE_INITTER.
Each OnRep_ body is one GAMEPLAYATTRIBUTE_REPNOTIFY(UMyHealthSet, Health, OldHealth) call, and every attribute needs a DOREPLIFETIME_CONDITION_NOTIFY(UMyHealthSet, Health, COND_None, REPNOTIFY_Always) line. The full .cpp with clamping is in gas-setup-patterns.md [blocked].
Registering and reading:
One ASC can host several UAttributeSet subclasses, but only one instance per class.
Tags in GAS
Tag definition (native tag macros, .ini tag sources), container matching and FGameplayTagQuery semantics belong to ue-gameplay-tags-messaging. What is GAS-specific:
The callback is declared as void HandleStunnedTagChanged(const FGameplayTag Tag, int32 NewCount); and needs no UFUNCTION() — tag events are plain multicast delegates.
Loose tags are set directly instead of through a GE. The third argument chooses how the tag replicates:
EGameplayTagReplicationState values: None, SimulatedTagOnly, TagOnly, CountToOwner, TagAndCountToAll. Anything other than None must be added on the server.
Gameplay Cues
Cues are cosmetic only. Tag prefix GameplayCue.; a GE triggers them through its GameplayCues array, or code fires them directly:
UGameplayCueManager discovers cue assets under the scan paths configured in project settings.
Project Settings
GAS configuration is a UDeveloperSettings class: UGameplayAbilitiesDeveloperSettings (Project Settings → Gameplay Abilities). Read it with GetDefault<UGameplayAbilitiesDeveloperSettings>(). Notable fields: GameplayCueNotifyPaths, ReplicateActivationOwnedTags, bAllowGameplayModEvaluationChannels, plus the global curve/attribute tables. Runtime helpers stay on UAbilitySystemGlobals (GetGameplayCueNotifyPaths(), AddGameplayCueNotifyPath(), GetAttributeSetInitter()).
Deprecated — do not use
Common Mistakes
Implementing IAbilitySystemInterface only on the pawn: when the ASC lives on the PlayerState, the PlayerState must implement the interface too, otherwise UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent fails on the owner.
Calling InitAbilityActorInfo only on the server: clients need it as well. Server path is PossessedBy, client path is OnRep_PlayerState. Without the client call, attribute change delegates never fire on the owning client.
Applying GEs before init: granting abilities or applying effects before InitAbilityActorInfo runs leaves the ASC without actor info and the attribute sets unregistered.
Clamping in the wrong hook: PreAttributeChange fires on every aggregator recompute, so it may only clamp NewValue. Death, hit reactions and gameplay events belong in PostGameplayEffectExecute, which only runs for executions (instant GEs and periodic ticks).
Forgetting CommitAbility: the ability runs, but no cost is paid and no cooldown starts.
Assuming loose tags replicate: AddLooseGameplayTag(Tag) defaults to EGameplayTagReplicationState::None. Pass TagOnly or CountToOwner from the server, or grant the tag through a GE.
Stacks silently capped: applications beyond GetStackLimitCount() are rejected without an error. Check GetCurrentStackCount(Handle) before assuming a stack landed.
Startup effects in BeginPlay without an authority check: late joiners re-apply them. Gate every GiveAbility and startup GE behind HasAuthority().
AI with no PlayerState: put the ASC on the AI character, call InitAbilityActorInfo(this, this) and set Minimal replication.
Related Skills
ue-gameplay-tags-messaging— tag definition (native tag macros, ini tag sources), containers,FGameplayTagQueryand gameplay messagingue-networking-replication—DOREPLIFETIMEvariants, RPCs, prediction keys, push modelue-animation-system— montage assets, notifies and slots that the montage ability tasks driveue-character-movement— movement modes that root-motion ability tasks manipulateue-cpp-foundations—UPROPERTY/UFUNCTIONspecifiers, delegates,TSubclassOf, subsystemsue-data-assets-tables—UCurveTableandUDataTableassets behind attribute initialisation andFScalableFloatue-state-trees— running abilities from State Tree tasksue-actor-component-architecture— component creation, subobject registration, replication of subobjectsue-gameplay-framework— PlayerState and Pawn lifecycle, and damage without GAS (TakeDamage,UDamageType)ue-input-system— Enhanced Input actions bound to ability input idsue-game-features— granting abilities and attribute sets from a game feature pluginue-niagara-effects— Niagara systems, user parameters, data interfaces and data channelsue-ui-umg-slate— UMG widgets, Slate, Common UI and MVVM


