ue-gameplay-abilities

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Gameplay Ability System (GAS)

Gameplay Ability System (GAS)

You are an expert in Unreal Engine's Gameplay Ability System (GAS).
你是Unreal Engine的Gameplay Ability System (GAS)专家。

Context Check

上下文检查

Before proceeding, read
.agents/ue-project-context.md
to determine:
  • Whether the GameplayAbilities plugin is enabled
  • Which actors own the AbilitySystemComponent (PlayerState vs Character)
  • The replication mode in use (Minimal, Mixed, Full)
  • Any existing AttributeSets or ability base classes
在开始操作前,请阅读
.agents/ue-project-context.md
以确认:
  • GameplayAbilities插件是否已启用
  • 哪些Actor拥有AbilitySystemComponent(PlayerState 还是 Character)
  • 当前使用的复制模式(Minimal、Mixed、Full)
  • 已存在的AttributeSet或能力基类

Information Gathering

信息收集

Ask the developer:
  1. What type of abilities are needed? (active, passive, triggered, instant)
  2. What attributes are required? (health, mana, stamina, custom stats)
  3. Is this multiplayer? If so, which actors carry the ASC?
  4. Are cooldowns and costs required, or is this a passive/trigger system?
  5. Do abilities need prediction (local-only feedback before server confirms)?

请向开发者询问以下问题:
  1. 需要哪种类型的能力?(主动、被动、触发、即时)
  2. 需要哪些属性?(生命值、法力值、耐力、自定义属性)
  3. 是否为多人游戏?如果是,哪些Actor承载ASC?
  4. 是否需要冷却时间和消耗,或者这是一个被动/触发系统?
  5. 能力是否需要预测(服务器确认前的本地反馈)?

GAS Architecture Overview

GAS架构概述

GAS has three pillars that live on
UAbilitySystemComponent
(ASC):
PillarClassPurpose
Abilities
UGameplayAbility
Logic for what happens when activated
Effects
UGameplayEffect
Data-driven stat mutations (instant, duration, infinite)
Attributes
UAttributeSet
Float properties representing character stats
GameplayTags thread through all three as requirements, grants, and blockers.

GAS有三大核心模块,均依赖
UAbilitySystemComponent
(ASC):
核心模块用途
能力
UGameplayAbility
定义能力激活时的执行逻辑
效果
UGameplayEffect
基于数据的属性变更(即时、持续、永久)
属性
UAttributeSet
代表角色属性的浮点型属性集合
GameplayTags贯穿这三大模块,用于实现需求校验、权限授予和逻辑阻断。

GAS Setup

GAS设置

1. Enable the Plugin

1. 启用插件

Enable
GameplayAbilities
in
.uproject
Plugins array, then in
[ProjectName].Build.cs
:
csharp
PublicDependencyModuleNames.AddRange(new string[]
{
    "GameplayAbilities", "GameplayTags", "GameplayTasks"
});
.uproject
的Plugins数组中启用
GameplayAbilities
,然后在
[ProjectName].Build.cs
中添加:
csharp
PublicDependencyModuleNames.AddRange(new string[]
{
    "GameplayAbilities", "GameplayTags", "GameplayTasks"
});

2. AbilitySystemComponent Ownership

2. AbilitySystemComponent归属

PlayerState (recommended for multiplayer): ASC persists across respawns because PlayerState is not destroyed on death. Use this for player characters in networked games.
Character/Pawn: Simpler. Use for AI characters or single-player games where persistence across respawns is not required.
See
references/gas-setup-patterns.md
for full initialization sequences for both patterns.
PlayerState(多人游戏推荐):ASC会在角色重生后保留,因为PlayerState不会在死亡时被销毁。适用于联网游戏中的玩家角色。
Character/Pawn:设置更简单。适用于AI角色或无需跨重生保留状态的单人游戏。
完整的初始化流程请参考
references/gas-setup-patterns.md
中的两种模式实现代码。

3. IAbilitySystemInterface

3. 实现IAbilitySystemInterface

Every actor that owns or exposes an ASC must implement
IAbilitySystemInterface
:
cpp
#include "AbilitySystemInterface.h"

UCLASS()
class AMyCharacter : public ACharacter, public IAbilitySystemInterface
{
    GENERATED_BODY()
public:
    virtual UAbilitySystemComponent* GetAbilitySystemComponent() const override
        { return AbilitySystemComponent; }
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "GAS")
    TObjectPtr<UAbilitySystemComponent> AbilitySystemComponent;
};
所有拥有或暴露ASC的Actor必须实现
IAbilitySystemInterface
cpp
#include "AbilitySystemInterface.h"

UCLASS()
class AMyCharacter : public ACharacter, public IAbilitySystemInterface
{
    GENERATED_BODY()
public:
    virtual UAbilitySystemComponent* GetAbilitySystemComponent() const override
        { return AbilitySystemComponent; }
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "GAS")
    TObjectPtr<UAbilitySystemComponent> AbilitySystemComponent;
};

4. Replication Modes

4. 复制模式设置

