mirror of
https://github.com/Donchitos/Claude-Code-Game-Studios.git
synced 2026-10-16 01:37:05 +00:00
Fix release for 1.1.1: the minimal path end to end, engine commands that run on Godot, Unity and Unreal, hook warnings you can see, and verdicts that say when they could not check. Details in CHANGELOG.md; upgrade steps in UPGRADING.md (v1.1.1 → v1.1.2). Fixes #67 Fixes #131 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WuJn2FdynEybfL5KC697nJ
411 lines
12 KiB
Markdown
411 lines
12 KiB
Markdown
# Unreal Engine 5.7 — Gameplay Ability System (GAS)
|
|
|
|
**Last verified:** 2026-02-13
|
|
**Status:** Production-Ready
|
|
**Plugin:** `GameplayAbilities` (built-in, enable in Plugins)
|
|
|
|
---
|
|
|
|
## Overview
|
|
|
|
**Gameplay Ability System (GAS)** is a modular framework for building abilities, attributes,
|
|
effects, and gameplay mechanics. It's the standard for RPGs, MOBAs, shooters with abilities,
|
|
and any game with complex ability systems.
|
|
|
|
**Use GAS for:**
|
|
- Character abilities (spells, skills, attacks)
|
|
- Attributes (health, mana, stamina, stats)
|
|
- Buffs/debuffs (temporary effects)
|
|
- Cooldowns and costs
|
|
- Damage calculation
|
|
- Multiplayer-ready ability replication
|
|
|
|
---
|
|
|
|
## Core Concepts
|
|
|
|
### 1. **Ability System Component** (ASC)
|
|
- The main component that owns abilities, attributes, and effects
|
|
- Added to Characters or PlayerStates
|
|
|
|
### 2. **Gameplay Abilities**
|
|
- Individual skills/actions (fireball, heal, dash, etc.)
|
|
- Activated, committed (cost/cooldown), and can be cancelled
|
|
|
|
### 3. **Attributes & Attribute Sets**
|
|
- Stats that can be modified (Health, Mana, Stamina, Strength, etc.)
|
|
- Stored in Attribute Sets
|
|
|
|
### 4. **Gameplay Effects**
|
|
- Modify attributes (damage, healing, buffs, debuffs)
|
|
- Can be instant, duration-based, or infinite
|
|
|
|
### 5. **Gameplay Tags**
|
|
- Hierarchical tags for ability logic (e.g., `Ability.Attack.Melee`, `Status.Stunned`)
|
|
|
|
---
|
|
|
|
## Setup
|
|
|
|
### 1. Enable Plugin
|
|
|
|
`Edit > Plugins > Gameplay Abilities > Enabled > Restart`
|
|
|
|
### 2. Add Ability System Component
|
|
|
|
```cpp
|
|
#include "AbilitySystemComponent.h"
|
|
#include "AttributeSet.h"
|
|
|
|
UCLASS()
|
|
class AMyCharacter : public ACharacter {
|
|
GENERATED_BODY()
|
|
|
|
public:
|
|
AMyCharacter() {
|
|
// Create ASC
|
|
AbilitySystemComponent = CreateDefaultSubobject<UAbilitySystemComponent>(TEXT("AbilitySystem"));
|
|
AbilitySystemComponent->SetIsReplicated(true);
|
|
AbilitySystemComponent->SetReplicationMode(EGameplayEffectReplicationMode::Mixed);
|
|
|
|
// Create Attribute Set
|
|
AttributeSet = CreateDefaultSubobject<UMyAttributeSet>(TEXT("AttributeSet"));
|
|
}
|
|
|
|
protected:
|
|
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Abilities")
|
|
TObjectPtr<UAbilitySystemComponent> AbilitySystemComponent;
|
|
|
|
UPROPERTY()
|
|
TObjectPtr<const UAttributeSet> AttributeSet;
|
|
};
|
|
```
|
|
|
|
### 3. Initialize ASC (Important for Multiplayer)
|
|
|
|
```cpp
|
|
void AMyCharacter::PossessedBy(AController* NewController) {
|
|
Super::PossessedBy(NewController);
|
|
|
|
// Server: Initialize ASC
|
|
if (AbilitySystemComponent) {
|
|
AbilitySystemComponent->InitAbilityActorInfo(this, this);
|
|
GiveDefaultAbilities();
|
|
}
|
|
}
|
|
|
|
void AMyCharacter::OnRep_PlayerState() {
|
|
Super::OnRep_PlayerState();
|
|
|
|
// Client: Initialize ASC
|
|
if (AbilitySystemComponent) {
|
|
AbilitySystemComponent->InitAbilityActorInfo(this, this);
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Attributes & Attribute Sets
|
|
|
|
### Create Attribute Set
|
|
|
|
```cpp
|
|
#include "AttributeSet.h"
|
|
#include "AbilitySystemComponent.h"
|
|
|
|
UCLASS()
|
|
class UMyAttributeSet : public UAttributeSet {
|
|
GENERATED_BODY()
|
|
|
|
public:
|
|
UMyAttributeSet();
|
|
|
|
// Health
|
|
UPROPERTY(BlueprintReadOnly, Category = "Attributes", ReplicatedUsing = OnRep_Health)
|
|
FGameplayAttributeData Health;
|
|
ATTRIBUTE_ACCESSORS(UMyAttributeSet, Health)
|
|
|
|
UPROPERTY(BlueprintReadOnly, Category = "Attributes", ReplicatedUsing = OnRep_MaxHealth)
|
|
FGameplayAttributeData MaxHealth;
|
|
ATTRIBUTE_ACCESSORS(UMyAttributeSet, MaxHealth)
|
|
|
|
// Mana
|
|
UPROPERTY(BlueprintReadOnly, Category = "Attributes", ReplicatedUsing = OnRep_Mana)
|
|
FGameplayAttributeData Mana;
|
|
ATTRIBUTE_ACCESSORS(UMyAttributeSet, Mana)
|
|
|
|
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
|
|
|
|
protected:
|
|
UFUNCTION()
|
|
virtual void OnRep_Health(const FGameplayAttributeData& OldHealth);
|
|
|
|
UFUNCTION()
|
|
virtual void OnRep_MaxHealth(const FGameplayAttributeData& OldMaxHealth);
|
|
|
|
UFUNCTION()
|
|
virtual void OnRep_Mana(const FGameplayAttributeData& OldMana);
|
|
};
|
|
```
|
|
|
|
### Implement Attribute Set
|
|
|
|
```cpp
|
|
#include "Net/UnrealNetwork.h"
|
|
|
|
UMyAttributeSet::UMyAttributeSet() {
|
|
// Default values
|
|
Health = 100.0f;
|
|
MaxHealth = 100.0f;
|
|
Mana = 50.0f;
|
|
}
|
|
|
|
void UMyAttributeSet::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const {
|
|
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
|
|
|
DOREPLIFETIME_CONDITION_NOTIFY(UMyAttributeSet, Health, COND_None, REPNOTIFY_Always);
|
|
DOREPLIFETIME_CONDITION_NOTIFY(UMyAttributeSet, MaxHealth, COND_None, REPNOTIFY_Always);
|
|
DOREPLIFETIME_CONDITION_NOTIFY(UMyAttributeSet, Mana, COND_None, REPNOTIFY_Always);
|
|
}
|
|
|
|
void UMyAttributeSet::OnRep_Health(const FGameplayAttributeData& OldHealth) {
|
|
GAMEPLAYATTRIBUTE_REPNOTIFY(UMyAttributeSet, Health, OldHealth);
|
|
}
|
|
|
|
// Implement other OnRep functions similarly...
|
|
```
|
|
|
|
### Clamping Attributes
|
|
|
|
Attribute Sets have no built-in clamping: the Data Table `Min Value` and
|
|
`Max Value` columns have no effect. Clamp in the Attribute Set:
|
|
- `PreAttributeChange()` is called just before any modification to an
|
|
attribute, and is the hook for rules like
|
|
`Health = Clamp(Health, 0, MaxHealth)` — its `NewValue` is mutable.
|
|
- `PreAttributeBaseChange()` is called just before a change to an attribute's
|
|
**base** value when an aggregator exists, and `PostGameplayEffectExecute()`
|
|
just after a Gameplay Effect is executed to modify the base value — clamp
|
|
base-value changes from instant effects there.
|
|
|
|
Stacking rules govern re-applications of the same Gameplay Effect; they do not
|
|
bound one attribute pushed by different effects.
|
|
|
|
---
|
|
|
|
## Gameplay Abilities
|
|
|
|
### Create Gameplay Ability
|
|
|
|
```cpp
|
|
#include "Abilities/GameplayAbility.h"
|
|
|
|
UCLASS()
|
|
class UGA_Fireball : public UGameplayAbility {
|
|
GENERATED_BODY()
|
|
|
|
public:
|
|
UGA_Fireball() {
|
|
// Ability config
|
|
InstancingPolicy = EGameplayAbilityInstancingPolicy::InstancedPerActor;
|
|
NetExecutionPolicy = EGameplayAbilityNetExecutionPolicy::ServerInitiated;
|
|
|
|
// Tags
|
|
// ⚠️ UNVERIFIED FOR 5.7 — `AbilityTags` may be deprecated in favour of
|
|
// SetAssetTags()/GetAssetTags(). Reported by the `unreal-specialist`
|
|
// agent (2026-08-12), which believed the change landed around 5.5 and
|
|
// correctly REFUSED to emit code on a symbol it could not verify.
|
|
// NOT SOURCEABLE offline: this repo has no 5.7 API dump to check against,
|
|
// and the same knowledge-gap warning that governs the agent governs this
|
|
// file. Verify against the UE 5.7 docs before using this snippet, and
|
|
// correct both occurrences (here and ~line 328) together.
|
|
AbilityTags.AddTag(FGameplayTag::RequestGameplayTag(FName("Ability.Attack.Fireball")));
|
|
}
|
|
|
|
virtual void ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo,
|
|
const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData) override {
|
|
|
|
if (!CommitAbility(Handle, ActorInfo, ActivationInfo)) {
|
|
// Failed to commit (not enough mana, on cooldown, etc.)
|
|
EndAbility(Handle, ActorInfo, ActivationInfo, true, true);
|
|
return;
|
|
}
|
|
|
|
// Spawn fireball projectile
|
|
SpawnFireball();
|
|
|
|
// End ability
|
|
EndAbility(Handle, ActorInfo, ActivationInfo, true, false);
|
|
}
|
|
|
|
void SpawnFireball() {
|
|
// Spawn fireball logic
|
|
}
|
|
};
|
|
```
|
|
|
|
### Grant Abilities to Character
|
|
|
|
```cpp
|
|
void AMyCharacter::GiveDefaultAbilities() {
|
|
if (!HasAuthority() || !AbilitySystemComponent) return;
|
|
|
|
// Grant abilities
|
|
AbilitySystemComponent->GiveAbility(FGameplayAbilitySpec(UGA_Fireball::StaticClass(), 1, INDEX_NONE, this));
|
|
AbilitySystemComponent->GiveAbility(FGameplayAbilitySpec(UGA_Heal::StaticClass(), 1, INDEX_NONE, this));
|
|
}
|
|
```
|
|
|
|
### Activate Ability
|
|
|
|
```cpp
|
|
// Activate by class
|
|
AbilitySystemComponent->TryActivateAbilityByClass(UGA_Fireball::StaticClass());
|
|
|
|
// Activate by tag
|
|
FGameplayTagContainer TagContainer;
|
|
TagContainer.AddTag(FGameplayTag::RequestGameplayTag(FName("Ability.Attack.Fireball")));
|
|
AbilitySystemComponent->TryActivateAbilitiesByTag(TagContainer);
|
|
```
|
|
|
|
---
|
|
|
|
## Gameplay Effects
|
|
|
|
### Create Gameplay Effect (Damage)
|
|
|
|
```cpp
|
|
// Create Blueprint: Content Browser > Gameplay > Gameplay Effect
|
|
|
|
// OR in C++:
|
|
UCLASS()
|
|
class UGE_Damage : public UGameplayEffect {
|
|
GENERATED_BODY()
|
|
|
|
public:
|
|
UGE_Damage() {
|
|
// Instant damage
|
|
DurationPolicy = EGameplayEffectDurationType::Instant;
|
|
|
|
// Modifier: Reduce Health
|
|
FGameplayModifierInfo ModifierInfo;
|
|
ModifierInfo.Attribute = UMyAttributeSet::GetHealthAttribute();
|
|
ModifierInfo.ModifierOp = EGameplayModOp::Additive;
|
|
ModifierInfo.ModifierMagnitude = FScalableFloat(-25.0f); // -25 health
|
|
|
|
Modifiers.Add(ModifierInfo);
|
|
}
|
|
};
|
|
```
|
|
|
|
### Apply Gameplay Effect
|
|
|
|
```cpp
|
|
// Apply damage to target
|
|
if (UAbilitySystemComponent* TargetASC = UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent(Target)) {
|
|
FGameplayEffectContextHandle EffectContext = AbilitySystemComponent->MakeEffectContext();
|
|
EffectContext.AddSourceObject(this);
|
|
|
|
FGameplayEffectSpecHandle SpecHandle = AbilitySystemComponent->MakeOutgoingSpec(
|
|
UGE_Damage::StaticClass(), 1, EffectContext);
|
|
|
|
if (SpecHandle.IsValid()) {
|
|
AbilitySystemComponent->ApplyGameplayEffectSpecToTarget(*SpecHandle.Data.Get(), TargetASC);
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Gameplay Tags
|
|
|
|
### Define Tags
|
|
|
|
`Project Settings > Project > Gameplay Tags > Gameplay Tag List`
|
|
|
|
Example hierarchy:
|
|
```
|
|
Ability
|
|
├─ Ability.Attack
|
|
│ ├─ Ability.Attack.Melee
|
|
│ └─ Ability.Attack.Ranged
|
|
├─ Ability.Defend
|
|
└─ Ability.Utility
|
|
|
|
Status
|
|
├─ Status.Stunned
|
|
├─ Status.Invulnerable
|
|
└─ Status.Silenced
|
|
```
|
|
|
|
### Use Tags in Abilities
|
|
|
|
```cpp
|
|
UCLASS()
|
|
class UGA_MeleeAttack : public UGameplayAbility {
|
|
GENERATED_BODY()
|
|
|
|
public:
|
|
UGA_MeleeAttack() {
|
|
// This ability has these tags
|
|
AbilityTags.AddTag(FGameplayTag::RequestGameplayTag(FName("Ability.Attack.Melee")));
|
|
|
|
// Block these tags while active
|
|
BlockAbilitiesWithTag.AddTag(FGameplayTag::RequestGameplayTag(FName("Ability.Attack")));
|
|
|
|
// Cancel these abilities when activated
|
|
CancelAbilitiesWithTag.AddTag(FGameplayTag::RequestGameplayTag(FName("Ability.Defend")));
|
|
|
|
// Can't activate if target has these tags
|
|
ActivationBlockedTags.AddTag(FGameplayTag::RequestGameplayTag(FName("Status.Stunned")));
|
|
}
|
|
};
|
|
```
|
|
|
|
---
|
|
|
|
## Cooldowns & Costs
|
|
|
|
### Add Cooldown
|
|
|
|
```cpp
|
|
// In Ability Blueprint or C++:
|
|
// Create Gameplay Effect with Duration = Cooldown time
|
|
// Assign to Ability > Cooldown Gameplay Effect Class
|
|
```
|
|
|
|
### Add Cost (Mana)
|
|
|
|
```cpp
|
|
// Create Gameplay Effect that reduces Mana
|
|
// Assign to Ability > Cost Gameplay Effect Class
|
|
```
|
|
|
|
---
|
|
|
|
## Common Patterns
|
|
|
|
### Get Current Attribute Value
|
|
|
|
```cpp
|
|
float CurrentHealth = AbilitySystemComponent->GetNumericAttribute(UMyAttributeSet::GetHealthAttribute());
|
|
```
|
|
|
|
### Listen for Attribute Changes
|
|
|
|
```cpp
|
|
AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(UMyAttributeSet::GetHealthAttribute())
|
|
.AddUObject(this, &AMyCharacter::OnHealthChanged);
|
|
|
|
void AMyCharacter::OnHealthChanged(const FOnAttributeChangeData& Data) {
|
|
UE_LOG(LogTemp, Warning, TEXT("Health: %f"), Data.NewValue);
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Sources
|
|
- https://docs.unrealengine.com/5.7/en-US/gameplay-ability-system-for-unreal-engine/
|
|
- Clamping: *Gameplay Attributes and Attribute Sets* https://dev.epicgames.com/documentation/en-us/unreal-engine/gameplay-attributes-and-attribute-sets-for-the-gameplay-ability-system-in-unreal-engine ; `UAttributeSet` API https://dev.epicgames.com/documentation/en-us/unreal-engine/API/Plugins/GameplayAbilities/UAttributeSet ; `PreAttributeChange` https://dev.epicgames.com/documentation/en-us/unreal-engine/API/Plugins/GameplayAbilities/UAttributeSet/PreAttributeChange
|
|
- https://github.com/tranek/GASDocumentation (community guide)
|