Set on the ASC after creation (server-side only):
cpp
// In BeginPlay or PossessedBy on the server:
AbilitySystemComponent->SetReplicationMode(EGameplayEffectReplicationMode::Mixed);
ModeWhen to Use
Minimal
AI or non-player actors; no GE replication to simulated proxies
Mixed
Player-controlled characters (owner gets full info, others get minimal)
Full
Non-player games or debugging; all GEs replicate to all clients
仅在服务器端创建ASC后设置复制模式:
cpp
// 在服务器端的BeginPlay或PossessedBy函数中调用:
AbilitySystemComponent->SetReplicationMode(EGameplayEffectReplicationMode::Mixed);
模式使用场景
Minimal
AI或非玩家角色;不向模拟代理复制GameplayEffect
Mixed
玩家控制角色(拥有者获取完整信息,其他客户端获取最小信息)
Full
非玩家游戏或调试场景;所有GameplayEffect复制到所有客户端

5. InitAbilityActorInfo

5. 初始化AbilityActorInfo

Must be called on both server and client after possession. Call in
PossessedBy
(server) and
OnRep_PlayerState
(client):
ASC->InitAbilityActorInfo(OwnerActor, AvatarActor)
. See
references/gas-setup-patterns.md
for full dual-path code with respawn handling.

必须在服务器和客户端完成角色控制后调用。在服务器的
PossessedBy
和客户端的
OnRep_PlayerState
中调用:
ASC->InitAbilityActorInfo(OwnerActor, AvatarActor)
。完整的包含重生处理的双路径代码请参考
references/gas-setup-patterns.md

GameplayAbilities

GameplayAbilities

Subclass UGameplayAbility

继承UGameplayAbility

cpp
#include "Abilities/GameplayAbility.h"

UCLASS()
class UMyFireballAbility : public UGameplayAbility
{
    GENERATED_BODY()
public:
    UMyFireballAbility();
    virtual void ActivateAbility(const FGameplayAbilitySpecHandle Handle,
        const FGameplayAbilityActorInfo* ActorInfo,
        const FGameplayAbilityActivationInfo ActivationInfo,
        const FGameplayEventData* TriggerEventData) override;
    virtual void EndAbility(const FGameplayAbilitySpecHandle Handle,
        const FGameplayAbilityActorInfo* ActorInfo,
        const FGameplayAbilityActivationInfo ActivationInfo,
        bool bReplicateEndAbility, bool bWasCancelled) override;
    // Ability Tasks — async building blocks for latent abilities:
    // UAbilityTask_WaitTargetData    — waits for targeting (crosshair/AoE confirm)
    // UAbilityTask_WaitGameplayEvent — waits for a GameplayEvent tag (e.g., anim notify)
    // UAbilityTask_WaitDelay         — simple timer
    // UAbilityTask_PlayMontageAndWait — montage with callbacks (see ue-animation-system)
    // See references/ability-task-reference.md for full list and custom task pattern.
    // CancelAbility — called by CancelAbilitiesWithTag or ASC->CancelAbility(Handle)
    // Internally calls EndAbility with bWasCancelled=true. Override to add cleanup:
    virtual void CancelAbility(const FGameplayAbilitySpecHandle Handle,
        const FGameplayAbilityActorInfo* ActorInfo,
        const FGameplayAbilityActivationInfo ActivationInfo,
        bool bReplicateCancelAbility) override;
    // Custom activation guard — return false to block activation beyond tag checks
    virtual bool CanActivateAbility(const FGameplayAbilitySpecHandle Handle,
        const FGameplayAbilityActorInfo* ActorInfo, /*...*/) const override;
    // Must call Super first. Add custom checks (resource availability, cooldown state).
protected:
    UPROPERTY(EditDefaultsOnly, Category = "GAS")
    TSubclassOf<UGameplayEffect> DamageEffect;
};
cpp
#include "Abilities/GameplayAbility.h"

UCLASS()
class UMyFireballAbility : public UGameplayAbility
{
    GENERATED_BODY()
public:
    UMyFireballAbility();
    virtual void ActivateAbility(const FGameplayAbilitySpecHandle Handle,
        const FGameplayAbilityActorInfo* ActorInfo,
        const FGameplayAbilityActivationInfo ActivationInfo,
        const FGameplayEventData* TriggerEventData) override;
    virtual void EndAbility(const FGameplayAbilitySpecHandle Handle,
        const FGameplayAbilityActorInfo* ActorInfo,
        const FGameplayAbilityActivationInfo ActivationInfo,
        bool bReplicateEndAbility, bool bWasCancelled) override;
    // Ability Tasks — 用于实现延迟能力的异步构建块:
    // UAbilityTask_WaitTargetData    — 等待目标选择(准星/范围确认)
    // UAbilityTask_WaitGameplayEvent — 等待GameplayEvent标签(例如动画通知)
    // UAbilityTask_WaitDelay         — 简单计时器
    // UAbilityTask_PlayMontageAndWait — 带回调的动画蒙太奇(参考ue-animation-system)
    // 完整列表和自定义任务模式请查看references/ability-task-reference.md。
    // CancelAbility — 由CancelAbilitiesWithTag或ASC->CancelAbility(Handle)调用
    // 内部会调用bWasCancelled=true的EndAbility。重写该方法以添加清理逻辑:
    virtual void CancelAbility(const FGameplayAbilitySpecHandle Handle,
        const FGameplayAbilityActorInfo* ActorInfo,
        const FGameplayAbilityActivationInfo ActivationInfo,
        bool bReplicateCancelAbility) override;
    // 自定义激活校验 — 返回false可在标签检查之外阻断能力激活
    virtual bool CanActivateAbility(const FGameplayAbilitySpecHandle Handle,
        const FGameplayAbilityActorInfo* ActorInfo, /*...*/) const override;
    // 必须先调用Super,再添加自定义检查(资源可用性、冷却状态)。
protected:
    UPROPERTY(EditDefaultsOnly, Category = "GAS")
    TSubclassOf<UGameplayEffect> DamageEffect;
};

ActivateAbility Pattern

激活能力的标准模式

cpp
void UMyFireballAbility::ActivateAbility(const FGameplayAbilitySpecHandle Handle,
    const FGameplayAbilityActorInfo* ActorInfo,
    const FGameplayAbilityActivationInfo ActivationInfo,
    const FGameplayEventData* TriggerEventData)
{
    // 1. Commit: validates and applies cost + cooldown
    if (!CommitAbility(Handle, ActorInfo, ActivationInfo))
    {
        EndAbility(Handle, ActorInfo, ActivationInfo, true, true);
        return;
    }
    // 2. Apply effect / spawn projectile / etc.
    FGameplayEffectSpecHandle Spec = MakeOutgoingGameplayEffectSpec(DamageEffect, GetAbilityLevel());
    FGameplayAbilityTargetDataHandle TargetData =
        UAbilitySystemBlueprintLibrary::AbilityTargetDataFromActor(
            ActorInfo->AvatarActor.Get());
    ApplyGameplayEffectSpecToTarget(Handle, ActorInfo, ActivationInfo, Spec, TargetData);
    // 3. End (instant abilities end immediately; latent abilities wait for tasks)
    EndAbility(Handle, ActorInfo, ActivationInfo, true, false);
}
CommitAbility
is shorthand for
CommitAbilityCost
+
CommitAbilityCooldown
. Call them separately when needed -- e.g., commit cost without starting cooldown for a channeled ability, or commit cooldown without cost for a free ability.
cpp
void UMyFireballAbility::ActivateAbility(const FGameplayAbilitySpecHandle Handle,
    const FGameplayAbilityActorInfo* ActorInfo,
    const FGameplayAbilityActivationInfo ActivationInfo,
    const FGameplayEventData* TriggerEventData)
{
    // 1. 提交:验证并应用消耗与冷却
    if (!CommitAbility(Handle, ActorInfo, ActivationInfo))
    {
        EndAbility(Handle, ActorInfo, ActivationInfo, true, true);
        return;
    }
    // 2. 应用效果/生成投射物等
    FGameplayEffectSpecHandle Spec = MakeOutgoingGameplayEffectSpec(DamageEffect, GetAbilityLevel());
    FGameplayAbilityTargetDataHandle TargetData =
        UAbilitySystemBlueprintLibrary::AbilityTargetDataFromActor(
            ActorInfo->AvatarActor.Get());
    ApplyGameplayEffectSpecToTarget(Handle, ActorInfo, ActivationInfo, Spec, TargetData);
    // 3. 结束能力(即时能力立即结束;延迟能力需等待任务完成)
    EndAbility(Handle, ActorInfo, ActivationInfo, true, false);
}
CommitAbility
CommitAbilityCost
+
CommitAbilityCooldown
的简写。在需要时可分开调用——例如,对于通道型能力,先提交消耗但不启动冷却;或者对于免费能力,仅提交冷却而无消耗。

Instancing and Net Execution Policy

实例化与网络执行策略

Set in the ability constructor:
cpp
UMyFireballAbility::UMyFireballAbility()
{
    // InstancedPerActor  - one instance per actor; cheapest for persistent abilities
    // InstancedPerExecution - new instance each activation; safe for concurrency
    // NonInstanced       - CDO runs the ability; no per-execution state
    InstancingPolicy = EGameplayAbilityInstancingPolicy::InstancedPerActor;

    // LocalPredicted  - client runs immediately, server validates (player abilities)
    // ServerOnly      - authority only, no prediction
    // LocalOnly       - local client only (UI, cosmetic)
    // ServerInitiated - server activates, clients run non-authoritative predicted copy
    NetExecutionPolicy = EGameplayAbilityNetExecutionPolicy::LocalPredicted;
}
在能力的构造函数中设置:
cpp
UMyFireballAbility::UMyFireballAbility()
{
    // InstancedPerActor  - 每个Actor一个实例;对持久化能力来说性能最优
    // InstancedPerExecution - 每次激活创建新实例;并发场景下更安全
    // NonInstanced       - 使用CDO运行能力;无每次执行的独立状态
    InstancingPolicy = EGameplayAbilityInstancingPolicy::InstancedPerActor;

    // LocalPredicted  - 客户端立即执行,服务器验证(玩家能力推荐)
    // ServerOnly      - 仅权威端执行,无预测
    // LocalOnly       - 仅本地客户端执行(UI、 cosmetic效果)
    // ServerInitiated - 服务器激活,客户端运行非权威预测副本
    NetExecutionPolicy = EGameplayAbilityNetExecutionPolicy::LocalPredicted;
}

Granting and Activating Abilities

授予与激活能力

cpp
// Grant (server/authority only):
FGameplayAbilitySpecHandle Handle = ASC->GiveAbility(
    FGameplayAbilitySpec(UMyFireballAbility::StaticClass(), 1 /*Level*/));

ASC->TryActivateAbility(Handle);                                       // by handle
ASC->TryActivateAbilityByClass(UMyFireballAbility::StaticClass());    // by class
ASC->TryActivateAbilitiesByTag(                                        // by tag
    FGameplayTagContainer(FGameplayTag::RequestGameplayTag("Ability.Skill.Fireball")));
cpp
// 授予能力(仅服务器/权威端可调用):
FGameplayAbilitySpecHandle Handle = ASC->GiveAbility(
    FGameplayAbilitySpec(UMyFireballAbility::StaticClass(), 1 /*等级*/));

ASC->TryActivateAbility(Handle);                                       // 通过句柄激活
ASC->TryActivateAbilityByClass(UMyFireballAbility::StaticClass());    // 通过类激活
ASC->TryActivateAbilitiesByTag(                                        // 通过标签激活
    FGameplayTagContainer(FGameplayTag::RequestGameplayTag("Ability.Skill.Fireball")));

Ability Tags

能力标签配置

Configure in the ability CDO constructor:
cpp
// Tags this ability grants to its owner while active:
ActivationOwnedTags.AddTag(FGameplayTag::RequestGameplayTag("Ability.Active.Casting"));
// Tags that prevent this ability from activating:
ActivationBlockedTags.AddTag(FGameplayTag::RequestGameplayTag("State.Stunned"));
// Cancel other active abilities with these tags on activation:
CancelAbilitiesWithTag.AddTag(FGameplayTag::RequestGameplayTag("Ability.Active.Melee"));

在能力的CDO构造函数中配置:
cpp
// 能力激活时授予所有者的标签:
ActivationOwnedTags.AddTag(FGameplayTag::RequestGameplayTag("Ability.Active.Casting"));
// 阻止该能力激活的标签:
ActivationBlockedTags.AddTag(FGameplayTag::RequestGameplayTag("State.Stunned"));
// 激活时会取消其他拥有以下标签的活跃能力:
CancelAbilitiesWithTag.AddTag(FGameplayTag::RequestGameplayTag("Ability.Active.Melee"));

GameplayEffects

GameplayEffects

Duration Policies

时长策略

PolicyBehavior
Instant
Executes once; modifies attribute base value permanently
HasDuration
Active for set duration; uses
DurationMagnitude
(seconds)
Infinite
Active until
RemoveActiveGameplayEffect
is called
策略行为
Instant
执行一次;永久修改属性的基础值
HasDuration
在设定时长内生效;使用
DurationMagnitude
(秒)配置时长
Infinite
持续生效,直到调用
RemoveActiveGameplayEffect

Applying Effects

应用效果

cpp
FGameplayEffectContextHandle Ctx = ASC->MakeEffectContext();
FGameplayEffectSpecHandle Spec = ASC->MakeOutgoingSpec(UMyDamageEffect::StaticClass(), Level, Ctx);

// SetByCaller: inject runtime magnitude using a tag key
Spec.Data->SetSetByCallerMagnitude(
    FGameplayTag::RequestGameplayTag("SetByCaller.Damage"), 75.f);

ASC->ApplyGameplayEffectSpecToSelf(*Spec.Data.Get());         // self
ASC->ApplyGameplayEffectSpecToTarget(*Spec.Data.Get(), TargetASC); // target

ASC->RemoveActiveGameplayEffect(ActiveHandle);                // by handle
ASC->RemoveActiveGameplayEffectBySourceEffect(
    UMyDamageEffect::StaticClass(), nullptr);                  // by class
See
references/gameplay-effect-reference.md
for stacking (AggregateBySource/AggregateByTarget), periodic effects (damage over time),
UGameplayEffectExecutionCalculation
(complex modifier logic), conditional effects, and immunity.

cpp
FGameplayEffectContextHandle Ctx = ASC->MakeEffectContext();
FGameplayEffectSpecHandle Spec = ASC->MakeOutgoingSpec(UMyDamageEffect::StaticClass(), Level, Ctx);

// SetByCaller:使用标签键注入运行时数值
Spec.Data->SetSetByCallerMagnitude(
    FGameplayTag::RequestGameplayTag("SetByCaller.Damage"), 75.f);

ASC->ApplyGameplayEffectSpecToSelf(*Spec.Data.Get());         // 应用到自身
ASC->ApplyGameplayEffectSpecToTarget(*Spec.Data.Get(), TargetASC); // 应用到目标

ASC->RemoveActiveGameplayEffect(ActiveHandle);                // 通过句柄移除
ASC->RemoveActiveGameplayEffectBySourceEffect(
    UMyDamageEffect::StaticClass(), nullptr);                  // 通过类移除
叠加规则(按源/按目标聚合)、周期性效果(持续伤害)、
UGameplayEffectExecutionCalculation
(复杂修改逻辑)、条件效果和免疫设置等内容,请查看
references/gameplay-effect-reference.md

AttributeSet

AttributeSet

Define Attributes

定义属性

cpp
// MyHealthSet.h
#include "AttributeSet.h"
#include "AbilitySystemComponent.h"

// Convenience macro - define in your project headers
#define ATTRIBUTE_ACCESSORS(ClassName, PropertyName) \
    GAMEPLAYATTRIBUTE_PROPERTY_GETTER(ClassName, PropertyName) \
    GAMEPLAYATTRIBUTE_VALUE_GETTER(PropertyName) \
    GAMEPLAYATTRIBUTE_VALUE_SETTER(PropertyName) \
    GAMEPLAYATTRIBUTE_VALUE_INITTER(PropertyName)

UCLASS()
class UMyHealthSet : public UAttributeSet
{
    GENERATED_BODY()
public:
    UMyHealthSet();

    // Called BEFORE any modification - use for clamping current value
    virtual void PreAttributeChange(const FGameplayAttribute& Attribute, float& NewValue) override;

    // Called AFTER instant GE executes - react to changes (death, events)
    virtual void PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data) override;

    virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;

    UPROPERTY(BlueprintReadOnly, ReplicatedUsing = OnRep_Health, Category = "Health")
    FGameplayAttributeData Health;
    ATTRIBUTE_ACCESSORS(UMyHealthSet, Health)

    UPROPERTY(BlueprintReadOnly, ReplicatedUsing = OnRep_MaxHealth, Category = "Health")
    FGameplayAttributeData MaxHealth;
    ATTRIBUTE_ACCESSORS(UMyHealthSet, MaxHealth)

protected:
    UFUNCTION()
    void OnRep_Health(const FGameplayAttributeData& OldHealth);

    UFUNCTION()
    void OnRep_MaxHealth(const FGameplayAttributeData& OldMaxHealth);
};
In the
.cpp
, implement replication and callbacks:
cpp
void UMyHealthSet::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
    Super::GetLifetimeReplicatedProps(OutLifetimeProps);
    DOREPLIFETIME_CONDITION_NOTIFY(UMyHealthSet, Health, COND_None, REPNOTIFY_Always);
    DOREPLIFETIME_CONDITION_NOTIFY(UMyHealthSet, MaxHealth, COND_None, REPNOTIFY_Always);
}

void UMyHealthSet::OnRep_Health(const FGameplayAttributeData& OldHealth)
{
    GAMEPLAYATTRIBUTE_REPNOTIFY(UMyHealthSet, Health, OldHealth);
}

// PreAttributeChange: clamp CURRENT value before any modification
void UMyHealthSet::PreAttributeChange(const FGameplayAttribute& Attribute, float& NewValue)
{
    Super::PreAttributeChange(Attribute, NewValue);
    if (Attribute == GetHealthAttribute())
        NewValue = FMath::Clamp(NewValue, 0.f, GetMaxHealth());
}

// PostGameplayEffectExecute: react AFTER instant GE modifies base value (damage, death)
void UMyHealthSet::PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data)
{
    Super::PostGameplayEffectExecute(Data);
    if (Data.EvaluatedData.Attribute == GetHealthAttribute())
    {
        SetHealth(FMath::Clamp(GetHealth(), 0.f, GetMaxHealth()));
        if (GetHealth() <= 0.f) { /* trigger death */ }
    }
}
cpp
// MyHealthSet.h
#include "AttributeSet.h"
#include "AbilitySystemComponent.h"

// 便捷宏 - 在项目头文件中定义
#define ATTRIBUTE_ACCESSORS(ClassName, PropertyName) \
    GAMEPLAYATTRIBUTE_PROPERTY_GETTER(ClassName, PropertyName) \
    GAMEPLAYATTRIBUTE_VALUE_GETTER(PropertyName) \
    GAMEPLAYATTRIBUTE_VALUE_SETTER(PropertyName) \
    GAMEPLAYATTRIBUTE_VALUE_INITTER(PropertyName)

UCLASS()
class UMyHealthSet : public UAttributeSet
{
    GENERATED_BODY()
public:
    UMyHealthSet();

    // 在属性修改前调用 - 用于限制当前值的范围
    virtual void PreAttributeChange(const FGameplayAttribute& Attribute, float& NewValue) override;

    // 即时GameplayEffect执行后调用 - 响应属性变化(死亡、事件触发)
    virtual void PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data) override;

    virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;

    UPROPERTY(BlueprintReadOnly, ReplicatedUsing = OnRep_Health, Category = "Health")
    FGameplayAttributeData Health;
    ATTRIBUTE_ACCESSORS(UMyHealthSet, Health)

    UPROPERTY(BlueprintReadOnly, ReplicatedUsing = OnRep_MaxHealth, Category = "Health")
    FGameplayAttributeData MaxHealth;
    ATTRIBUTE_ACCESSORS(UMyHealthSet, MaxHealth)

protected:
    UFUNCTION()
    void OnRep_Health(const FGameplayAttributeData& OldHealth);

    UFUNCTION()
    void OnRep_MaxHealth(const FGameplayAttributeData& OldMaxHealth);
};
.cpp
文件中实现复制与回调逻辑:
cpp
void UMyHealthSet::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
    Super::GetLifetimeReplicatedProps(OutLifetimeProps);
    DOREPLIFETIME_CONDITION_NOTIFY(UMyHealthSet, Health, COND_None, REPNOTIFY_Always);
    DOREPLIFETIME_CONDITION_NOTIFY(UMyHealthSet, MaxHealth, COND_None, REPNOTIFY_Always);
}

void UMyHealthSet::OnRep_Health(const FGameplayAttributeData& OldHealth)
{
    GAMEPLAYATTRIBUTE_REPNOTIFY(UMyHealthSet, Health, OldHealth);
}

// PreAttributeChange: 在任何修改前限制当前值的范围
void UMyHealthSet::PreAttributeChange(const FGameplayAttribute& Attribute, float& NewValue)
{
    Super::PreAttributeChange(Attribute, NewValue);
    if (Attribute == GetHealthAttribute())
        NewValue = FMath::Clamp(NewValue, 0.f, GetMaxHealth());
}

// PostGameplayEffectExecute: 即时GameplayEffect修改基础值后响应(伤害、死亡)
void UMyHealthSet::PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data)
{
    Super::PostGameplayEffectExecute(Data);
    if (Data.EvaluatedData.Attribute == GetHealthAttribute())
    {
        SetHealth(FMath::Clamp(GetHealth(), 0.f, GetMaxHealth()));
        if (GetHealth() <= 0.f) { /* 触发死亡逻辑 */ }
    }
}

Register AttributeSet on ASC

在ASC上注册AttributeSet

cpp
// In PlayerState or Character constructor:
// Option 1: CreateDefaultSubobject (auto-registered as subobject)
HealthSet = CreateDefaultSubobject<UMyHealthSet>(TEXT("HealthSet"));

// Option 2: Runtime (authority only) — create and register as subobject:
UMyHealthSet* NewSet = NewObject<UMyHealthSet>(this);
AbilitySystemComponent->AddSpawnedAttribute(NewSet);

// Read attribute value:
float CurrentHealth = AbilitySystemComponent->GetNumericAttribute(
    UMyHealthSet::GetHealthAttribute());
Multiple AttributeSets: An ASC can host multiple
UAttributeSet
subclasses (e.g.,
UHealthSet
+
UOffenseSet
), each auto-discovered via subobject enumeration. Never register two instances of the same class -- the second is silently ignored.

cpp
// 在PlayerState或Character的构造函数中:
// 选项1: CreateDefaultSubobject(自动作为子对象注册)
HealthSet = CreateDefaultSubobject<UMyHealthSet>(TEXT("HealthSet"));

// 选项2: 运行时注册(仅权威端)—— 创建并注册为子对象:
UMyHealthSet* NewSet = NewObject<UMyHealthSet>(this);
AbilitySystemComponent->AddSpawnedAttribute(NewSet);

// 读取属性值:
float CurrentHealth = AbilitySystemComponent->GetNumericAttribute(
    UMyHealthSet::GetHealthAttribute());
多AttributeSet: 一个ASC可以托管多个
UAttributeSet
子类(例如
UHealthSet
+
UOffenseSet
),每个子类会通过子对象枚举自动被发现。不要注册同一类的多个实例——第二个实例会被静默忽略。

GameplayTags

GameplayTags

Defining Tags

定义标签

In
Config/DefaultGameplayTags.ini
or via native tags (preferred for code references):
cpp
// MyGameplayTags.h
#include "NativeGameplayTags.h"
UE_DECLARE_GAMEPLAY_TAG_EXTERN(TAG_Ability_Fireball)
UE_DECLARE_GAMEPLAY_TAG_EXTERN(TAG_State_Stunned)

// MyGameplayTags.cpp
UE_DEFINE_GAMEPLAY_TAG_COMMENT(TAG_Ability_Fireball, "Ability.Skill.Fireball", "Fireball ability")
UE_DEFINE_GAMEPLAY_TAG_COMMENT(TAG_State_Stunned, "State.Stunned", "Actor is stunned")
Config/DefaultGameplayTags.ini
中定义,或通过原生标签定义(代码引用时推荐):
cpp
// MyGameplayTags.h
#include "NativeGameplayTags.h"
UE_DECLARE_GAMEPLAY_TAG_EXTERN(TAG_Ability_Fireball)
UE_DECLARE_GAMEPLAY_TAG_EXTERN(TAG_State_Stunned)

// MyGameplayTags.cpp
UE_DEFINE_GAMEPLAY_TAG_COMMENT(TAG_Ability_Fireball, "Ability.Skill.Fireball", "火球术能力")
UE_DEFINE_GAMEPLAY_TAG_COMMENT(TAG_State_Stunned, "State.Stunned", "Actor处于眩晕状态")

Tag Matching

标签匹配

"A.1".MatchesTag("A") == true
(hierarchical);
MatchesTagExact
requires exact match.
cpp
FGameplayTagContainer Tags;
Tags.HasTag(FireballTag);      // parent-aware match
Tags.HasTagExact(FireballTag); // exact only
Tags.HasAny(OtherContainer);
Tags.HasAll(OtherContainer);

// Query ASC:
ASC->HasMatchingGameplayTag(TAG_State_Stunned);
ASC->GetOwnedGameplayTags(); // FGameplayTagContainer

// Listen for changes:
ASC->RegisterGameplayTagEvent(TAG_State_Stunned, EGameplayTagEventType::NewOrRemoved)
    .AddUObject(this, &AMyCharacter::OnStunnedTagChanged);
"A.1".MatchesTag("A") == true
(层级匹配);
MatchesTagExact
要求完全匹配。
cpp
FGameplayTagContainer Tags;
Tags.HasTag(FireballTag);      // 支持父标签匹配
Tags.HasTagExact(FireballTag); // 仅完全匹配
Tags.HasAny(OtherContainer);
Tags.HasAll(OtherContainer);

// 查询ASC的标签状态:
ASC->HasMatchingGameplayTag(TAG_State_Stunned);
ASC->GetOwnedGameplayTags(); // 返回FGameplayTagContainer

// 监听标签变化:
ASC->RegisterGameplayTagEvent(TAG_State_Stunned, EGameplayTagEventType::NewOrRemoved)
    .AddUObject(this, &AMyCharacter::OnStunnedTagChanged);

Loose Tags (Manual, No GE)

松散标签(手动管理,不关联GameplayEffect)

cpp
ASC->AddLooseGameplayTag(TAG_State_Stunned);
ASC->RemoveLooseGameplayTag(TAG_State_Stunned);
// Loose tags are NOT replicated by default. To replicate a loose tag,
// pass EGameplayTagReplicationState::TagOnly as the third argument:
ASC->AddLooseGameplayTag(TAG_State_Buffed, 1, EGameplayTagReplicationState::TagOnly);
ASC->RemoveLooseGameplayTag(TAG_State_Buffed, 1, EGameplayTagReplicationState::TagOnly);

cpp
ASC->AddLooseGameplayTag(TAG_State_Stunned);
ASC->RemoveLooseGameplayTag(TAG_State_Stunned);
// 松散标签默认不复制。若要复制松散标签,
// 需将第三个参数设为EGameplayTagReplicationState::TagOnly:
ASC->AddLooseGameplayTag(TAG_State_Buffed, 1, EGameplayTagReplicationState::TagOnly);
ASC->RemoveLooseGameplayTag(TAG_State_Buffed, 1, EGameplayTagReplicationState::TagOnly);

GameplayCues

GameplayCues

Cosmetic-only (particles, sounds, decals). Never affect gameplay state. Tag prefix:
GameplayCue.
In the GE asset, add
FGameplayEffectCue
entries with
GameplayCueTags
and level range.
cpp
// Burst (one-shot):
ASC->ExecuteGameplayCue(FGameplayTag::RequestGameplayTag("GameplayCue.Hit.Fire"),
    ASC->MakeEffectContext());

// Persistent (add/remove pair):
ASC->AddGameplayCue(FGameplayTag::RequestGameplayTag("GameplayCue.Buff.Speed"));
ASC->RemoveGameplayCue(FGameplayTag::RequestGameplayTag("GameplayCue.Buff.Speed"));

// Poll active state:
bool bActive = ASC->IsGameplayCueActive(FGameplayTag::RequestGameplayTag("GameplayCue.Buff.Speed"));
Cue Notify classes:
  • AGameplayCueNotify_Actor
    : Persistent/looping. Overrides
    OnActive
    ,
    WhileActive
    ,
    OnRemove
    .
  • AGameplayCueNotify_Static
    : Burst/one-shot. Overrides
    OnExecute
    .
Place cue notify assets in
/Game/GAS/GameplayCues/
for
UGameplayCueManager
auto-discovery.

仅用于表现效果(粒子、音效、贴花),绝不能影响游戏状态。标签前缀为
GameplayCue.
在GameplayEffect资源中添加
FGameplayEffectCue
条目,配置
GameplayCueTags
和等级范围。
cpp
// 一次性触发:
ASC->ExecuteGameplayCue(FGameplayTag::RequestGameplayTag("GameplayCue.Hit.Fire"),
    ASC->MakeEffectContext());

// 持久化触发(需成对调用添加/移除):
ASC->AddGameplayCue(FGameplayTag::RequestGameplayTag("GameplayCue.Buff.Speed"));
ASC->RemoveGameplayCue(FGameplayTag::RequestGameplayTag("GameplayCue.Buff.Speed"));

// 检查激活状态:
bool bActive = ASC->IsGameplayCueActive(FGameplayTag::RequestGameplayTag("GameplayCue.Buff.Speed"));
Cue通知类:
  • AGameplayCueNotify_Actor
    : 持久化/循环效果。重写
    OnActive
    WhileActive
    OnRemove
    方法。
  • AGameplayCueNotify_Static
    : 一次性触发效果。重写
    OnExecute
    方法。
将Cue通知资源放置在
/Game/GAS/GameplayCues/
目录下,以便
UGameplayCueManager
自动发现。

Common Mistakes and Anti-Patterns

常见错误与反模式

ASC ownership confusion: Implement
IAbilitySystemInterface
on the class that owns the ASC (PlayerState), not just on the Pawn. Otherwise
UAbilitySystemBlueprintLibrary
lookups fail.
InitAbilityActorInfo only on server: Clients need it too. Call in
OnRep_PlayerState
(client) and
PossessedBy
(server). Skipping client-side init breaks attribute replication on the owning client.
GEs applied before InitAbilityActorInfo: The ASC is not ready; attributes are not registered. Always complete init before granting abilities or applying effects.
PreAttributeChange vs PostGameplayEffectExecute:
PreAttributeChange
fires on every current-value change (aggregator updates, buff adds/removes). Use it only to clamp. Use
PostGameplayEffectExecute
to react to instant GE base-value execution (damage, death). Never send game events from
PreAttributeChange
.
Forgetting CommitAbility: Without it, the ability runs but consumes no mana and starts no cooldown.
Loose tags not replicated:
AddLooseGameplayTag
does not replicate by default. Pass
EGameplayTagReplicationState::TagOnly
as the third argument to replicate the tag, or grant via a GE for fully replicated effect-driven tags.
Effect stacking overflow: Stacks beyond
LimitCount
are silently rejected. Use
GetCurrentStackCount
to inspect the current level before attempting further stack applications.
GAS with AI: AI has no PlayerState. Place the ASC on the AICharacter, call
InitAbilityActorInfo(AICharacter, AICharacter)
, set replication mode to
Minimal
.
Hot-joining: Late-joining clients receive active effects via
FActiveGameplayEffectsContainer
replication after
InitAbilityActorInfo
. Never apply startup GEs in
BeginPlay
unconditionally -- server-only, or late joiners double-apply.

ASC归属混淆:在拥有ASC的类(如PlayerState)上实现
IAbilitySystemInterface
,而不仅仅是Pawn。否则
UAbilitySystemBlueprintLibrary
的查找会失败。
仅在服务器端初始化AbilityActorInfo:客户端也需要调用该方法。在客户端的
OnRep_PlayerState
和服务器端的
PossessedBy
中都要调用。跳过客户端初始化会导致拥有者客户端的属性复制失效。
在初始化AbilityActorInfo前应用GameplayEffect:此时ASC尚未就绪,属性未完成注册。必须在初始化完成后再授予能力或应用效果。
PreAttributeChange与PostGameplayEffectExecute的误用
PreAttributeChange
会在每次当前值变更时触发(聚合器更新、增益添加/移除)。仅用它来限制数值范围。
PostGameplayEffectExecute
用于响应即时GameplayEffect对基础值的修改(伤害、死亡)。绝不要在
PreAttributeChange
中发送游戏事件。
忘记调用CommitAbility:如果不调用该方法,能力会正常执行但不会消耗资源也不会启动冷却。
松散标签未复制
AddLooseGameplayTag
默认不复制。若要复制标签,需将第三个参数设为
EGameplayTagReplicationState::TagOnly
,或通过GameplayEffect授予标签以实现完全复制的效果驱动标签。
效果叠加溢出:超过
LimitCount
的叠加会被静默拒绝。在尝试叠加前,使用
GetCurrentStackCount
检查当前叠加层数。
AI使用GAS:AI没有PlayerState。将ASC放置在AICharacter上,调用
InitAbilityActorInfo(AICharacter, AICharacter)
,并将复制模式设为
Minimal
热加入:延迟加入的客户端会在
InitAbilityActorInfo
后通过
FActiveGameplayEffectsContainer
复制获取活跃效果。不要在
BeginPlay
中无条件应用启动GameplayEffect——仅在服务器端执行,否则延迟加入的客户端会重复应用。

Reference Files

参考文件

  • references/gas-setup-patterns.md
    — Full ASC ownership patterns and initialization sequences for PlayerState and Character owners, multiplayer and single-player
  • references/gameplay-effect-reference.md
    — Effect configuration, stacking rules, modifier types, execution calculations, periodic effects, conditional effects
  • references/ability-task-reference.md
    — Common built-in ability tasks and custom task patterns
  • references/gas-setup-patterns.md
    — 完整的ASC归属模式和初始化流程,包含PlayerState和Character两种归属场景,覆盖多人和单人游戏
  • references/gameplay-effect-reference.md
    — 效果配置、叠加规则、修改器类型、执行计算、周期性效果、条件效果
  • references/ability-task-reference.md
    — 常用内置Ability Task和自定义任务模式

Related Skills

相关技能

  • ue-actor-component-architecture
    — Component setup and subobject registration
  • ue-networking-replication
    — Replication modes, RPCs, prediction keys
  • ue-animation-system
    — Montage ability tasks (PlayMontageAndWait)
  • ue-gameplay-framework
    — PlayerState ownership pattern, Pawn/Controller lifecycle
  • ue-cpp-foundations
    — Delegate binding, UPROPERTY macros, TSubclassOf patterns
  • ue-actor-component-architecture
    — 组件设置与子对象注册
  • ue-networking-replication
    — 复制模式、RPC、预测键
  • ue-animation-system
    — 动画蒙太奇Ability Task(PlayMontageAndWait)
  • ue-gameplay-framework
    — PlayerState归属模式、Pawn/Controller生命周期
  • ue-cpp-foundations
    — 委托绑定、UPROPERTY宏、TSubclassOf模式