虚幻GameAbilitySystem源码与设计解析-AbilitySystemComponent的实现

// Copyright Epic Games, Inc. All Rights Reserved.

#pragma once

#include "CoreMinimal.h"
#include "UObject/ObjectMacros.h"
#include "Templates/SubclassOf.h"
#include "Engine/NetSerialization.h"
#include "Engine/EngineTypes.h"
#include "Engine/TimerHandle.h"
#include "GameplayTagContainer.h"
#include "AttributeSet.h"
#include "EngineDefines.h"
#include "GameplayPrediction.h"
#include "GameplayCueInterface.h"
#include "GameplayTagAssetInterface.h"
#include "GameplayAbilitySpec.h"
#include "GameplayEffect.h"
#include "GameplayTasksComponent.h"
#include "Abilities/GameplayAbilityRepAnimMontage.h"
#include "Abilities/GameplayAbilityTargetTypes.h"
#include "Abilities/GameplayAbility.h"
#include "AbilitySystemReplicationProxyInterface.h"
#include "Net/Core/PushModel/PushModel.h"

#if UE_ENABLE_INCLUDE_ORDER_DEPRECATED_IN_5_2
#include "Abilities/GameplayAbilityTypes.h"
#include "GameplayEffectTypes.h"
#endif

#include "AbilitySystemComponent.generated.h"

class AGameplayAbilityTargetActor;
class AHUD;
class FDebugDisplayInfo;
class UAnimMontage;
class UAnimSequenceBase;
class UCanvas;
class UInputComponent;
没屁用的头文件
/** 
 * UAbilitySystemComponent
 * 
 * 这是一个便于与能力系统的三个方面进行交互的组件:
 * 
 * 游戏玩法技能(GameplayAbilities):
 *  - 提供一种赋予/分配技能的方式,这些技能可以被使用(例如由玩家或 AI 使用)。
 *  - 提供对实例化技能的管理(必须有东西来持有它们)。
 *  - 提供复制功能:
 *      - 技能状态必须始终在 UGameplayAbility 自身上进行复制,但 UAbilitySystemComponent 为技能的实际激活提供 RPC 复制。
 * 
 * 游戏玩法效果(GameplayEffects):
 *  - 提供一个 FActiveGameplayEffectsContainer 来持有活动的游戏玩法效果。
 *  - 提供将游戏玩法效果应用到目标或自身的方法。
 *  - 提供用于查询 FActiveGameplayEffectsContainer 中信息(持续时间、强度等)的包装器。
 *  - 提供清除/移除游戏玩法效果的方法。
 * 
 * 游戏玩法属性(GameplayAttributes):
 *  - 提供分配和初始化属性集的方法。
 *  - 提供获取属性集的方法。
 */

委托

/** 当目标选择角色拒绝目标确认时调用 */
DECLARE_MULTICAST_DELEGATE_OneParam(FTargetingRejectedConfirmation, int32);

/** 当GA激活失败时调用,传递失败的技能和解释失败原因的标签 */
DECLARE_MULTICAST_DELEGATE_TwoParams(FAbilityFailedDelegate, const UGameplayAbility*, const FGameplayTagContainer&);

/** 当GA结束时调用 */
DECLARE_MULTICAST_DELEGATE_OneParam(FAbilityEnded, UGameplayAbility*);

/** 通知感兴趣的各方GASpec已被修改 */
DECLARE_MULTICAST_DELEGATE_OneParam(FAbilitySpecDirtied, const FGameplayAbilitySpec&);

/** 当GESpec因免疫被活动游戏玩法效果阻止时通知 */
DECLARE_MULTICAST_DELEGATE_TwoParams(FImmunityBlockGE, const FGameplayEffectSpec& /*被阻止的规格*/, const FActiveGameplayEffect* /*免疫游戏玩法效果*/);

/** 我们允许一个委托列表来决定GE的应用是否可以被阻止。如果被阻止,将调用上面的 ImmunityBlockGE 委托 */
DECLARE_DELEGATE_RetVal_TwoParams(bool, FGameplayEffectApplicationQuery, const FActiveGameplayEffectsContainer& /*活动游戏玩法效果容器*/, const FGameplayEffectSpec& /*要考虑的游戏玩法效果规格*/);

 网络复制策略

/** 游戏玩法效果如何复制到客户端 */
UENUM()
enum class EGameplayEffectReplicationMode : uint8
{
    /** 仅复制最少的游戏玩法效果信息。注意:这不适用于拥有的AbilitySystemComponents(使用Mixed模式)。 */
    Minimal,
    /** 仅向模拟代理复制最少的游戏玩法效果信息,但向所有者和自主代理复制完整信息 */
    Mixed,
    /** 向所有对象复制完整的游戏玩法信息 */
    Full,
};

 

/** 当执行操作(如收集可激活的能力)时,我们如何处理待处理的项目(例如尚未添加或移除的能力) */
enum class EConsiderPending : uint8
{
    /** 不考虑任何待处理的操作(如待添加或移除的能力) */
    None = 0,

    /** 执行操作时考虑待添加的项目 */
    PendingAdd = (1 << 0),

    /** 执行操作时考虑待移除的项目 */
    PendingRemove = (1 << 1),

    All = PendingAdd | PendingRemove
};
ENUM_CLASS_FLAGS(EConsiderPending)

没什么用的类头

/** 与游戏玩法能力系统交互的核心ActorComponent */
UCLASS(ClassGroup=AbilitySystem, hidecategories=(Object,LOD,Lighting,Transform,Sockets,TextureStreaming), editinlinenew, meta=(BlueprintSpawnableComponent))
class GAMEPLAYABILITIES_API UAbilitySystemComponent : public UGameplayTasksComponent, public IGameplayTagAssetInterface, public IAbilitySystemReplicationProxyInterface
{
    GENERATED_UCLASS_BODY()

各种回调

/** 用于注册GA Key输入的回调 */
    DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FAbilityAbilityKey, /*UGameplayAbility*, Ability, */int32, InputID);

    /** 用于注册确认/取消输入的回调 */
    DECLARE_DYNAMIC_MULTICAST_DELEGATE(FAbilityConfirmOrCancel);

    /** 当效果应用时的委托 */
    DECLARE_MULTICAST_DELEGATE_ThreeParams(FOnGameplayEffectAppliedDelegate, UAbilitySystemComponent*, const FGameplayEffectSpec&, FActiveGameplayEffectHandle);

Attributes

查 增 (没有删和改)

    /** 查找现有的属性集 */
    template <class T >
    const T*    GetSet() const
    {
        return (T*)GetAttributeSubobject(T::StaticClass());
    }

    /** 查找现有的属性集。如果不存在则断言报错。 */
    template <class T >
    const T*    GetSetChecked() const
    {
        return (T*)GetAttributeSubobjectChecked(T::StaticClass());
    }

    /** 添加一个新的属性集(初始化为默认值) */
    template <class T >
    const T*  AddSet()
    {
        return (T*)GetOrCreateAttributeSubobject(T::StaticClass());
    }

    /** 
     * 手动添加一个新的属性集,该属性集是此能力系统组件的子对象。
     * 此组件的所有子对象在初始化期间会自动添加。
     */
    template <class T>
    const T* AddAttributeSetSubobject(T* Subobject)
    {
        AddSpawnedAttribute(Subobject);
        return Subobject;
    }

是否具有该属性

    /**
     * 此能力系统组件是否具有此属性?
     * 
     * @param Attribute    要从游戏玩法效果句柄中检索目标标签的属性句柄
     * 
     * @return 如果属性有效且此能力系统组件包含包含该属性的属性集,则返回true。否则返回false。
     */
    bool HasAttributeSetForAttribute(FGameplayAttribute Attribute) const;

    /** 从数据表初始化起始属性。支持不佳,带有曲线表引用的游戏玩法效果可能是更好的解决方案 */
    const UAttributeSet* InitStats(TSubclassOf<class UAttributeSet> Attributes, const UDataTable* DataTable);

    UFUNCTION(BlueprintCallable, Category="Skills", meta=(DisplayName="InitStats", ScriptName="InitStats"))
    void K2_InitStats(TSubclassOf<class UAttributeSet> Attributes, const UDataTable* DataTable);
        
    /** 返回此能力系统组件的所有属性列表 */
    UFUNCTION(BlueprintPure, Category="Gameplay Attributes")
    void GetAllAttributes(TArray<FGameplayAttribute>& OutAttributes);

属性实例

    /**
     * 返回属性集实例的引用,如果此组件中存在的话
     *
     * @param AttributeSetClass 要查找的属性集类型
     * @param bFound 如果属性集实例存在则设置为true
     */
    UFUNCTION(BlueprintCallable, BlueprintPure = false, Category = "Gameplay Attributes", meta=(DeterminesOutputType = AttributeSetClass))
    const UAttributeSet* GetAttributeSet(TSubclassOf<UAttributeSet> AttributeSetClass) const;

返回当前值

    /**
     * 返回给定游戏玩法属性的当前值,如果未找到属性则返回零。
     * 注意:这不会考虑预测的游戏玩法效果修改器,因此客户端上的值可能并非始终准确。
     *
     * @param Attribute 要查询的游戏玩法属性
     * @param bFound 如果属性存在于该组件中则设置为true
     */
    UFUNCTION(BlueprintPure, Category = "Gameplay Attributes")
    float GetGameplayAttributeValue(FGameplayAttribute Attribute, bool& bFound) const;

    UPROPERTY(EditAnywhere, Category="AttributeTest")
    TArray<FAttributeDefaults>    DefaultStartingData;

属性集操作

/** 移除所有当前的属性集并注册传入数组中的属性集。注意,尽可能直接调用Add/Remove更好。 */
    void SetSpawnedAttributes(const TArray<UAttributeSet*>& NewAttributeSet);

    UE_DEPRECATED(5.1, "This function will be made private. Use Add/Remove SpawnedAttributes instead")
    TArray<TObjectPtr<UAttributeSet>>& GetSpawnedAttributes_Mutable();

    /** 当你不打算修改列表时访问已生成的属性列表。 */
    const TArray<UAttributeSet*>& GetSpawnedAttributes() const;

    /** 添加一个新的属性集 */
    void AddSpawnedAttribute(UAttributeSet* Attribute);

    /** 移除一个现有的属性集 */
    void RemoveSpawnedAttribute(UAttributeSet* Attribute);

    /** 移除所有属性集 */
    void RemoveAllSpawnedAttributes();

关联的动画实例Tag没看懂

  /** 此组件将在其中播放蒙太奇的关联动画实例。使用NAME_None表示主动画实例。 */
    UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Skills")
    FName AffectedAnimInstanceTag; 

获取

 /** 设置属性的基础值。现有的活动修饰符不会被清除,而是作用于新的基础值。 */
    void SetNumericAttributeBase(const FGameplayAttribute &Attribute, float NewBaseValue);

    /** 获取属性的基础值。即,没有状态修饰符的属性值 */
    float GetNumericAttributeBase(const FGameplayAttribute &Attribute) const;

*******很重要的修改功能******

    /**
     * 对给定属性应用就地修改。这将正确更新属性的聚合器,更新属性集属性,
     * 并调用OnDirty回调。
     * 这不会调用属性集上的Pre/PostGameplayEffectExecute调用。这不会进行标签检查、应用要求、免疫等操作。
     * 不会创建或应用GameplayEffectSpec!
     * 这仅应在应用真正的GameplayEffectSpec太慢或不可能的情况下使用。
     */
    void ApplyModToAttribute(const FGameplayAttribute &Attribute, TEnumAsByte<EGameplayModOp::Type> ModifierOp, float ModifierMagnitude);
/**
     * 对给定属性应用就地修改。与ApplyModToAttribute不同,此函数将在客户端或服务器上运行。
     * 这可能会导致与预测相关的问题,并且无法正确回滚。
     */
    void ApplyModToAttributeUnsafe(const FGameplayAttribute &Attribute, TEnumAsByte<EGameplayModOp::Type> ModifierOp, float ModifierMagnitude);

返回当前值或者应用filter后的值

    /** 返回属性的当前(最终)值 */
    float GetNumericAttribute(const FGameplayAttribute &Attribute) const;
    float GetNumericAttributeChecked(const FGameplayAttribute &Attribute) const;

    /** 返回应用标签过滤器后的属性值 */
    float GetFilteredAttributeValue(const FGameplayAttribute& Attribute, const FGameplayTagRequirements& SourceTags, const FGameplayTagContainer& TargetTags, const TArray<FActiveGameplayEffectHandle>& HandlesToIgnore = TArray<FActiveGameplayEffectHandle>());

网络部分

    // ----------------------------------------------------------------------------------------------------------------
    //    Replication
    // ----------------------------------------------------------------------------------------------------------------

    /** 强制化身演员更新其复制。对于因移动/位置原因需要复制的情况很有用。 */
    virtual void ForceAvatarReplication();

    /** 当为true时,我们将不为这个能力系统组件复制活动的游戏玩法效果,因此也不复制属性和标签 */
    virtual void SetReplicationMode(EGameplayEffectReplicationMode NewReplicationMode);

网络复制

    /** 游戏玩法效果如何复制 */
    EGameplayEffectReplicationMode ReplicationMode;

    /** 如果ReplicationProxyEnabled,通过谁来路由复制(如果返回null,当ReplicationProxyEnabled时,我们将不进行复制)  */
    virtual IAbilitySystemReplicationProxyInterface* GetReplicationInterface();

    /** 当前预测键,使用FScopedPredictionWindow设置 */
    FPredictionKey    ScopedPredictionKey;

    /** 返回任何操作应使用的预测键 */
    FPredictionKey GetPredictionKeyForNewAction() const
    {
        return ScopedPredictionKey.IsValidForMorePrediction() ? ScopedPredictionKey : FPredictionKey();
    }

    /** 我们是否有有效的预测键来进行更多的预测操作 */
    bool CanPredict() const
    {
        return ScopedPredictionKey.IsValidForMorePrediction();
    }

    /** 如果在服务器上运行或有有效的预测键,则返回true */
    bool HasAuthorityOrPredictionKey(const FGameplayAbilityActivationInfo* ActivationInfo) const;

    /** 如果此组件的演员具有权限,则返回true */
    virtual bool IsOwnerActorAuthoritative() const;

    /** 如果此组件应记录蒙太奇复制信息,则返回true。 */
    virtual bool ShouldRecordMontageReplication() const;

    /** 向客户端或服务器复制能力已结束/取消,视情况而定 */
    virtual void ReplicateEndOrCancelAbility(FGameplayAbilitySpecHandle Handle, FGameplayAbilityActivationInfo ActivationInfo, UGameplayAbility* Ability, bool bWasCanceled);

    /** 强制取消能力,并且不向另一侧复制。当能力被另一侧取消时应调用此函数 */
    virtual void ForceCancelAbilityDueToReplication(UGameplayAbility* Instance);

待激活GA

 /** 一个待激活的能力,目前无法激活,稍后会重新检查 */
    struct FPendingAbilityInfo
    {
        bool operator==(const FPendingAbilityInfo& Other) const
        {
            // 不比较事件数据,同一键和句柄但不同事件数据的多个激活无效
            return PredictionKey == Other.PredictionKey    && Handle == Other.Handle;
        }

        /** 需要激活的能力的属性 */
        FGameplayAbilitySpecHandle Handle;
        FPredictionKey    PredictionKey;
        FGameplayEventData TriggerEventData;

        /** 如果此能力是远程激活的并且需要跟进,则为true,否则如果能力尚未激活则为false */
        bool bPartiallyActivated;

        FPendingAbilityInfo()
            : bPartiallyActivated(false)
        {}
    };

待激活列表

    /** 这是一个在服务器上激活但客户端尚无法执行的游戏玩法能力列表。稍后会尝试执行这些能力 */
    TArray<FPendingAbilityInfo> PendingServerActivatedAbilities;

GE

// ----------------------------------------------------------------------------------------------------------------
//    游戏玩法效果:供其他系统使用的主要对外API
// ----------------------------------------------------------------------------------------------------------------

/** 将之前创建的GESpec应用到一个目标上 */
UFUNCTION(蓝图可调用, 类别 = 游戏玩法效果, 元数据 = (显示名称 = "ApplyGameplayEffectSpecToTarget", 脚本名称 = "ApplyGameplayEffectSpecToTarget"))
FActiveGameplayEffectHandle BP_ApplyGameplayEffectSpecToTarget(const FGameplayEffectSpecHandle& SpecHandle, UAbilitySystemComponent* Target);

// 将之前创建的GESpec应用到一个目标上
virtual FActiveGameplayEffectHandle ApplyGameplayEffectSpecToTarget(const FGameplayEffectSpec& GameplayEffect, UAbilitySystemComponent *Target, FPredictionKey PredictionKey=FPredictionKey());

/** 将之前创建的GESpec应用到自身——BP ver */
UFUNCTION(蓝图可调用, 类别 = 游戏玩法效果, 元数据 = (显示名称 = "ApplyGameplayEffectSpecToSelf", 脚本名称 = "ApplyGameplayEffectSpecToSelf"))
FActiveGameplayEffectHandle BP_ApplyGameplayEffectSpecToSelf(const FGameplayEffectSpecHandle& SpecHandle);

// 将之前创建的GESpec应用到自身——native ver*/
virtual FActiveGameplayEffectHandle ApplyGameplayEffectSpecToSelf(const FGameplayEffectSpec& GameplayEffect, FPredictionKey PredictionKey = FPredictionKey());

/** 根据传入的句柄获取活动的GE */
const UGameplayEffect* GetGameplayEffectDefForHandle(FActiveGameplayEffectHandle Handle);

/** 根据句柄移除游戏玩法效果。StacksToRemove = -1 表示移除所有堆叠。 */
UFUNCTION(蓝图可调用, 仅蓝图具有权限, 类别 = 游戏玩法效果)
virtual bool RemoveActiveGameplayEffect(FActiveGameplayEffectHandle Handle, int32 StacksToRemove=-1);

移除GE

/** 
 * 移除所有基于GESpec类定义的活动GE
 * 
 * @param GameplayEffect 要移除的游戏玩法效果的类;若为空,则不执行任何操作
 * @param InstigatorAbilitySystemComponent 若指定该参数,则仅移除由这个引发者的能力系统组件所应用的游戏玩法效果
 * @param StacksToRemove 要移除的堆叠层数,-1 表示移除所有堆叠
 */
    UFUNCTION(BlueprintCallable, BlueprintAuthorityOnly, Category = GameplayEffects)
    virtual void RemoveActiveGameplayEffectBySourceEffect(TSubclassOf<UGameplayEffect> GameplayEffect, UAbilitySystemComponent* InstigatorAbilitySystemComponent, int32 StacksToRemove = -1);

    /** 获取一个准备好应用到其他对象上的传出GESoec。 */
    UFUNCTION(BlueprintCallable, Category = GameplayEffects)
    virtual FGameplayEffectSpecHandle MakeOutgoingSpec(TSubclassOf<UGameplayEffect> GameplayEffectClass, float Level, FGameplayEffectContextHandle Context) const;

    /** 为这个能力系统组件的所有者创建一个GESpec上下文。 */
    UFUNCTION(BlueprintCallable, Category = GameplayEffects)
    virtual FGameplayEffectContextHandle MakeEffectContext() const;

GE

/**
 * 获取指定源游戏玩法效果在能力系统组件上的数量。对于非堆叠效果,这是所有活动实例的数量总和。
 * 对于堆叠效果,这是所有有效堆叠计数的总和。如果指定了引发者,则只统计由该引发者应用的效果。
 * 
 * @param SourceGameplayEffect 要统计数量的效果
 * @param OptionalInstigatorFilterComponent 如果指定,只统计由这个能力系统组件应用的效果
 * 
 * @return 指定源游戏玩法效果的数量
 */
    UFUNCTION(BlueprintCallable, BlueprintPure, Category=GameplayEffects)
    int32 GetGameplayEffectCount(TSubclassOf<UGameplayEffect> SourceGameplayEffect, UAbilitySystemComponent* OptionalInstigatorFilterComponent, bool bEnforceOnGoingCheck = true) const;

GE Spec统计

/**
 * 获取指定源游戏玩法效果在能力系统组件上的数量。对于非堆叠效果,这是所有活动实例的数量总和。
 * 对于堆叠效果,这是所有有效堆叠计数的总和。如果指定了引发者,则只统计由该引发者应用的效果。
 * 
 * @param SoftSourceGameplayEffect 要统计数量的效果。如果该效果当前未加载,数量为 0
 * @param OptionalInstigatorFilterComponent 如果指定,只统计由这个能力系统组件应用的效果
 * 
 * @return 指定源游戏玩法效果的数量
 */
    UFUNCTION(BlueprintCallable, BlueprintPure, Category = GameplayEffects)
    int32 GetGameplayEffectCount_IfLoaded(TSoftClassPtr<UGameplayEffect> SoftSourceGameplayEffect, UAbilitySystemComponent* OptionalInstigatorFilterComponent, bool bEnforceOnGoingCheck = true) const;

GE堆叠相关

/** 返回所有通过查询的游戏玩法效果的堆叠计数之和 */
int32 GetAggregatedStackCount(const FGameplayEffectQuery& Query) const;

/** 此函数存在仅仅是为了能连接到多播委托 */
UE_DEPRECATED_FORGAME(5.5, "此函数不应设为公共函数。使用 RemoveActiveGameplayEffect(该函数不允许无权限方移除效果),若你想打破此规则,可暴露 RemoveActiveGameplayEffect_AllowClientRemoval 函数。")
void RemoveActiveGameplayEffect_NoReturn(FActiveGameplayEffectHandle Handle, int32 StacksToRemove = -1);

/** 针对预测性添加的游戏玩法提示调用此函数。若预测有误,需移除标签计数,并可能触发移除事件 */
virtual void OnPredictiveGameplayCueCatchup(FGameplayTag Tag);

/** 返回一个游戏玩法效果的总持续时间 */
float GetGameplayEffectDuration(FActiveGameplayEffectHandle Handle) const;

/** 每当服务器时间通过游戏状态进行复制时调用此函数,以确保我们的冷却计时器与服务器时间同步 */
virtual void RecomputeGameplayEffectStartTimes(const float WorldTime, const float ServerWorldTime);

/** 返回一个游戏玩法效果的开始时间和总持续时间 */
void GetGameplayEffectStartTimeAndDuration(FActiveGameplayEffectHandle Handle, float& StartEffectTime, float& Duration) const;

动态更新

/** 动态更新一个正在生效的游戏玩法效果中由调用者设置的幅度值 */
    UFUNCTION(BlueprintCallable, BlueprintAuthorityOnly, Category = GameplayEffects)
    virtual void UpdateActiveGameplayEffectSetByCallerMagnitude(FActiveGameplayEffectHandle ActiveHandle, UPARAM(meta=(Categories = "SetByCaller"))FGameplayTag SetByCallerTag, float NewValue);

/** 动态更新一个正在生效的游戏玩法效果中多个由调用者设置的幅度值 */
    /** Dynamically update multiple set-by-caller magnitudes for an active gameplay effect */
    UFUNCTION(BlueprintCallable, BlueprintAuthorityOnly, Category = GameplayEffects)
    virtual void UpdateActiveGameplayEffectSetByCallerMagnitudes(FActiveGameplayEffectHandle ActiveHandle, const TMap<FGameplayTag, float>& NewSetByCallerValues);

/** 更新一个已经应用的游戏玩法效果的等级。目的是实现无缝更新,而不是像移除并重新应用那样 */
    UFUNCTION(BlueprintCallable, BlueprintAuthorityOnly, Category = GameplayEffects)
    virtual void SetActiveGameplayEffectLevel(FActiveGameplayEffectHandle ActiveHandle, int32 NewLevel);
/** 通过查询更新一个已经应用的游戏玩法效果的等级。目的是实现无缝更新,而不是像移除并重新应用那样 */
    UFUNCTION(BlueprintCallable, BlueprintAuthorityOnly, Category = GameplayEffects)
    virtual void SetActiveGameplayEffectLevelUsingQuery(FGameplayEffectQuery Query, int32 NewLevel);

/** 抑制一个正在生效的游戏玩法效果,使其禁用但不移除 */
UE_DEPRECATED(5.4, "使用 SetActiveGameplayEffectInhibit 并传入 MoveTemp(ActiveGEHandle),这样可以明确该句柄不再有效。检查(然后使用)返回的 FActiveGameplayEffectHandle 来继续你的操作。")

抑制效果

/**
 * (解除)抑制一个正在生效的游戏玩法效果,使其可以被禁用(并执行一些禁用操作,例如移除标签)。
 * 一个被抑制的正在生效的游戏玩法效果会处于休眠状态,等待在某些条件(通常是标签)满足时重新启用。当解除抑制时,它将重新应用其部分功能。
 * 注意:传入的 ActiveGEHandle 句柄将不再有效。使用返回的 FActiveGameplayEffectHandle 句柄来判断该活动游戏玩法效果是否仍然处于激活状态。
 */
    UE_DEPRECATED(5.4, "Use SetActiveGameplayEffectInhibit with a MoveTemp(ActiveGEHandle) so it's clear the Handle is no longer valid. Check (then use) the returned FActiveGameplayEffectHandle to continue your operation.")
    virtual void InhibitActiveGameplayEffect(FActiveGameplayEffectHandle ActiveGEHandle, bool bInhibit, bool bInvokeGameplayCueEvents);

查询GE效果 一般用于UI

/**
 * 直接访问以查询游戏玩法效果的影响幅度,但结果不一定总是准确的。外部代码(如用户界面等)应该如何询问诸如“这个游戏玩法效果使我的伤害增加了多少”之类的问题呢?
 * (最有可能的是,我们希望在后端处理这个问题——当应用伤害时,我们可以获取到数值是如何变化的完整信息。但我们可能仍然需要像下面这样的轮询方法(我的伤害会是多少))
 */
    UFUNCTION(BlueprintCallable, Category = GameplayEffects)
    float GetGameplayEffectMagnitude(FActiveGameplayEffectHandle Handle, FGameplayAttribute Attribute) const;

返回GE各种描述

/** 返回已应用的游戏玩法效果的当前堆叠数量 */
int32 GetCurrentStackCount(FActiveGameplayEffectHandle Handle) const;

/** 返回已应用的游戏玩法效果的当前堆叠数量,但通过该游戏玩法效果授予的能力规格句柄来查询 */
int32 GetCurrentStackCount(FGameplayAbilitySpecHandle Handle) const;

/** 返回描述活动游戏玩法效果的调试字符串 */
FString GetActiveGEDebugString(FActiveGameplayEffectHandle Handle) const;

/** 获取授予传入能力的游戏玩法效果的句柄 */
FActiveGameplayEffectHandle FindActiveGameplayEffectHandle(FGameplayAbilitySpecHandle Handle) const;

/** 返回指向实际活动游戏玩法效果结构的常量指针 */
const FActiveGameplayEffect* GetActiveGameplayEffect(const FActiveGameplayEffectHandle Handle) const;

/** 返回此能力系统组件上的所有活动游戏玩法效果 */
const FActiveGameplayEffectsContainer& GetActiveGameplayEffects() const;

/** 返回与活动句柄关联的游戏玩法效果的默认对象的常量指针 */
const UGameplayEffect* GetGameplayEffectCDO(const FActiveGameplayEffectHandle Handle) const;

通过GE获取源头标签

/**
 * 若可能,从指定句柄所代表的游戏玩法规格中获取源标签
 * 
 * @param Handle 用于检索源标签的游戏玩法效果的句柄
 * 
 * @return 若可能,返回该句柄所代表的游戏玩法规格中的源标签
 */
    const FGameplayTagContainer* GetGameplayEffectSourceTagsFromHandle(FActiveGameplayEffectHandle Handle) const;

 

/**
 * 若可能,从指定句柄所代表的游戏玩法规格中获取目标标签
 * 
 * @param Handle 用于检索目标标签的游戏玩法效果的句柄
 * 
 * @return 若可能,返回该句柄所代表的游戏玩法规格中的目标标签
 */
    const FGameplayTagContainer* GetGameplayEffectTargetTagsFromHandle(FActiveGameplayEffectHandle Handle) const;

获得捕获Spec(可能是二次封装用的)并非暴露给实际开发

/**
 * 用从组件中捕获属性所需的数据填充指定的捕获规格
 * 
 * @param OutCaptureSpec [输出] 用于填充捕获数据的捕获规格
 */
    void CaptureAttributeForGameplayEffect(OUT FGameplayEffectAttributeCaptureSpec& OutCaptureSpec);

 

// ----------------------------------------------------------------------------------------------------------------
//  回调 / 通知
//  (这些回调和通知需要处于 UObject 级别,这样我们才能安全地进行绑定。而不是直接在 ActiveGameplayEffect/Container 级别进行绑定,因为如果 AbilitySystemComponent 被销毁,直接绑定是不安全的。)
// ----------------------------------------------------------------------------------------------------------------
在游戏开发中,尤其是涉及到能力系统组件(AbilitySystemComponent)、活动游戏玩法效果(ActiveGameplayEffect)和容器(Container)等概念时,经常需要使用回调和通知机制来处理各种事件。例如,当一个游戏玩法效果开始、结束或者属性值发生变化时,需要触发相应的逻辑。
这里提到将回调和通知放在 UObject 级别进行绑定是出于安全性的考虑。UObject 是虚幻引擎中很多类的基类,具有自己的生命周期管理机制。如果直接在 ActiveGameplayEffect 或其容器级别进行绑定,当 AbilitySystemComponent 被销毁时,这些绑定可能会变成悬空指针,从而导致程序崩溃或出现未定义行为。而在 UObject 级别绑定,可以利用 UObject 的垃圾回收和生命周期管理,确保绑定的安全性。
/** 当特定属性聚合器的值发生变化时调用,此时游戏玩法效果会刷新其值 */
void OnAttributeAggregatorDirty(FAggregator* Aggregator, FGameplayAttribute Attribute, bool FromRecursiveCall=false);

/** 当属性幅度发生变化时调用,用于将信息转发给依赖这些属性的游戏玩法效果 */
void OnMagnitudeDependencyChange(FActiveGameplayEffectHandle Handle, const FAggregator* ChangedAggregator);

/** 此能力系统组件已成功将游戏玩法效果应用到某个目标(可能是自身) */
void OnGameplayEffectAppliedToTarget(UAbilitySystemComponent* Target, const FGameplayEffectSpec& SpecApplied, FActiveGameplayEffectHandle ActiveHandle);
void OnGameplayEffectAppliedToSelf(UAbilitySystemComponent* Source, const FGameplayEffectSpec& SpecApplied, FActiveGameplayEffectHandle ActiveHandle);
void OnPeriodicGameplayEffectExecuteOnTarget(UAbilitySystemComponent* Target, const FGameplayEffectSpec& SpecExecuted, FActiveGameplayEffectHandle ActiveHandle);
void OnPeriodicGameplayEffectExecuteOnSelf(UAbilitySystemComponent* Source, const FGameplayEffectSpec& SpecExecuted, FActiveGameplayEffectHandle ActiveHandle);

/** 当游戏玩法效果的持续时间发生变化时调用 */
virtual void OnGameplayEffectDurationChange(struct FActiveGameplayEffect& ActiveEffect);

/** 每当游戏玩法效果应用到自身时在服务器上调用,包括即时和基于持续时间的效果 */
FOnGameplayEffectAppliedDelegate OnGameplayEffectAppliedDelegateToSelf;

/** 每当游戏玩法效果应用到其他目标时在服务器上调用,包括即时和基于持续时间的效果 */
FOnGameplayEffectAppliedDelegate OnGameplayEffectAppliedDelegateToTarget;

/** 每当添加基于持续时间的游戏玩法效果时在客户端和服务器上调用(例如,即时效果不会触发此事件) */
FOnGameplayEffectAppliedDelegate OnActiveGameplayEffectAddedDelegateToSelf;

/** 每当周期性游戏玩法效果在自身上执行时在服务器上调用 */
FOnGameplayEffectAppliedDelegate OnPeriodicGameplayEffectExecuteDelegateOnSelf;

/** 每当周期性游戏玩法效果在目标上执行时在服务器上调用 */
FOnGameplayEffectAppliedDelegate OnPeriodicGameplayEffectExecuteDelegateOnTarget;

/** 免疫通知支持 */
FImmunityBlockGE OnImmunityBlockGameplayEffectDelegate;

/** 注册属性值变化事件,建议使用 GetGameplayAttributeValueChangeDelegate 替代 */
FOnGameplayAttributeChange& RegisterGameplayAttributeEvent(FGameplayAttribute Attribute);

/** 注册属性值变化事件 */
FOnGameplayAttributeValueChange& GetGameplayAttributeValueChangeDelegate(FGameplayAttribute Attribute);

/** 任何时候能力被激活(开始)时的通用回调 */
FGenericAbilityDelegate AbilityActivatedCallbacks;

/** 任何时候能力结束时的回调 */
FAbilityEnded AbilityEndedCallbacks;

/** 任何时候能力结束时的回调,带有额外信息 */
FGameplayAbilityEndedDelegate OnAbilityEnded;

/** 任何时候能力被提交(应用成本/冷却时间)时的通用回调 */
FGenericAbilityDelegate AbilityCommittedCallbacks;

/** 当能力激活失败时,携带失败原因调用 */
FAbilityFailedDelegate AbilityFailedCallbacks;

/** 当能力规格的内部发生变化时调用 */
FAbilitySpecDirtied AbilitySpecDirtiedCallbacks;

/** 我们允许用户设置一系列函数,只有这些函数返回 true 时,游戏玩法效果才能被应用 */
TArray<FGameplayEffectApplicationQuery> GameplayEffectApplicationQueries;

/** 调用上面的通知回调 */
virtual void NotifyAbilityCommit(UGameplayAbility* Ability);
virtual void NotifyAbilityActivated(const FGameplayAbilitySpecHandle Handle, UGameplayAbility* Ability);
virtual void NotifyAbilityFailed(const FGameplayAbilitySpecHandle Handle, UGameplayAbility* Ability, const FGameplayTagContainer& FailureReason);

/** 当任何游戏玩法效果被移除时调用 */
FOnGivenActiveGameplayEffectRemoved& OnAnyGameplayEffectRemovedDelegate();

/** 返回允许绑定多个游戏玩法效果变化事件的委托结构 */
FActiveGameplayEffectEvents* GetActiveEffectEventSet(FActiveGameplayEffectHandle Handle);
FOnActiveGameplayEffectRemoved_Info* OnGameplayEffectRemoved_InfoDelegate(FActiveGameplayEffectHandle Handle);
FOnActiveGameplayEffectStackChange* OnGameplayEffectStackChangeDelegate(FActiveGameplayEffectHandle Handle);
FOnActiveGameplayEffectTimeChange* OnGameplayEffectTimeChangeDelegate(FActiveGameplayEffectHandle Handle);
FOnActiveGameplayEffectInhibitionChanged* OnGameplayEffectInhibitionChangedDelegate(FActiveGameplayEffectHandle Handle);

GameplayTag操作

这段代码主要围绕游戏玩法标签(Gameplay Tags)的操作展开,实现了 IGameplayTagAssetInterface 接口。游戏玩法标签在游戏开发里常被用于标记游戏对象的各种状态、属性或行为,通过这些标签可以方便地进行条件判断、事件触发等操作。代码借助 TagCountContainer 容器来管理标签,提供了标签匹配检查、获取拥有的标签、查询标签计数、设置标签计数以及更新标签计数等功能。

// ----------------------------------------------------------------------------------------------------------------
//  游戏玩法标签操作
//  使用标签计数容器实现 IGameplayTagAssetInterface 接口
// ----------------------------------------------------------------------------------------------------------------
/**
 * 检查是否存在与指定游戏玩法标签匹配的标签
 * @param TagToCheck 要检查的游戏玩法标签
 * @return 如果存在匹配的标签则返回 true,否则返回 false
 */
FORCEINLINE bool HasMatchingGameplayTag(FGameplayTag TagToCheck) const override
{
    return GameplayTagCountContainer.HasMatchingGameplayTag(TagToCheck);
}

/**
 * 检查是否存在与指定标签容器中所有标签都匹配的标签
 * @param TagContainer 包含要检查的标签的容器
 * @return 如果存在与所有标签都匹配的标签则返回 true,否则返回 false
 */
FORCEINLINE bool HasAllMatchingGameplayTags(const FGameplayTagContainer& TagContainer) const override
{
    return GameplayTagCountContainer.HasAllMatchingGameplayTags(TagContainer);
}

/**
 * 检查是否存在与指定标签容器中任意一个标签匹配的标签
 * @param TagContainer 包含要检查的标签的容器
 * @return 如果存在与任意一个标签匹配的标签则返回 true,否则返回 false
 */
FORCEINLINE bool HasAnyMatchingGameplayTags(const FGameplayTagContainer& TagContainer) const override
{
    return GameplayTagCountContainer.HasAnyMatchingGameplayTags(TagContainer);
}

/**
 * 获取当前拥有的游戏玩法标签,并填充到指定的标签容器中
 * @param TagContainer 用于存储拥有的游戏玩法标签的容器
 */
FORCEINLINE void GetOwnedGameplayTags(FGameplayTagContainer& TagContainer) const override
{
    TagContainer.Reset();
    TagContainer.AppendTags(GetOwnedGameplayTags());
}

/**
 * 获取当前拥有的游戏玩法标签
 * @return 包含拥有的游戏玩法标签的容器的常量引用
 */
[[nodiscard]] FORCEINLINE const FGameplayTagContainer& GetOwnedGameplayTags() const
{
    return GameplayTagCountContainer.GetExplicitGameplayTags();
}

/**
 * 检查指定的标签查询是否与当前拥有的游戏玩法标签匹配
 * @param TagQuery 要检查的标签查询
 * @return 如果匹配则返回 true,否则返回 false
 */
FORCEINLINE bool MatchesGameplayTagQuery(const FGameplayTagQuery& TagQuery) const
{
    return TagQuery.Matches(GameplayTagCountContainer.GetExplicitGameplayTags());
}

/**
 * 返回指定标签的实例数量
 * @param TagToCheck 要查询实例数量的标签
 * @return 指定标签的实例数量
 */
FORCEINLINE int32 GetTagCount(FGameplayTag TagToCheck) const
{
    return GameplayTagCountContainer.GetTagCount(TagToCheck);
}

/**
 * 强制设置指定标签的实例数量
 * @param Tag 要设置实例数量的标签
 * @param NewCount 要设置的新的实例数量
 */
FORCEINLINE void SetTagMapCount(const FGameplayTag& Tag, int32 NewCount)
{
    GameplayTagCountContainer.SetTagCount(Tag, NewCount);
}

/**
 * 更新指定标签的实例数量,并在更新成功时调用回调函数
 * @param BaseTag 要更新实例数量的基础标签
 * @param CountDelta 实例数量的变化量
 */
FORCEINLINE void UpdateTagMap(const FGameplayTag& BaseTag, int32 CountDelta)
{
    if (GameplayTagCountContainer.UpdateTagCount(BaseTag, CountDelta))
    {
        OnTagUpdated(BaseTag, CountDelta > 0);
    }
}

/**
 * 更新指定标签容器中所有标签的实例数量,并在更新成功时调用回调函数
 * @param Container 包含要更新实例数量的标签的容器
 * @param CountDelta 实例数量的变化量
 */
FORCEINLINE void UpdateTagMap(const FGameplayTagContainer& Container, int32 CountDelta)
{
    if (!Container.IsEmpty())
    {
        UpdateTagMap_Internal(Container, CountDelta);
    }
}

/**
 * 将阻塞能力的标签填充到指定的标签容器中
 * @param TagContainer 用于存储阻塞能力标签的容器
 */
FORCEINLINE void GetBlockedAbilityTags(FGameplayTagContainer& TagContainer) const
{
    TagContainer.AppendTags(GetBlockedAbilityTags());
}

/**
 * 获取阻塞能力的游戏玩法标签
 * @return 包含阻塞能力标签的容器的常量引用
 */
[[nodiscard]] FORCEINLINE const FGameplayTagContainer& GetBlockedAbilityTags() const
{
    return BlockedAbilityTags.GetExplicitGameplayTags();
}

 

/**
 * 允许游戏代码添加不依赖于游戏玩法效果(GameplayEffect)的松散游戏玩法标签。
 * 通过这种方式添加的标签不会被复制!如果需要复制这些标签,请使用这些函数的“Replicated”版本。
 * 调用的游戏代码需要确保在必要的客户端/服务器上添加这些标签。
 */
    FORCEINLINE void AddReplicatedLooseGameplayTag(const FGameplayTag& GameplayTag)
    {
        GetReplicatedLooseTags_Mutable().AddTag(GameplayTag);
    }

    FORCEINLINE void AddReplicatedLooseGameplayTags(const FGameplayTagContainer& GameplayTags)
    {
        GetReplicatedLooseTags_Mutable().AddTags(GameplayTags);
    }

    FORCEINLINE void RemoveReplicatedLooseGameplayTag(const FGameplayTag& GameplayTag)
    {
        GetReplicatedLooseTags_Mutable().RemoveTag(GameplayTag);
    }

    FORCEINLINE void RemoveReplicatedLooseGameplayTags(const FGameplayTagContainer& GameplayTags)
    {
        GetReplicatedLooseTags_Mutable().RemoveTags(GameplayTags);
    }

    FORCEINLINE void SetReplicatedLooseGameplayTagCount(const FGameplayTag& GameplayTag, int32 NewCount)
    {
        GetReplicatedLooseTags_Mutable().SetTagCount(GameplayTag, NewCount);
    }

 

/**
 * 最小化复制标签是指在启用 `bMinimalReplication` 模式时,来自游戏玩法效果(Gameplay Effects,简称 GEs)的可复制标签。
 * (此时游戏玩法效果本身不会被复制,但它们所授予的标签会通过这些函数进行复制。)
 */

    FORCEINLINE void AddMinimalReplicationGameplayTag(const FGameplayTag& GameplayTag)
    {
        GetMinimalReplicationTags_Mutable().AddTag(GameplayTag);
    }

    FORCEINLINE void AddMinimalReplicationGameplayTags(const FGameplayTagContainer& GameplayTags)
    {
        GetMinimalReplicationTags_Mutable().AddTags(GameplayTags);
    }

    FORCEINLINE void RemoveMinimalReplicationGameplayTag(const FGameplayTag& GameplayTag)
    {
        GetMinimalReplicationTags_Mutable().RemoveTag(GameplayTag);
    }

    FORCEINLINE void RemoveMinimalReplicationGameplayTags(const FGameplayTagContainer& GameplayTags)
    {
        GetMinimalReplicationTags_Mutable().RemoveTags(GameplayTags);
    }

 

/** 允许为特定游戏玩法标签的添加或移除注册事件 */
FOnGameplayEffectTagCountChanged& RegisterGameplayTagEvent(FGameplayTag Tag, EGameplayTagEventType::Type EventType=EGameplayTagEventType::NewOrRemoved);

/** 注销之前添加的事件 */
bool UnregisterGameplayTagEvent(FDelegateHandle DelegateHandle, FGameplayTag Tag, EGameplayTagEventType::Type EventType=EGameplayTagEventType::NewOrRemoved);

/** 注册一个标签事件并立即调用它 */
FDelegateHandle RegisterAndCallGameplayTagEvent(FGameplayTag Tag, FOnGameplayEffectTagCountChanged::FDelegate Delegate, EGameplayTagEventType::Type EventType=EGameplayTagEventType::NewOrRemoved);

/** 返回一个多播委托,每当有标签被添加或移除时(但仅当标签是“新增”或“移除”时,而不是仅计数增加时)会调用该委托 */
FOnGameplayEffectTagCountChanged& RegisterGenericGameplayTagEvent();

/** 执行一个游戏玩法事件。返回该事件触发的成功激活的能力数量 */
virtual int32 HandleGameplayEvent(FGameplayTag EventTag, const FGameplayEventData* Payload);

/** 当游戏玩法事件发生时添加一个新的委托进行调用。仅当事件匹配传入的过滤器容器中的任何标签时,该委托才会被调用 */
FDelegateHandle AddGameplayEventTagContainerDelegate(const FGameplayTagContainer& TagFilter, const FGameplayEventTagMulticastDelegate::FDelegate& Delegate);

/** 移除之前注册的委托 */
void RemoveGameplayEventTagContainerDelegate(const FGameplayTagContainer& TagFilter, FDelegateHandle DelegateHandle);

/** 绑定到游戏玩法标签的回调,只有在使用确切标签时才会激活。若要处理标签层次结构,请使用 AddGameplayEventContainerDelegate */
TMap<FGameplayTag, FGameplayEventMulticastDelegate> GenericGameplayEventCallbacks;

属性编辑功能 内部属性 (似乎不是暴露给外面用的function)

// ----------------------------------------------------------------------------------------------------------------
//  系统属性
// ----------------------------------------------------------------------------------------------------------------

/** 内部属性,用于修改此组件创建的游戏玩法效果的持续时间 */
UPROPERTY(meta=(SystemGameplayAttribute="true"))
float OutgoingDuration;

/** 内部属性,用于修改应用到此组件的游戏玩法效果的持续时间 */
UPROPERTY(meta = (SystemGameplayAttribute = "true"))
float IncomingDuration;

/** 获取 OutgoingDuration 属性的指针 */
static FProperty* GetOutgoingDurationProperty();

/** 获取 IncomingDuration 属性的指针 */
static FProperty* GetIncomingDurationProperty();

/** 获取 OutgoingDuration 属性的捕获定义 */
static const FGameplayEffectAttributeCaptureDefinition& GetOutgoingDurationCapture();

/** 获取 IncomingDuration 属性的捕获定义 */
static const FGameplayEffectAttributeCaptureDefinition& GetIncomingDurationCapture();

头两个非常重要

// ----------------------------------------------------------------------------------------------------------------
//  额外辅助函数
// ----------------------------------------------------------------------------------------------------------------

/** 将游戏玩法效果应用到传入的目标上 */
UFUNCTION(BlueprintCallable, Category = GameplayEffects, meta=(DisplayName = "ApplyGameplayEffectToTarget", ScriptName = "ApplyGameplayEffectToTarget"))
FActiveGameplayEffectHandle BP_ApplyGameplayEffectToTarget(TSubclassOf<UGameplayEffect> GameplayEffectClass, UAbilitySystemComponent *Target, float Level, FGameplayEffectContextHandle Context);
FActiveGameplayEffectHandle ApplyGameplayEffectToTarget(UGameplayEffect *GameplayEffect, UAbilitySystemComponent *Target, float Level = UGameplayEffect::INVALID_LEVEL, FGameplayEffectContextHandle Context = FGameplayEffectContextHandle(), FPredictionKey PredictionKey = FPredictionKey());

/** 将游戏玩法效果应用到自身 */
UFUNCTION(BlueprintCallable, Category = GameplayEffects, meta=(DisplayName = "ApplyGameplayEffectToSelf", ScriptName = "ApplyGameplayEffectToSelf"))
FActiveGameplayEffectHandle BP_ApplyGameplayEffectToSelf(TSubclassOf<UGameplayEffect> GameplayEffectClass, float Level, FGameplayEffectContextHandle EffectContext);
FActiveGameplayEffectHandle ApplyGameplayEffectToSelf(const UGameplayEffect *GameplayEffect, float Level, const FGameplayEffectContextHandle& EffectContext, FPredictionKey PredictionKey = FPredictionKey());

/** 返回当前此能力系统组件上激活的游戏玩法效果的数量 */
int32 GetNumActiveGameplayEffects() const
{
    return ActiveGameplayEffects.GetNumGameplayEffects();
}

/** 复制此能力组件上的所有激活效果 */
void GetAllActiveGameplayEffectSpecs(TArray<FGameplayEffectSpec>& OutSpecCopies) const
{
    ActiveGameplayEffects.GetAllActiveGameplayEffectSpecs(OutSpecCopies);
}

/** 从 OnRep 函数调用,在客户端设置属性的基础值 */
void SetBaseAttributeValueFromReplication(const FGameplayAttribute& Attribute, float NewValue, float OldValue)
{
    ActiveGameplayEffects.SetBaseAttributeValueFromReplication(Attribute, FGameplayAttributeData(NewValue), FGameplayAttributeData(OldValue) );
}

/** 从 OnRep 函数调用,在客户端设置属性的基础值 */
void SetBaseAttributeValueFromReplication(const FGameplayAttribute& Attribute, const FGameplayAttributeData& NewValue, const FGameplayAttributeData& OldValue)
{
    ActiveGameplayEffects.SetBaseAttributeValueFromReplication(Attribute, NewValue, OldValue);
}

/** 测试此游戏玩法效果中的所有修饰符是否会使属性值大于 0.0f */
bool CanApplyAttributeModifiers(const UGameplayEffect *GameplayEffect, float Level, const FGameplayEffectContextHandle& EffectContext)
{
    return ActiveGameplayEffects.CanApplyAttributeModifiers(GameplayEffect, Level, EffectContext);
}

/** 获取所有匹配查询条件的效果的剩余时间 */
TArray<float> GetActiveEffectsTimeRemaining(const FGameplayEffectQuery& Query) const;

/** 获取所有匹配查询条件的效果的总持续时间 */
TArray<float> GetActiveEffectsDuration(const FGameplayEffectQuery& Query) const;

/** 获取所有匹配查询条件的效果的剩余时间和总持续时间 */
TArray<TPair<float,float>> GetActiveEffectsTimeRemainingAndDuration(const FGameplayEffectQuery& Query) const;

/** 返回符合查询条件的激活效果列表 */
UFUNCTION(BlueprintCallable, BlueprintPure=false, Category = "GameplayEffects", meta=(DisplayName = "Get Active Gameplay Effects for Query"))
TArray<FActiveGameplayEffectHandle> GetActiveEffects(const FGameplayEffectQuery& Query) const;

/** 返回拥有所有传入标签的激活效果列表 */
UFUNCTION(BlueprintCallable, BlueprintPure = false, Category = "GameplayEffects")
TArray<FActiveGameplayEffectHandle> GetActiveEffectsWithAllTags(FGameplayTagContainer Tags) const;

/** 这将给出所有匹配此查询的效果结束的世界时间。如果多个效果匹配,返回最后结束的那个时间 */
float GetActiveEffectsEndTime(const FGameplayEffectQuery& Query) const;
float GetActiveEffectsEndTimeWithInstigators(const FGameplayEffectQuery& Query, TArray<AActor*>& Instigators) const;

/** 返回结束时间和总持续时间 */
bool GetActiveEffectsEndTimeAndDuration(const FGameplayEffectQuery& Query, float& EndTime, float& Duration) const;

/** 修改游戏玩法效果的开始时间,以处理最初计时器不同步的问题 */
virtual void ModifyActiveEffectStartTime(FActiveGameplayEffectHandle Handle, float StartTimeDiff);

/** 移除所有包含 Tags 中任意标签的激活效果 */
UFUNCTION(BlueprintCallable, Category = GameplayEffects)
int32 RemoveActiveEffectsWithTags(FGameplayTagContainer Tags);

/** 移除所有捕获的源标签包含 Tags 中任意标签的激活效果 */
UFUNCTION(BlueprintCallable, Category = GameplayEffects)
int32 RemoveActiveEffectsWithSourceTags(FGameplayTagContainer Tags);

/** 移除所有应用了 Tags 中任意标签的激活效果 */
UFUNCTION(BlueprintCallable, Category = GameplayEffects)
int32 RemoveActiveEffectsWithAppliedTags(FGameplayTagContainer Tags);

/** 移除所有授予了 Tags 中任意标签的激活效果 */
UFUNCTION(BlueprintCallable, Category = GameplayEffects)
int32 RemoveActiveEffectsWithGrantedTags(FGameplayTagContainer Tags);

/** 移除所有匹配给定查询条件的激活效果。StacksToRemove = -1 表示移除所有堆叠 */
virtual int32 RemoveActiveEffects(const FGameplayEffectQuery& Query, int32 StacksToRemove = -1);

GameCue

    // Do not call these functions directly, call the wrappers on GameplayCueManager instead
    UFUNCTION(NetMulticast, unreliable)
    void NetMulticast_InvokeGameplayCueExecuted_FromSpec(const FGameplayEffectSpecForRPC Spec, FPredictionKey PredictionKey) override;

    UFUNCTION(NetMulticast, unreliable)
    void NetMulticast_InvokeGameplayCueExecuted(const FGameplayTag GameplayCueTag, FPredictionKey PredictionKey, FGameplayEffectContextHandle EffectContext) override;

    UFUNCTION(NetMulticast, unreliable)
    void NetMulticast_InvokeGameplayCuesExecuted(const FGameplayTagContainer GameplayCueTags, FPredictionKey PredictionKey, FGameplayEffectContextHandle EffectContext) override;

    UFUNCTION(NetMulticast, unreliable)
    void NetMulticast_InvokeGameplayCueExecuted_WithParams(const FGameplayTag GameplayCueTag, FPredictionKey PredictionKey, FGameplayCueParameters GameplayCueParameters) override;

    UFUNCTION(NetMulticast, unreliable)
    void NetMulticast_InvokeGameplayCuesExecuted_WithParams(const FGameplayTagContainer GameplayCueTags, FPredictionKey PredictionKey, FGameplayCueParameters GameplayCueParameters) override;

    UFUNCTION(NetMulticast, unreliable)
    void NetMulticast_InvokeGameplayCueAdded(const FGameplayTag GameplayCueTag, FPredictionKey PredictionKey, FGameplayEffectContextHandle EffectContext) override;

    UFUNCTION(NetMulticast, unreliable)
    void NetMulticast_InvokeGameplayCueAdded_WithParams(const FGameplayTag GameplayCueTag, FPredictionKey PredictionKey, FGameplayCueParameters Parameters) override;
    
    UFUNCTION(NetMulticast, unreliable)
    void NetMulticast_InvokeGameplayCueAddedAndWhileActive_FromSpec(const FGameplayEffectSpecForRPC& Spec, FPredictionKey PredictionKey) override;

    UFUNCTION(NetMulticast, unreliable)
    void NetMulticast_InvokeGameplayCueAddedAndWhileActive_WithParams(const FGameplayTag GameplayCueTag, FPredictionKey PredictionKey, FGameplayCueParameters GameplayCueParameters) override;

    UFUNCTION(NetMulticast, unreliable)
    void NetMulticast_InvokeGameplayCuesAddedAndWhileActive_WithParams(const FGameplayTagContainer GameplayCueTags, FPredictionKey PredictionKey, FGameplayCueParameters GameplayCueParameters) override;
不要直接使用
/** 游戏玩法提示(GameplayCues)也可以单独触发。这些函数可以接收一个可选的效果上下文,用于传递命中结果等信息 */
// 执行一个游戏玩法提示,使用效果上下文
void ExecuteGameplayCue(const FGameplayTag GameplayCueTag, FGameplayEffectContextHandle EffectContext = FGameplayEffectContextHandle());
// 执行一个游戏玩法提示,使用游戏玩法提示参数
void ExecuteGameplayCue(const FGameplayTag GameplayCueTag, const FGameplayCueParameters& GameplayCueParameters);

/** 添加一个持续的游戏玩法提示 */
// 添加持续游戏玩法提示,使用效果上下文
void AddGameplayCue(const FGameplayTag GameplayCueTag, FGameplayEffectContextHandle EffectContext = FGameplayEffectContextHandle());
// 添加持续游戏玩法提示,使用游戏玩法提示参数
void AddGameplayCue(const FGameplayTag GameplayCueTag, const FGameplayCueParameters& GameplayCueParameters);

/** 在最小复制模式下添加游戏玩法提示。仅应在非最小复制模式下会以其他方式(例如通过游戏玩法效果)复制游戏玩法提示的路径中调用 */
void AddGameplayCue_MinimalReplication(const FGameplayTag GameplayCueTag, FGameplayEffectContextHandle EffectContext = FGameplayEffectContextHandle());

/** 移除一个持续的游戏玩法提示 */
void RemoveGameplayCue(const FGameplayTag GameplayCueTag);

/** 在最小复制模式下移除游戏玩法提示。仅应在非最小复制模式下会以其他方式(例如通过游戏玩法效果)复制游戏玩法提示的路径中调用 */
void RemoveGameplayCue_MinimalReplication(const FGameplayTag GameplayCueTag);

/** 移除所有单独添加的游戏玩法提示,即不是作为游戏玩法效果一部分添加的提示 */
void RemoveAllGameplayCues();

/** 处理来自外部源的游戏玩法提示事件 */
// 使用游戏玩法效果规格 RPC 结构体处理游戏玩法提示事件
void InvokeGameplayCueEvent(const FGameplayEffectSpecForRPC& Spec, EGameplayCueEvent::Type EventType);
// 使用游戏玩法提示标签和事件类型处理游戏玩法提示事件,可传入效果上下文
void InvokeGameplayCueEvent(const FGameplayTag GameplayCueTag, EGameplayCueEvent::Type EventType, FGameplayEffectContextHandle EffectContext = FGameplayEffectContextHandle());
// 使用游戏玩法提示标签、事件类型和游戏玩法提示参数处理游戏玩法提示事件
void InvokeGameplayCueEvent(const FGameplayTag GameplayCueTag, EGameplayCueEvent::Type EventType, const FGameplayCueParameters& GameplayCueParameters);

/** 允许轮询以查看某个游戏玩法提示是否处于激活状态。我们预计大多数游戏玩法提示处理是基于事件的,但在某些情况下,我们可能需要检查某个游戏玩法提示是否激活(例如动画蓝图) */
UFUNCTION(BlueprintCallable, Category="GameplayCue", meta=(GameplayTagFilter="GameplayCue"))
bool IsGameplayCueActive(const FGameplayTag GameplayCueTag) const
{
    return HasMatchingGameplayTag(GameplayCueTag);
}

/** 将使用此能力系统组件的所有者(引发者)和化身角色(效果引发者)初始化游戏玩法提示参数 */
virtual void InitDefaultGameplayCueParameters(FGameplayCueParameters& Parameters);

/** 我们是否准备好触发游戏玩法提示了? */
virtual bool IsReadyForGameplayCues();

/** 处理在进行网络增量序列化且等待化身角色加载时可能被延迟的游戏玩法提示 */
virtual void HandleDeferredGameplayCues(const FActiveGameplayEffectsContainer* GameplayEffectsContainer);

/** 为所有激活且未被抑制的游戏玩法效果上的游戏玩法提示触发 “WhileActive” 事件。这通常用于 “重生” 或化身网格体/角色发生变化的情况 */
UE_DEPRECATED(5.4, "ReinvokeActiveGameplayCues 未被使用,并且其逻辑与预测游戏玩法效果不一致。如果需要,你可以在自己的项目中实现它。")
virtual void ReinvokeActiveGameplayCues();

 

GA相关

/**
 * 游戏玩法技能(GameplayAbilities)
 * 
 * 能力系统组件(AbilitySystemComponent)在技能方面的作用是提供:
 *  - 管理技能实例(无论是每个角色一个实例,还是每次执行一个实例)。
 *      - 总得有人来跟踪这些实例。
 *      - 非实例化的技能 *可以* 在能力系统组件中不包含任何技能相关内容的情况下执行。
 *          它们应该能够在游戏玩法技能角色信息(GameplayAbilityActorInfo)和游戏玩法技能(GameplayAbility)的基础上运行。
 * 
 * 为了方便使用,它可能还会提供一些其他功能:
 *  - 一些基本的输入绑定(无论是实例化还是非实例化的技能)。
 *  - 诸如“此组件拥有这些技能”这样的概念。
 */

极为重要和常用

/*
 * 授予一项技能。
 * 如果当前角色没有权限(非权威端),此操作将被忽略。
 * 返回一个句柄,可用于诸如 TryActivateAbility 等函数中。
 * 
 * @param AbilitySpec 一个 FGameplayAbilitySpec 结构体,包含了技能类、技能等级以及要绑定的输入 ID 等信息。
 */
    FGameplayAbilitySpecHandle GiveAbility(const FGameplayAbilitySpec& AbilitySpec);
/*
 * 授予一项技能并尝试仅激活它一次,激活后该技能将被移除。
 * 此操作仅在服务器端有效,并且该技能的网络执行策略不能设置为本地(Local)或本地预测(Local Predicted)。
 * 
 * @param AbilitySpec 一个 FGameplayAbilitySpec 结构体,包含了技能类、技能等级以及要绑定的输入 ID 等信息。
 * @param GameplayEventData 可选的激活事件数据。如果提供了该数据,将调用 “从事件激活技能(Activate Ability From Event)” 方法而非 “激活技能(ActivateAbility)” 方法,并传递该事件数据。
 */
    FGameplayAbilitySpecHandle GiveAbilityAndActivateOnce(FGameplayAbilitySpec& AbilitySpec, const FGameplayEventData* GameplayEventData = nullptr);
/**
 * 授予一个游戏玩法技能并返回其句柄。
 * 如果角色没有权限(非权威端),此操作将被忽略。
 *
 * @param AbilityClass 要授予的技能类型
 * @param Level 授予技能时的等级
 * @param InputID 用于绑定技能激活的输入 ID 值
 */
    UFUNCTION(BlueprintCallable, BlueprintAuthorityOnly, Category = "Gameplay Abilities", meta = (DisplayName = "Give Ability", ScriptName = "GiveAbility"))
    FGameplayAbilitySpecHandle K2_GiveAbility(TSubclassOf<UGameplayAbility> AbilityClass, int32 Level = 0, int32 InputID = -1);
/**
 * 授予一个游戏玩法技能,激活它一次,然后移除该技能。
 * 如果角色没有权限(非权威端),此操作将被忽略。
 *
 * @param AbilityClass 要授予的技能类型
 * @param Level 授予技能时的等级
 * @param InputID 用于绑定技能激活的输入 ID 值
 */
    UFUNCTION(BlueprintCallable, BlueprintAuthorityOnly, Category = "Gameplay Abilities", meta = (DisplayName = "Give Ability And Activate Once", ScriptName = "GiveAbilityAndActivateOnce"))
    FGameplayAbilitySpecHandle K2_GiveAbilityAndActivateOnce(TSubclassOf<UGameplayAbility> AbilityClass, int32 Level = 0, int32 InputID = -1);
/** 清除所有“授予的”技能。如果角色没有权限(非权威端),此操作将被忽略。 */
    UFUNCTION(BlueprintCallable, BlueprintAuthorityOnly, Category="Gameplay Abilities")
    void ClearAllAbilities();
/**
 * 清除所有绑定到给定输入 ID 的技能。
 * 如果角色没有权限(非权威端),此操作将被忽略。
 *
 * @param InputID 要移除的技能所绑定的数字输入 ID。
 */
    UFUNCTION(BlueprintCallable, BlueprintAuthorityOnly, Category="Gameplay Abilities")
    void ClearAllAbilities();
/**
 * 清除所有绑定到给定输入 ID 的技能。
 * 如果角色没有权限(非权威端),此操作将被忽略。
 *
 * @param InputID 要移除的技能所绑定的数字输入 ID。
 */
    UFUNCTION(BlueprintCallable, BlueprintAuthorityOnly, Category = "Gameplay Abilities")
    void ClearAllAbilitiesWithInputID(int32 InputID = 0);
    /** 
     * Removes the specified ability.
     * This will be ignored if the actor is not authoritative.
     * 
     * @param Handle Ability Spec Handle of the ability we want to remove
     */
    UFUNCTION(BlueprintCallable, BlueprintAuthorityOnly, Category = "Gameplay Abilities")
    void ClearAbility(const FGameplayAbilitySpecHandle& Handle);
    
    /** Sets an ability spec to remove when its finished. If the spec is not currently active, it terminates it immediately. Also clears InputID of the Spec. */
    void SetRemoveAbilityOnEnd(FGameplayAbilitySpecHandle AbilitySpecHandle);
/** 
 * 获取所有可激活的游戏玩法技能,这些技能需满足以下两个条件:一是匹配 GameplayTagContainer 中所有的标签;二是其 DoesAbilitySatisfyTagRequirements() 函数返回 true。
 * 后面这个条件使得该函数能够在无需深入了解技能细节的情况下找到正确的技能。例如,如果有两个 “近战” 技能,其中一个需要武器,另一个需要徒手状态,那么这些技能可以利用阻塞标签和必需标签来决定何时可以触发。
 * 使用满足标签要求这一机制简化了很多使用场景。例如,行为树可以使用各种装饰器来测试通过此机制获取的技能,并且可以使用任务来执行该技能,而无需知道实际上存在多个此类技能。
 */
    void GetActivatableGameplayAbilitySpecsByAllMatchingTags(const FGameplayTagContainer& GameplayTagContainer, TArray < struct FGameplayAbilitySpec* >& MatchingGameplayAbilities, bool bOnlyAbilitiesThatSatisfyTagRequirements = true) const;
/** 
 * 尝试激活所有匹配给定标签且通过 DoesAbilitySatisfyTagRequirements() 检查的游戏玩法技能。
 * 如果有任何技能尝试激活,则返回 true。可能会激活多个技能,并且这些技能之后可能会激活失败。
 * 如果 bAllowRemoteActivation 为 true,它将远程激活本地/服务器端的技能;如果为 false,则仅尝试本地激活技能。
 */
UFUNCTION(BlueprintCallable, Category = "Abilities")
bool TryActivateAbilitiesByTag(const FGameplayTagContainer& GameplayTagContainer, bool bAllowRemoteActivation = true);

/**
 * 尝试激活传入的技能类对应的技能。在激活前会检查技能的消耗和激活条件。
 * 如果认为技能已激活则返回 true,但由于后续激活过程中可能失败,可能会出现误判为激活成功的情况。
 * 如果 bAllowRemoteActivation 为 true,它将远程激活本地/服务器端的技能;如果为 false,则仅尝试本地激活技能。
 */
UFUNCTION(BlueprintCallable, Category = "Abilities")
bool TryActivateAbilityByClass(TSubclassOf<UGameplayAbility> InAbilityToActivate, bool bAllowRemoteActivation = true);

/** 
 * 尝试激活给定句柄对应的技能,在激活前会检查技能的消耗和激活条件。
 * 如果认为技能已激活则返回 true,但由于后续激活过程中可能失败,可能会出现误判为激活成功的情况。
 * 如果 bAllowRemoteActivation 为 true,它将远程激活本地/服务器端的技能;如果为 false,则仅尝试本地激活技能。
 */
UFUNCTION(BlueprintCallable, Category = "Abilities")
bool TryActivateAbility(FGameplayAbilitySpecHandle AbilityToActivate, bool bAllowRemoteActivation = true);

/** 检查是否存在可激活且已触发的带有指定标签的技能 */
bool HasActivatableTriggeredAbility(FGameplayTag Tag);

/** 从游戏玩法事件触发一个技能,根据执行标志,该技能只会在本地或服务器端触发 */
bool TriggerAbilityFromGameplayEvent(FGameplayAbilitySpecHandle AbilityToTrigger, FGameplayAbilityActorInfo* ActorInfo, FGameplayTag Tag, const FGameplayEventData* Payload, UAbilitySystemComponent& Component);
// ----------------------------------------------------------------------------------------------------------------
// Ability Cancelling/Interrupts
// ----------------------------------------------------------------------------------------------------------------
/** 取消指定的技能类默认对象(CDO)对应的技能。 */
void CancelAbility(UGameplayAbility* Ability);

/** 取消由传入的技能规格句柄所指示的技能。如果在已激活的技能中未找到该句柄,则不执行任何操作。 */
void CancelAbilityHandle(const FGameplayAbilitySpecHandle& AbilityHandle);

/** 取消所有带有指定标签的技能。不会取消忽略的实例。
 * @param WithTags 要取消的技能必须包含的标签容器,默认为 nullptr。
 * @param WithoutTags 要取消的技能必须不包含的标签容器,默认为 nullptr。
 * @param Ignore 要忽略的技能实例,默认为 nullptr。
 */
void CancelAbilities(const FGameplayTagContainer* WithTags = nullptr, const FGameplayTagContainer* WithoutTags = nullptr, UGameplayAbility* Ignore = nullptr);

/** 取消所有技能,无论其标签如何。不会取消忽略的实例。
 * @param Ignore 要忽略的技能实例,默认为 nullptr。
 */
void CancelAllAbilities(UGameplayAbility* Ignore = nullptr);

/** 取消所有技能,并销毁任何剩余的实例化技能。 */
virtual void DestroyActiveState();

 

/** 
 * 从技能激活或原生代码调用,将应用正确的技能阻塞标签并取消现有的技能。子类可以重写此行为
 * 
 * @param AbilityTags 具有阻塞和取消标志的技能的标签
 * @param RequestingAbility 请求更改的游戏玩法技能,对于原生事件可以为 NULL
 * @param bEnableBlockTags 如果为 true,将启用阻塞标签;如果为 false,将禁用阻塞标签
 * @param BlockTags 要阻塞的标签
 * @param bExecuteCancelTags 如果为 true,将取消匹配标签的技能
 * @param CancelTags 要取消的技能对应的标签
 */
virtual void ApplyAbilityBlockAndCancelTags(const FGameplayTagContainer& AbilityTags, UGameplayAbility* RequestingAbility, bool bEnableBlockTags, const FGameplayTagContainer& BlockTags, bool bExecuteCancelTags, const FGameplayTagContainer& CancelTags);

/** 当一个技能可取消或不可取消时调用。默认情况下不执行任何操作,可以重写以关联游戏玩法事件 */
virtual void HandleChangeAbilityCanBeCanceled(const FGameplayTagContainer& AbilityTags, UGameplayAbility* RequestingAbility, bool bCanBeCanceled) {}

/** 如果传入的任何标签被阻塞,则返回 true */
virtual bool AreAbilityTagsBlocked(const FGameplayTagContainer& Tags) const;

/** 阻塞或取消对特定技能标签的阻塞 */
void BlockAbilitiesWithTags(const FGameplayTagContainer& Tags);
void UnBlockAbilitiesWithTags(const FGameplayTagContainer& Tags);

/** 检查技能系统当前是否正在阻塞某个输入 ID。如果输入 ID 被阻塞,则返回 true;否则返回 false。 */
bool IsAbilityInputBlocked(int32 InputID) const;

/** 阻塞或取消对特定输入 ID 的阻塞 */
void BlockAbilityByInputID(int32 InputID);
void UnBlockAbilityByInputID(int32 InputID);
// ----------------------------------------------------------------------------------------------------------------
// Functions meant to be called from GameplayAbility and subclasses, but not meant for general use
// ----------------------------------------------------------------------------------------------------------------
/** 返回所有可激活技能的列表。只读。 */
const TArray<FGameplayAbilitySpec>& GetActivatableAbilities() const
{
    return ActivatableAbilities.Items;
}

/** 返回所有可激活技能的列表。 */
TArray<FGameplayAbilitySpec>& GetActivatableAbilities()
{
    return ActivatableAbilities.Items;
}

/** 返回技能激活时的本地世界时间。在权威端(服务器)和自主代理端(控制客户端)有效。  */
float GetAbilityLastActivatedTime() const { return AbilityLastActivatedTime; }

/** 根据句柄返回一个技能规格。如果要修改,请调用 MarkAbilitySpecDirty。将返回值视为临时的,因为该指针可能会在后续对 AbilitySystemComponent 的任何调用中失效。 */
FGameplayAbilitySpec* FindAbilitySpecFromHandle(FGameplayAbilitySpecHandle Handle, EConsiderPending ConsiderPending = EConsiderPending::PendingRemove) const;

/** 根据游戏玩法效果句柄返回一个技能规格。如果要修改,请调用 MarkAbilitySpecDirty */
UE_DEPRECATED(5.3, "FindAbilitySpecFromGEHandle 从来都不准确,因为一个游戏玩法效果可以授予多个游戏玩法技能。现在它返回 nullptr。")
FGameplayAbilitySpec* FindAbilitySpecFromGEHandle(FActiveGameplayEffectHandle Handle) const;

/**
 * 返回从一个游戏玩法效果句柄授予的所有技能规格句柄。只有服务器可以调用此函数。
 * @param ScopeLock - 此锁用于告知调用者,返回值仅在该锁的作用域内有效
 * @param Handle - 授予我们要查找的技能的活动游戏玩法效果的句柄
 * @param ConsiderPending - 我们是否返回正在等待添加/移除的技能规格?
 */
TArray<const FGameplayAbilitySpec*> FindAbilitySpecsFromGEHandle(const FScopedAbilityListLock& ScopeLock, FActiveGameplayEffectHandle Handle, EConsiderPending ConsiderPending = EConsiderPending::PendingRemove) const;

/** 返回与给定技能类对应的技能规格。如果要修改,请调用 MarkAbilitySpecDirty */
FGameplayAbilitySpec* FindAbilitySpecFromClass(TSubclassOf<UGameplayAbility> InAbilityClass) const;

/** 根据句柄返回一个技能规格。如果要修改,请调用 MarkAbilitySpecDirty */
FGameplayAbilitySpec* FindAbilitySpecFromInputID(int32 InputID) const;

/**
 * 返回所有具有给定输入 ID 的技能
 *
 * @param InputID 要匹配的输入 ID
 * @param OutAbilitySpecs 指向匹配规格的指针数组
 */
virtual void FindAllAbilitySpecsFromInputID(int32 InputID, TArray<const FGameplayAbilitySpec*>& OutAbilitySpecs) const;

/**
 * 根据技能类、等级和可选的输入 ID 构建一个简单的 FGameplayAbilitySpec
 */
virtual FGameplayAbilitySpec BuildAbilitySpecFromClass(TSubclassOf<UGameplayAbility> AbilityClass, int32 Level = 0, int32 InputID = -1);

/**
 * 返回一个包含所有授予的技能句柄的数组
 * 注意:目前这不包括正在激活中的技能
 * 
 * @param OutAbilityHandles 此数组将填充授予的技能规格句柄
 */
UFUNCTION(BlueprintCallable, BlueprintPure = false, Category = "Gameplay Abilities")
void GetAllAbilities(TArray<FGameplayAbilitySpecHandle>& OutAbilityHandles) const;

/**
 * 返回一个包含所有匹配提供的标签的技能的数组
 *
 * @param OutAbilityHandles 此数组将填充匹配的技能规格句柄
 * @param Tags 要匹配的游戏玩法标签
 * @param bExactMatch 如果为 true,标签必须精确匹配。否则,将返回匹配任何标签的技能
 */
UFUNCTION(BlueprintCallable, BlueprintPure = false, Category = "Gameplay Abilities")
void FindAllAbilitiesWithTags(TArray<FGameplayAbilitySpecHandle>& OutAbilityHandles, FGameplayTagContainer Tags, bool bExactMatch = true) const;

/**
 * 返回一个包含所有匹配提供的游戏玩法标签查询的技能的数组
 *
 * @param OutAbilityHandles 此数组将填充匹配的技能规格句柄
 * @param Query 要匹配的游戏玩法标签查询
 */
UFUNCTION(BlueprintCallable, BlueprintPure = false, Category = "Gameplay Abilities")
void FindAllAbilitiesMatchingQuery(TArray<FGameplayAbilitySpecHandle>& OutAbilityHandles, FGameplayTagQuery Query) const;

/**
 * 返回一个包含所有绑定到某个输入 ID 值的技能的数组
 *
 * @param OutAbilityHandles 此数组将填充匹配的技能规格句柄
 * @param InputID 要匹配的输入 ID
 */
UFUNCTION(BlueprintCallable, BlueprintPure = false, Category = "Gameplay Abilities")
void FindAllAbilitiesWithInputID(TArray<FGameplayAbilitySpecHandle>& OutAbilityHandles, int32 InputID = 0) const;

/** 从活动游戏玩法效果的句柄中检索游戏玩法效果的效果上下文。 */
FGameplayEffectContextHandle GetEffectContextFromActiveGEHandle(FActiveGameplayEffectHandle Handle);

/** 调用此函数以标记一个技能规格已被修改 */
void MarkAbilitySpecDirty(FGameplayAbilitySpec& Spec, bool WasAddOrRemove=false);

/** 尝试激活给定的技能,只有从正确的客户端/服务器上下文调用时才会生效 */
bool InternalTryActivateAbility(FGameplayAbilitySpecHandle AbilityToActivate, FPredictionKey InPredictionKey = FPredictionKey(), UGameplayAbility ** OutInstancedAbility = nullptr, FOnGameplayAbilityEnded::FDelegate* OnGameplayAbilityEndedDelegate = nullptr, const FGameplayEventData* TriggerEventData = nullptr);

/** InternalTryActivateAbility 使用的失败标签(例如,这存储了最后一次调用 InternalTryActivateAbility 的失败标签) */
FGameplayTagContainer InternalTryActivateAbilityFailureTags;

/** 由技能调用,让组件知道它已结束 */
virtual void NotifyAbilityEnded(FGameplayAbilitySpecHandle Handle, UGameplayAbility* Ability, bool bWasCancelled);

void ClearAbilityReplicatedDataCache(FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActivationInfo& ActivationInfo);

/** 由 FScopedAbilityListLock 调用 */
void IncrementAbilityListLock();
void DecrementAbilityListLock();
// ----------------------------------------------------------------------------------------------------------------
// Debugging
// ----------------------------------------------------------------------------------------------------------------

 

// ----------------------------------------------------------------------------------------------------------------
// 调试
// ----------------------------------------------------------------------------------------------------------------

/**
 * 能力系统组件调试信息结构体
 * 用于存储调试过程中所需的各种信息,包括画布、显示选项、位置信息等
 */
struct FAbilitySystemComponentDebugInfo
{
    // 构造函数,将结构体中的所有成员初始化为零
    FAbilitySystemComponentDebugInfo()
    {
        FMemory::Memzero(*this);
    }

    // 用于绘制调试信息的画布
    class UCanvas* Canvas;

    // 是否将调试信息打印到日志中
    bool bPrintToLog;

    // 是否显示属性信息
    bool bShowAttributes;
    // 是否显示游戏玩法效果信息
    bool bShowGameplayEffects;
    // 是否显示技能信息
    bool bShowAbilities;

    // 当前绘制的 X 坐标位置
    float XPos;
    // 当前绘制的 Y 坐标位置
    float YPos;
    // 初始的 X 坐标位置
    float OriginalX;
    // 初始的 Y 坐标位置
    float OriginalY;
    // Y 坐标的最大值
    float MaxY;
    // 新列的 Y 方向填充值
    float NewColumnYPadding;
    // 用于跟踪 Y 坐标的临时变量
    float YL;

    // 是否累积调试信息
    bool Accumulate;
    // 存储调试信息字符串的数组
    TArray<FString> Strings;

    // 游戏自定义的标志,可在 Debug_Internal 函数中设置和读取
    int32 GameFlags; 
};

/**
 * 静态函数,用于在 HUD 上显示调试信息
 * @param HUD 显示调试信息的 HUD 对象
 * @param Canvas 用于绘制调试信息的画布
 * @param DisplayInfo 调试显示信息结构体
 * @param YL 用于跟踪 Y 坐标的引用变量
 * @param YPos 当前 Y 坐标的引用变量
 */
static void OnShowDebugInfo(AHUD* HUD, UCanvas* Canvas, const FDebugDisplayInfo& DisplayInfo, float& YL, float& YPos);

/**
 * 虚函数,用于在画布上显示调试信息
 * @param Canvas 用于绘制调试信息的画布
 * @param DebugDisplay 调试显示信息结构体
 * @param YL 用于跟踪 Y 坐标的引用变量
 * @param YPos 当前 Y 坐标的引用变量
 */
virtual void DisplayDebug(class UCanvas* Canvas, const class FDebugDisplayInfo& DebugDisplay, float& YL, float& YPos);

/**
 * 虚函数,用于打印调试信息到日志中
 */
virtual void PrintDebug();

/**
 * 累积屏幕位置信息
 * @param Info 能力系统组件调试信息结构体
 */
void AccumulateScreenPos(FAbilitySystemComponentDebugInfo& Info);

/**
 * 内部调试函数,可由子类重写以实现自定义调试逻辑
 * @param Info 能力系统组件调试信息结构体
 */
virtual void Debug_Internal(struct FAbilitySystemComponentDebugInfo& Info);

/**
 * 在调试信息中添加一行信息
 * @param Info 能力系统组件调试信息结构体
 * @param Str 要添加的调试信息字符串
 * @param XOffset X 方向的偏移量
 * @param YOffset Y 方向的偏移量
 * @param MinTextRowsToAdvance 最少要前进的文本行数
 */
void DebugLine(struct FAbilitySystemComponentDebugInfo& Info, FString Str, float XOffset, float YOffset, int32 MinTextRowsToAdvance = 0);

/**
 * 清理名称字符串,去除不必要的字符
 * @param Str 要清理的名称字符串
 * @return 清理后的名称字符串
 */
FString CleanupName(FString Str);

/**
 * 打印所有游戏玩法效果的调试列表
 */
void PrintAllGameplayEffects() const;

/**
 * 向服务器发送请求,要求服务器将能力系统的调试信息发送回客户端
 * 通过 ClientPrintDebug_Response 函数接收响应
 */
UFUNCTION(Server, reliable, WithValidation)
void ServerPrintDebug_Request();

/**
 * 与 ServerPrintDebug_Request 类似,但会将客户端的调试字符串包含在内
 * 以便服务器可以将其嵌入到回放中
 * @param Strings 客户端的调试字符串数组
 */
UFUNCTION(Server, reliable, WithValidation)
void ServerPrintDebug_RequestWithStrings(const TArray<FString>& Strings);

/**
 * 虚函数,当 ServerPrintDebug 系列函数在服务器上运行时,游戏可以重写此函数以执行自定义操作
 */
virtual void OnServerPrintDebug_Request();

/**
 * 确定是调用 ServerPrintDebug_Request 还是 ServerPrintDebug_RequestWithStrings
 * @return 如果应该发送客户端调试字符串到服务器,则返回 true;否则返回 false
 */
virtual bool ShouldSendClientDebugStringsToServer() const;

/**
 * 客户端函数,用于接收服务器发送的调试信息响应
 * @param Strings 服务器发送的调试信息字符串数组
 * @param GameFlags 游戏自定义的标志
 */
UFUNCTION(Client, reliable)
void ClientPrintDebug_Response(const TArray<FString>& Strings, int32 GameFlags);

/**
 * 虚函数,当接收到客户端调试信息响应时,可重写此函数以执行自定义操作
 * @param Strings 服务器发送的调试信息字符串数组
 * @param GameFlags 游戏自定义的标志
 */
virtual void OnClientPrintDebug_Response(const TArray<FString>& Strings, int32 GameFlags);

#if ENABLE_VISUAL_LOG
/**
 * 清除调试即时效果信息
 */
void ClearDebugInstantEffects();

/**
 * 重写函数,用于抓取调试快照
 * @param Snapshot 用于存储调试快照的可视化日志条目指针
 */
virtual void GrabDebugSnapshot(FVisualLogEntry* Snapshot) const override;
#endif // ENABLE_VISUAL_LOG

/**
 * 已弃用的属性,未来引擎版本将设为私有
 * 建议使用 SetClientDebugStrings、GetClientDebugStrings 或 GetClientDebugStrings_Mutable 代替
 * 存储客户端的调试字符串
 */
UE_DEPRECATED(4.26, "This will be made private in future engine versions. Use SetClientDebugStrings, GetClientDebugStrings, or GetClientDebugStrings_Mutable instead.")
UPROPERTY(ReplicatedUsing=OnRep_ClientDebugString)
TArray<FString> ClientDebugStrings;

/**
 * 设置客户端的调试字符串
 * @param NewClientDebugStrings 新的客户端调试字符串数组
 */
void SetClientDebugStrings(TArray<FString>&& NewClientDebugStrings);

/**
 * 获取可修改的客户端调试字符串数组
 * @return 可修改的客户端调试字符串数组引用
 */
TArray<FString>& GetClientDebugStrings_Mutable();

/**
 * 获取客户端调试字符串数组
 * @return 客户端调试字符串数组的常量引用
 */
const TArray<FString>& GetClientDebugStrings() const;

/**
 * 已弃用的属性,未来引擎版本将设为私有
 * 建议使用 SetServerDebugStrings、GetServerDebugStrings 或 GetServerDebugStrings_Mutable 代替
 * 存储服务器的调试字符串
 */
UE_DEPRECATED(4.26, "This will be made private in future engine versions. Use SetServerDebugStrings, GetServerDebugStrings, or GetServerDebugStrings_Mutable instead.")
UPROPERTY(ReplicatedUsing=OnRep_ServerDebugString)
TArray<FString> ServerDebugStrings;

/**
 * 设置服务器的调试字符串
 * @param NewServerDebugStrings 新的服务器调试字符串数组
 */
void SetServerDebugStrings(TArray<FString>&& NewServerDebugStrings);

/**
 * 获取可修改的服务器调试字符串数组
 * @return 可修改的服务器调试字符串数组引用
 */
TArray<FString>& GetServerDebugStrings_Mutable();

/**
 * 获取服务器调试字符串数组
 * @return 服务器调试字符串数组的常量引用
 */
const TArray<FString>& GetServerDebugStrings() const;

/**
 * 当客户端调试字符串属性复制时调用的函数
 */
UFUNCTION()
virtual void OnRep_ClientDebugString();

/**
 * 当服务器调试字符串属性复制时调用的函数
 */
UFUNCTION()
virtual void OnRep_ServerDebugString();

// ----------------------------------------------------------------------------------------------------------------

// 客户端到服务器的远程过程调用(RPC)批处理 // 这是一个正在开发中的功能,用于对客户端到服务器的通信进行批处理。该功能是可选的,且尚未完全完成。

//它仅对以下函数进行批处理。在批处理窗口期间,调用其他服务器 RPC 是不安全的。只有在你清楚自己在做什么的情况下才选择使用该功能!

// ----------------------------------------------------------------------------------------------------------------

// 调用服务器尝试激活技能
// @param AbilityToActivate 要激活的技能规格句柄
// @param InputPressed 输入是否被按下
// @param PredictionKey 预测键
void CallServerTryActivateAbility(FGameplayAbilitySpecHandle AbilityToActivate, bool InputPressed, FPredictionKey PredictionKey);

// 调用服务器设置复制的目标数据
// @param AbilityHandle 技能规格句柄
// @param AbilityOriginalPredictionKey 技能原始的预测键
// @param ReplicatedTargetDataHandle 复制的目标数据句柄
// @param ApplicationTag 应用标签
// @param CurrentPredictionKey 当前的预测键
void CallServerSetReplicatedTargetData(FGameplayAbilitySpecHandle AbilityHandle, FPredictionKey AbilityOriginalPredictionKey, const FGameplayAbilityTargetDataHandle& ReplicatedTargetDataHandle, FGameplayTag ApplicationTag, FPredictionKey CurrentPredictionKey);

// 调用服务器结束技能
// @param AbilityToEnd 要结束的技能规格句柄
// @param ActivationInfo 技能激活信息
// @param PredictionKey 预测键
void CallServerEndAbility(FGameplayAbilitySpecHandle AbilityToEnd, FGameplayAbilityActivationInfo ActivationInfo, FPredictionKey PredictionKey);

// 是否应该进行服务器技能远程过程调用(RPC)批处理
// @return 如果应该进行批处理则返回 true,否则返回 false
virtual bool ShouldDoServerAbilityRPCBatch() const { return false; }

// 开始服务器技能 RPC 批处理
// @param AbilityHandle 技能规格句柄
virtual void BeginServerAbilityRPCBatch(FGameplayAbilitySpecHandle AbilityHandle);

// 结束服务器技能 RPC 批处理
// @param AbilityHandle 技能规格句柄
virtual void EndServerAbilityRPCBatch(FGameplayAbilitySpecHandle AbilityHandle);

/** 在结束服务器技能 RPC 批处理时,将客户端累积的数据批量发送到服务器 */
TArray<FServerAbilityRPCBatch, TInlineAllocator<1> > LocalServerAbilityRPCBatchData;

// 服务器端的 RPC 函数,用于接收批量的技能 RPC 信息
// @param BatchInfo 批量的技能 RPC 信息
UFUNCTION(Server, reliable, WithValidation)
void    ServerAbilityRPCBatch(FServerAbilityRPCBatch BatchInfo);

// 可被子类重写的内部函数,用于处理服务器端的技能 RPC 批量信息
// @param BatchInfo 批量的技能 RPC 信息
virtual void ServerAbilityRPCBatch_Internal(FServerAbilityRPCBatch& BatchInfo);

// ----------------------------------------------------------------------------------------------------------------
// 输入处理/目标选择
// ----------------------------------------------------------------------------------------------------------------

/**
 * 此函数用于从输入角度抑制技能的激活。(例如,菜单被拉起,另一个游戏机制正在占用所有输入等)
 * 此函数应仅在本地拥有的玩家上调用。
 * 此函数不应用于游戏机制,如沉默或禁用。这些应通过游戏玩法效果来实现。
 * @return 如果用户技能激活被抑制则返回 true,否则返回 false
 */
UFUNCTION(BlueprintCallable, Category="Abilities")
bool GetUserAbilityActivationInhibited() const;

/**
 * 禁用或启用本地用户激活技能的能力。此函数仅应用于与输入/用户界面等相关的抑制。请勿用于游戏机制。
 * @param NewInhibit 如果为 true,则抑制用户激活技能;如果为 false,则允许用户激活技能
 */
UFUNCTION(BlueprintCallable, Category="Abilities")
virtual void SetUserAbilityActivationInhibited(bool NewInhibit);

/** 用户技能激活当前是否被抑制 */
UPROPERTY()
bool UserAbilityActivationInhibited;

/** 启用后,游戏玩法提示(GameplayCue)的远程过程调用(RPC)将通过化身角色(AvatarActor)的 IAbilitySystemReplicationProxyInterface 进行路由,而不是通过此组件 */
UPROPERTY()
bool ReplicationProxyEnabled;

/** 抑制此组件上通过游戏玩法效果(GE)授予的所有技能 */
UPROPERTY()
bool bSuppressGrantAbility;

/** 抑制此组件上的所有游戏玩法提示(GameplayCue) */
UPROPERTY()
bool bSuppressGameplayCues;

/** 当前活动的目标选择演员列表 */
UPROPERTY()
TArray<TObjectPtr<AGameplayAbilityTargetActor>> SpawnedTargetActors;

/**
 * 使用一些默认的动作名称绑定到输入组件
 * @param InputComponent 要绑定的输入组件
 */
virtual void BindToInputComponent(UInputComponent* InputComponent);

/**
 * 使用自定义绑定绑定到输入组件
 * @param InputComponent 要绑定的输入组件
 * @param BindInfo 技能输入绑定信息
 */
virtual void BindAbilityActivationToInputComponent(UInputComponent* InputComponent, FGameplayAbilityInputBinds BindInfo);

/**
 * 初始化被阻止的技能绑定变量
 * @param BindInfo 技能输入绑定信息
 */
virtual void SetBlockAbilityBindingsArray(FGameplayAbilityInputBinds BindInfo);

/**
 * 处理技能绑定输入按下事件
 * @param InputID 输入 ID
 */
virtual void AbilityLocalInputPressed(int32 InputID);

/**
 * 处理技能绑定输入释放事件
 * @param InputID 输入 ID
 */
virtual void AbilityLocalInputReleased(int32 InputID);

/*
 * 发送一个本地玩家输入按下事件,使用提供的输入 ID,通知任何绑定的技能
 *
 * @param InputID 要匹配的输入 ID
 */
UFUNCTION(BlueprintCallable, Category = "Gameplay Abilities")
void PressInputID(int32 InputID);

/**
 * 发送一个本地玩家输入释放事件,使用提供的输入 ID,通知任何绑定的技能
 * @param InputID 要匹配的输入 ID
 */
UFUNCTION(BlueprintCallable, Category = "Gameplay Abilities")
void ReleaseInputID(int32 InputID);

/**
 * 处理目标选择演员的确认/取消输入
 */
virtual void LocalInputConfirm();
virtual void LocalInputCancel();

/**
 * 发送一个本地玩家输入确认事件,通知技能
 */
UFUNCTION(BlueprintCallable, Category = "Gameplay Abilities")
void InputConfirm();

/**
 * 发送一个本地玩家输入取消事件,通知技能
 */
UFUNCTION(BlueprintCallable, Category = "Gameplay Abilities")
void InputCancel();

/** 用于绑定通用确认/取消事件的输入 ID */
int32 GenericConfirmInputID;
int32 GenericCancelInputID;

/**
 * 检查输入 ID 是否绑定到通用确认输入,并且通用本地确认回调已绑定
 * @param InputID 要检查的输入 ID
 * @return 如果输入 ID 绑定到通用确认输入且回调已绑定,则返回 true,否则返回 false
 */
bool IsGenericConfirmInputBound(int32 InputID) const    { return ((InputID == GenericConfirmInputID) && GenericLocalConfirmCallbacks.IsBound()); }

/**
 * 检查输入 ID 是否绑定到通用取消输入,并且通用本地取消回调已绑定
 * @param InputID 要检查的输入 ID
 * @return 如果输入 ID 绑定到通用取消输入且回调已绑定,则返回 true,否则返回 false
 */
bool IsGenericCancelInputBound(int32 InputID) const        { return ((InputID == GenericCancelInputID) && GenericLocalCancelCallbacks.IsBound()); }

/** 任何技能都可以监听的通用本地确认事件回调 */
FAbilityConfirmOrCancel    GenericLocalConfirmCallbacks;

/** 任何技能都可以监听的通用本地取消事件回调 */
FAbilityConfirmOrCancel    GenericLocalCancelCallbacks;

/**
 * 通知任何活动的目标选择演员停止并返回当前的目标选择数据
 */
UFUNCTION(BlueprintCallable, Category = "Abilities")
virtual void TargetConfirm();

/**
 * 停止并取消任何活动的目标选择演员,不返回任何目标选择数据
 */
UFUNCTION(BlueprintCallable, Category = "Abilities")
virtual void TargetCancel();

// ----------------------------------------------------------------------------------------------------------------
// AnimMontage Support
// ----------------------------------------------------------------------------------------------------------------
/**
 * 播放一个蒙太奇(动画蒙太奇),并根据传入的技能和激活信息处理复制和预测逻辑。
 * @param AnimatingAbility 正在播放动画的技能对象
 * @param ActivationInfo 技能激活信息
 * @param Montage 要播放的动画蒙太奇
 * @param InPlayRate 播放速率
 * @param StartSectionName 起始片段名称,默认为无
 * @param StartTimeSeconds 起始时间(秒),默认为 0.0
 * @return 蒙太奇的播放时长
 */
virtual float PlayMontage(UGameplayAbility* AnimatingAbility, FGameplayAbilityActivationInfo ActivationInfo, UAnimMontage* Montage, float InPlayRate, FName StartSectionName = NAME_None, float StartTimeSeconds = 0.0f);

/**
 * 以动态蒙太奇的方式在指定插槽播放动画序列。
 * @param AnimatingAbility 正在播放动画的技能对象
 * @param ActivationInfo 技能激活信息
 * @param AnimAsset 动画序列资产
 * @param SlotName 动画插槽名称
 * @param BlendInTime 淡入时间
 * @param BlendOutTime 淡出时间
 * @param InPlayRate 播放速率,默认为 1.0
 * @param StartTimeSeconds 起始时间(秒),默认为 0.0
 * @return 播放的动画蒙太奇对象
 */
virtual UAnimMontage* PlaySlotAnimationAsDynamicMontage(UGameplayAbility* AnimatingAbility, FGameplayAbilityActivationInfo ActivationInfo, UAnimSequenceBase* AnimAsset, FName SlotName, float BlendInTime, float BlendOutTime, float InPlayRate = 1.f, float StartTimeSeconds = 0.0f);

/**
 * 播放蒙太奇但不更新复制/预测结构。用于模拟代理在接收到复制指令时播放蒙太奇。
 * @param Montage 要播放的动画蒙太奇
 * @param InPlayRate 播放速率
 * @param StartSectionName 起始片段名称,默认为无
 * @return 蒙太奇的播放时长
 */
virtual float PlayMontageSimulated(UAnimMontage* Montage, float InPlayRate, FName StartSectionName = NAME_None);

/**
 * 以模拟方式在指定插槽以动态蒙太奇的方式播放动画序列。
 * @param AnimAsset 动画序列资产
 * @param SlotName 动画插槽名称
 * @param BlendInTime 淡入时间
 * @param BlendOutTime 淡出时间
 * @param InPlayRate 播放速率,默认为 1.0
 * @return 播放的动画蒙太奇对象
 */
virtual UAnimMontage* PlaySlotAnimationAsDynamicMontageSimulated(UAnimSequenceBase* AnimAsset, FName SlotName, float BlendInTime, float BlendOutTime, float InPlayRate = 1.f);

/**
 * 停止当前正在播放的蒙太奇。调用者应该是当前正在播放动画的技能对象(或者有合理理由不进行检查)。
 * @param OverrideBlendOutTime 覆盖的淡出时间,默认为 -1.0f 表示使用蒙太奇默认的淡出时间
 */
virtual void CurrentMontageStop(float OverrideBlendOutTime = -1.0f);

/**
 * 如果当前播放的蒙太奇是传入的蒙太奇,则停止它。
 * @param Montage 要检查并停止的蒙太奇
 * @param OverrideBlendOutTime 覆盖的淡出时间,默认为 -1.0f 表示使用蒙太奇默认的淡出时间
 */
virtual void StopMontageIfCurrent(const UAnimMontage& Montage, float OverrideBlendOutTime = -1.0f);

/**
 * 如果传入的技能仍然是当前正在播放动画的技能,则清除它。
 * @param Ability 要清除的技能对象
 */
virtual void ClearAnimatingAbility(UGameplayAbility* Ability);

/**
 * 将当前播放的蒙太奇跳转到指定片段。调用者应该是当前正在播放动画的技能对象(或者有合理理由不进行检查)。
 * @param SectionName 要跳转的片段名称
 */
virtual void CurrentMontageJumpToSection(FName SectionName);

/**
 * 设置当前蒙太奇的下一个片段名称。调用者应该是当前正在播放动画的技能对象(或者有合理理由不进行检查)。
 * @param FromSectionName 当前所在的片段名称
 * @param ToSectionName 要设置的下一个片段名称
 */
virtual void CurrentMontageSetNextSectionName(FName FromSectionName, FName ToSectionName);

/**
 * 设置当前蒙太奇的播放速率。
 * @param InPlayRate 要设置的播放速率
 */
virtual void CurrentMontageSetPlayRate(float InPlayRate);

/**
 * 检查传入的技能是否是当前正在播放动画的技能。
 * @param Ability 要检查的技能对象
 * @return 如果是当前正在播放动画的技能则返回 true,否则返回 false
 */
bool IsAnimatingAbility(UGameplayAbility* Ability) const;

/**
 * 获取当前正在播放动画的技能对象。
 * @return 当前正在播放动画的技能对象指针
 */
UGameplayAbility* GetAnimatingAbility();

/**
 * 获取当前正在播放的蒙太奇对象。
 * @return 当前正在播放的蒙太奇对象指针
 */
UAnimMontage* GetCurrentMontage() const;

/**
 * 获取当前正在播放的动画蒙太奇的片段 ID。
 * @return 当前片段的 ID
 */
int32 GetCurrentMontageSectionID() const;

/**
 * 获取当前正在播放的动画蒙太奇的片段名称。
 * @return 当前片段的名称
 */
FName GetCurrentMontageSectionName() const;

/**
 * 获取当前片段的时长。
 * @return 当前片段的时长(秒)
 */
float GetCurrentMontageSectionLength() const;

/**
 * 获取当前片段剩余的播放时间。
 * @return 当前片段剩余的播放时间(秒)
 */
float GetCurrentMontageSectionTimeLeft() const;

/**
 * 设置蒙太奇中位置的复制方法。
 * @param InMethod 要设置的复制方法
 */
void SetMontageRepAnimPositionMethod(ERepAnimPositionMethod InMethod);

// ----------------------------------------------------------------------------------------------------------------
// Actor interaction 都很重要 但是我不太明白GAS的avatar和owner的区别
// ----------------------------------------------------------------------------------------------------------------
private:

    /** 逻辑上拥有此组件的演员 */
    UPROPERTY(ReplicatedUsing = OnRep_OwningActor)
    TObjectPtr<AActor> OwnerActor;

    /** 用于技能的物理表现的演员。可以为空 */
    UPROPERTY(ReplicatedUsing = OnRep_OwningActor)
    TObjectPtr<AActor> AvatarActor;

public:

    /** 设置拥有者演员 */
    void SetOwnerActor(AActor* NewOwnerActor);

    /** 获取拥有者演员 */
    AActor* GetOwnerActor() const { return OwnerActor; }

    /** 直接设置化身演员 */
    void SetAvatarActor_Direct(AActor* NewAvatarActor);

    /** 直接获取化身演员 */
    AActor* GetAvatarActor_Direct() const { return AvatarActor; }

    /** 当拥有者演员属性复制时调用 */
    UFUNCTION()
    void OnRep_OwningActor();

    /** 当化身演员被销毁时调用 */
    UFUNCTION()
    void OnAvatarActorDestroyed(AActor* InActor);

    /** 当拥有者演员被销毁时调用 */
    UFUNCTION()
    void OnOwnerActorDestroyed(AActor* InActor);

    /** 当生成的属性结束播放时调用 */
    UFUNCTION()
    void OnSpawnedAttributesEndPlayed(AActor* InActor, EEndPlayReason::Type EndPlayReason);

    /** 缓存拥有者演员的相关数据,技能需要频繁访问这些数据(移动组件、网格组件、动画实例等) */
    TSharedPtr<FGameplayAbilityActorInfo> AbilityActorInfo;

    /**
     * 初始化技能的演员信息 - 该结构保存了我们作用的对象以及控制我们的对象的信息。
     *      OwnerActor 是逻辑上拥有此组件的演员。
     *      AvatarActor 是我们在世界中作用的物理演员。通常是一个 pawn,但也可以是一座塔、建筑物、炮塔等,可能与 Owner 相同。
     */
    virtual void InitAbilityActorInfo(AActor* InOwnerActor, AActor* InAvatarActor);

    /** 返回用于特定任务的化身演员,通常是 GetAvatarActor */
    virtual AActor* GetGameplayTaskAvatar(const UGameplayTask* Task) const override;

    /** 返回此组件的化身演员 */
    AActor* GetAvatarActor() const;

    /** 更改化身演员,保持拥有者演员不变 */
    void SetAvatarActor(AActor* InAvatarActor);

    /** 当 ASC 的 AbilityActorInfo 设置了玩家控制器时调用 */
    virtual void OnPlayerControllerSet() { }

    /**
     * 当初始化到此系统的演员死亡时调用,这将从该系统和 FGameplayAbilityActorInfo 中清除该演员
     */
    virtual void ClearActorInfo();

    /**
     * 根据当前的 ActorInfo 刷新技能的 ActorInfo 结构。也就是说,AvatarActor 保持不变,但我们会查找新的
     * 动画实例、移动组件、玩家控制器等。
     */
    void RefreshAbilityActorInfo();

// ----------------------------------------------------------------------------------------------------------------

// 同步远程过程调用(RPC)

// 虽然这些看起来像是状态,但实际上它们是带有一些有效负载数据的同步事件

// ----------------------------------------------------------------------------------------------------------------

/**
 * 将通用复制事件复制到服务器。
 * @param EventType 通用复制事件的类型
 * @param AbilityHandle 技能规格句柄
 * @param AbilityOriginalPredictionKey 技能的原始预测键
 * @param CurrentPredictionKey 当前的预测键
 */
UFUNCTION(Server, reliable, WithValidation)
void ServerSetReplicatedEvent(EAbilityGenericReplicatedEvent::Type EventType, FGameplayAbilitySpecHandle AbilityHandle, FPredictionKey AbilityOriginalPredictionKey, FPredictionKey CurrentPredictionKey);

/**
 * 将带有有效负载的通用复制事件复制到服务器。
 * @param EventType 通用复制事件的类型
 * @param AbilityHandle 技能规格句柄
 * @param AbilityOriginalPredictionKey 技能的原始预测键
 * @param CurrentPredictionKey 当前的预测键
 * @param VectorPayload 向量有效负载
 */
UFUNCTION(Server, reliable, WithValidation)
void ServerSetReplicatedEventWithPayload(EAbilityGenericReplicatedEvent::Type EventType, FGameplayAbilitySpecHandle AbilityHandle, FPredictionKey AbilityOriginalPredictionKey, FPredictionKey CurrentPredictionKey, FVector_NetQuantize100 VectorPayload);

/**
 * 将通用复制事件复制到客户端。
 * @param EventType 通用复制事件的类型
 * @param AbilityHandle 技能规格句柄
 * @param AbilityOriginalPredictionKey 技能的原始预测键
 */
UFUNCTION(Client, reliable)
void ClientSetReplicatedEvent(EAbilityGenericReplicatedEvent::Type EventType, FGameplayAbilitySpecHandle AbilityHandle, FPredictionKey AbilityOriginalPredictionKey);

/**
 * 调用已注册到给定通用复制事件的本地回调函数。
 * @param EventType 通用复制事件的类型
 * @param AbilityHandle 技能规格句柄
 * @param AbilityOriginalPredictionKey 技能的原始预测键
 * @param CurrentPredictionKey 当前的预测键,默认为空
 * @return 如果成功调用回调函数则返回 true,否则返回 false
 */
bool InvokeReplicatedEvent(EAbilityGenericReplicatedEvent::Type EventType, FGameplayAbilitySpecHandle AbilityHandle, FPredictionKey AbilityOriginalPredictionKey, FPredictionKey CurrentPredictionKey = FPredictionKey());

/**
 * 调用已注册到给定带有有效负载的通用复制事件的本地回调函数。
 * @param EventType 通用复制事件的类型
 * @param AbilityHandle 技能规格句柄
 * @param AbilityOriginalPredictionKey 技能的原始预测键
 * @param CurrentPredictionKey 当前的预测键
 * @param VectorPayload 向量有效负载
 * @return 如果成功调用回调函数则返回 true,否则返回 false
 */
bool InvokeReplicatedEventWithPayload(EAbilityGenericReplicatedEvent::Type EventType, FGameplayAbilitySpecHandle AbilityHandle, FPredictionKey AbilityOriginalPredictionKey, FPredictionKey CurrentPredictionKey, FVector_NetQuantize100 VectorPayload);

/**
 * 将目标数据复制到服务器。
 * @param AbilityHandle 技能规格句柄
 * @param AbilityOriginalPredictionKey 技能的原始预测键
 * @param ReplicatedTargetDataHandle 复制的目标数据句柄
 * @param ApplicationTag 应用标签
 * @param CurrentPredictionKey 当前的预测键
 */
UFUNCTION(Server, reliable, WithValidation)
void ServerSetReplicatedTargetData(FGameplayAbilitySpecHandle AbilityHandle, FPredictionKey AbilityOriginalPredictionKey, const FGameplayAbilityTargetDataHandle& ReplicatedTargetDataHandle, FGameplayTag ApplicationTag, FPredictionKey CurrentPredictionKey);

/**
 * 向服务器复制目标选择已取消的信息。
 * @param AbilityHandle 技能规格句柄
 * @param AbilityOriginalPredictionKey 技能的原始预测键
 * @param CurrentPredictionKey 当前的预测键
 */
UFUNCTION(Server, reliable, WithValidation)
void ServerSetReplicatedTargetDataCancelled(FGameplayAbilitySpecHandle AbilityHandle, FPredictionKey AbilityOriginalPredictionKey, FPredictionKey CurrentPredictionKey);

/**
 * 设置当前目标数据并调用适用的回调函数。
 * @param AbilityHandle 技能规格句柄
 * @param AbilityOriginalPredictionKey 技能的原始预测键
 * @param TargetData 目标数据句柄
 * @param ApplicationTag 应用标签
 */
virtual void ConfirmAbilityTargetData(FGameplayAbilitySpecHandle AbilityHandle, FPredictionKey AbilityOriginalPredictionKey, const FGameplayAbilityTargetDataHandle& TargetData, const FGameplayTag& ApplicationTag);

/**
 * 取消技能目标数据并调用回调函数。
 * @param AbilityHandle 技能规格句柄
 * @param AbilityOriginalPredictionKey 技能的原始预测键
 */
virtual void CancelAbilityTargetData(FGameplayAbilitySpecHandle AbilityHandle, FPredictionKey AbilityOriginalPredictionKey);

/**
 * 删除所有缓存的技能客户端数据(以前称为 ConsumeAbilityTargetData)。
 * @param AbilityHandle 技能规格句柄
 * @param AbilityOriginalPredictionKey 技能的原始预测键
 */
void ConsumeAllReplicatedData(FGameplayAbilitySpecHandle AbilityHandle, FPredictionKey AbilityOriginalPredictionKey);

/**
 * 消耗客户端缓存的目标数据(仅目标数据)。
 * @param AbilityHandle 技能规格句柄
 * @param AbilityOriginalPredictionKey 技能的原始预测键
 */
void ConsumeClientReplicatedTargetData(FGameplayAbilitySpecHandle AbilityHandle, FPredictionKey AbilityOriginalPredictionKey);

/**
 * 消耗给定的通用复制事件(取消设置)。
 * @param EventType 通用复制事件的类型
 * @param AbilityHandle 技能规格句柄
 * @param AbilityOriginalPredictionKey 技能的原始预测键
 */
void ConsumeGenericReplicatedEvent(EAbilityGenericReplicatedEvent::Type EventType, FGameplayAbilitySpecHandle AbilityHandle, FPredictionKey AbilityOriginalPredictionKey);

/**
 * 获取给定通用复制事件的复制数据。
 * @param EventType 通用复制事件的类型
 * @param AbilityHandle 技能规格句柄
 * @param AbilityOriginalPredictionKey 技能的原始预测键
 * @return 通用复制事件的复制数据
 */
FAbilityReplicatedData GetReplicatedDataOfGenericReplicatedEvent(EAbilityGenericReplicatedEvent::Type EventType, FGameplayAbilitySpecHandle AbilityHandle, FPredictionKey AbilityOriginalPredictionKey);

/**
 * 调用所有已发送的复制委托(目标数据或通用复制事件)。注意,如果技能中的多个地方注册了事件然后调用此函数,可能会有危险。
 * @param AbilityHandle 技能规格句柄
 * @param AbilityOriginalPredictionKey 技能的原始预测键
 */
void CallAllReplicatedDelegatesIfSet(FGameplayAbilitySpecHandle AbilityHandle, FPredictionKey AbilityOriginalPredictionKey);

/**
 * 如果目标数据确认/取消事件已发送,则调用这些事件的委托。
 * @param AbilityHandle 技能规格句柄
 * @param AbilityOriginalPredictionKey 技能的原始预测键
 * @return 如果成功调用委托则返回 true,否则返回 false
 */
bool CallReplicatedTargetDataDelegatesIfSet(FGameplayAbilitySpecHandle AbilityHandle, FPredictionKey AbilityOriginalPredictionKey);

/**
 * 如果给定的通用复制事件已经发送,则调用其委托。
 * @param EventType 通用复制事件的类型
 * @param AbilityHandle 技能规格句柄
 * @param AbilityOriginalPredictionKey 技能的原始预测键
 * @return 如果成功调用委托则返回 true,否则返回 false
 */
bool CallReplicatedEventDelegateIfSet(EAbilityGenericReplicatedEvent::Type EventType, FGameplayAbilitySpecHandle AbilityHandle, FPredictionKey AbilityOriginalPredictionKey);

/**
 * 如果客户端事件已经发送,则调用传入的委托。如果没有发送,则将委托添加到多播回调中,当事件发送时会触发该回调。
 * @param EventType 通用复制事件的类型
 * @param AbilityHandle 技能规格句柄
 * @param AbilityOriginalPredictionKey 技能的原始预测键
 * @param Delegate 要调用或添加的委托
 * @return 如果事件已经发送并调用了委托则返回 true,否则返回 false
 */
bool CallOrAddReplicatedDelegate(EAbilityGenericReplicatedEvent::Type EventType, FGameplayAbilitySpecHandle AbilityHandle, FPredictionKey AbilityOriginalPredictionKey, FSimpleMulticastDelegate::FDelegate Delegate);

/**
 * 返回给定技能/预测键对的目标数据设置委托。
 * @param AbilityHandle 技能规格句柄
 * @param AbilityOriginalPredictionKey 技能的原始预测键
 * @return 目标数据设置委托的引用
 */
FAbilityTargetDataSetDelegate& AbilityTargetDataSetDelegate(FGameplayAbilitySpecHandle AbilityHandle, FPredictionKey AbilityOriginalPredictionKey);

/**
 * 返回给定技能/预测键对的目标数据取消委托。
 * @param AbilityHandle 技能规格句柄
 * @param AbilityOriginalPredictionKey 技能的原始预测键
 * @return 目标数据取消委托的引用
 */
FSimpleMulticastDelegate& AbilityTargetDataCancelledDelegate(FGameplayAbilitySpecHandle AbilityHandle, FPredictionKey AbilityOriginalPredictionKey);

/**
 * 返回给定技能/预测键对的通用复制事件委托。
 * @param EventType 通用复制事件的类型
 * @param AbilityHandle 技能规格句柄
 * @param AbilityOriginalPredictionKey 技能的原始预测键
 * @return 通用复制事件委托的引用
 */
FSimpleMulticastDelegate& AbilityReplicatedEventDelegate(EAbilityGenericReplicatedEvent::Type EventType, FGameplayAbilitySpecHandle AbilityHandle, FPredictionKey AbilityOriginalPredictionKey);

/**
 * 直接输入状态复制。如果技能上的 bReplicateInputDirectly 为 true,则会调用这些函数,通常不建议使用。(建议使用通用复制事件)
 * @param AbilityHandle 技能规格句柄
 */
UFUNCTION(Server, reliable, WithValidation)
void ServerSetInputPressed(FGameplayAbilitySpecHandle AbilityHandle);

/**
 * 直接输入状态复制。如果技能上的 bReplicateInputDirectly 为 true,则会调用这些函数,通常不建议使用。(建议使用通用复制事件)
 * @param AbilityHandle 技能规格句柄
 */
UFUNCTION(Server, reliable, WithValidation)
void ServerSetInputReleased(FGameplayAbilitySpecHandle AbilityHandle);

/**
 * 始终在本地玩家上调用。只有当 GameplayAbility 上的 bReplicateInputDirectly 被设置时,才会在服务器上调用。
 * @param Spec 技能规格
 */
virtual void AbilitySpecInputPressed(FGameplayAbilitySpec& Spec);

/**
 * 始终在本地玩家上调用。只有当 GameplayAbility 上的 bReplicateInputDirectly 被设置时,才会在服务器上调用。
 * @param Spec 技能规格
 */
virtual void AbilitySpecInputReleased(FGameplayAbilitySpec& Spec);
// ----------------------------------------------------------------------------------------------------------------
// Component overrides
// ----------------------------------------------------------------------------------------------------------------
    virtual void InitializeComponent() override;
    virtual void UninitializeComponent() override;
    virtual void OnComponentDestroyed(bool bDestroyingHierarchy) override;
    virtual bool GetShouldTick() const override;
    virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction) override;
    virtual void GetSubobjectsWithStableNamesForNetworking(TArray<UObject*>& Objs) override;
    virtual bool ReplicateSubobjects(class UActorChannel *Channel, class FOutBunch *Bunch, FReplicationFlags *RepFlags) override;
    /** Force owning actor to update it's replication, to make sure that gameplay cues get sent down quickly. Override to change how aggressive this is */
    virtual void ForceReplication() override;
    virtual void PreNetReceive() override;
    virtual void PostNetReceive() override;
    virtual void OnRegister() override;
    virtual void OnUnregister() override;
    virtual void ReadyForReplication() override;
    virtual void BeginPlay() override;

protected:
    virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
    virtual void GetReplicatedCustomConditionState(FCustomPropertyConditionState& OutActiveState) const override;

    void UpdateActiveGameplayEffectsReplicationCondition();
    void UpdateMinimalReplicationGameplayCuesCondition();
override的部分components
/**
 * 我们可以激活的技能。
 *  - 该数组将包含非实例化技能的类默认对象(CDO)以及每次执行时实例化的技能。
 *  - 角色实例化技能将是实际的实例对象(而非类默认对象)。
 *
 * 这个数组并非系统正常运行的关键要素。它只是为了方便“赋予角色技能”而存在。不过,即使没有技能系统组件,技能也可以在其他对象上发挥作用。
 * 例如,一个技能可以被编写为在静态网格体角色上执行。只要该技能不依赖于技能系统组件提供的实例化或其他功能,那么它的运行就不需要这个组件。
 */
UPROPERTY(ReplicatedUsing = OnRep_ActivateAbilities, BlueprintReadOnly, Transient, Category = "Abilities")
FGameplayAbilitySpecContainer ActivatableAbilities;
/**
 * 将技能规格映射到目标数据。用于跟踪复制的数据和回调
 */
FGameplayAbilityReplicatedDataContainer AbilityTargetDataMap;

/**
 * 游戏玩法标签容器过滤器列表,以及它们调用的委托
 */
TArray<TPair<FGameplayTagContainer, FGameplayEventTagMulticastDelegate>> GameplayEventTagContainerDelegates;

/**
 * 与此组件关联的所有每次执行都实例化的游戏玩法技能的完整列表
 * 注意:自 5.1 版本起已弃用,此数组将设为私有。请使用 GetReplicatedInstancedAbilities、AddReplicatedInstancedAbility 或 RemoveReplicatedInstancedAbility 代替
 */
UE_DEPRECATED(5.1, "This array will be made private. Use GetReplicatedInstancedAbilities, AddReplicatedInstancedAbility or RemoveReplicatedInstancedAbility instead.")
UPROPERTY()
TArray<TObjectPtr<UGameplayAbility>> AllReplicatedInstancedAbilities;

/**
 * 获取与此组件关联的所有每次执行都实例化的游戏玩法技能的完整列表
 * 注意:使用此方法可避免直接访问已弃用的 AllReplicatedInstancedAbilities 数组
 */
PRAGMA_DISABLE_DEPRECATION_WARNINGS
const TArray<UGameplayAbility*>& GetReplicatedInstancedAbilities() const { return AllReplicatedInstancedAbilities; }
PRAGMA_ENABLE_DEPRECATION_WARNINGS

/**
 * 向此组件添加一个游戏玩法技能
 * @param GameplayAbility 要添加的游戏玩法技能
 */
void AddReplicatedInstancedAbility(UGameplayAbility* GameplayAbility);

/**
 * 从此组件移除一个游戏玩法技能
 * @param GameplayAbility 要移除的游戏玩法技能
 */
void RemoveReplicatedInstancedAbility(UGameplayAbility* GameplayAbility);

/**
 * 注销此组件的所有游戏玩法技能
 */
void RemoveAllReplicatedInstancedAbilities();

/**
 * 会在 GiveAbility 或 OnRep 时调用。使用给定的技能初始化事件(触发器和输入)
 * @param AbilitySpec 要处理的技能规格
 */
virtual void OnGiveAbility(FGameplayAbilitySpec& AbilitySpec);

/**
 * 会在 RemoveAbility 或 OnRep 时调用。取消给定技能的输入绑定
 * @param AbilitySpec 要处理的技能规格
 */
virtual void OnRemoveAbility(FGameplayAbilitySpec& AbilitySpec);

/**
 * 从 ClearAbility、ClearAllAbilities 或 OnRep 调用。清除任何不应再存在的触发器
 */
void CheckForClearedAbilities();

/**
 * 取消特定的技能规格
 * @param Spec 要取消的技能规格
 * @param Ignore 要忽略的游戏玩法技能
 */
virtual void CancelAbilitySpec(FGameplayAbilitySpec& Spec, UGameplayAbility* Ignore);

/**
 * 创建一个技能的新实例,并将其存储在规格中
 * @param Spec 技能规格
 * @param Ability 游戏玩法技能
 * @return 新创建的游戏玩法技能实例
 */
virtual UGameplayAbility* CreateNewInstanceOfAbility(FGameplayAbilitySpec& Spec, const UGameplayAbility* Ability);

/**
 * 表示我们处于 ABILITY_SCOPE_LOCK() 的层数。当 AbilityScopeLockCount > 0 时,技能列表可能无法修改
 */
int32 AbilityScopeLockCount;

/**
 * 当退出当前技能作用域锁时将被移除的技能
 */
TArray<FGameplayAbilitySpecHandle, TInlineAllocator<2> > AbilityPendingRemoves;

/**
 * 当退出当前技能作用域锁时将被添加的技能
 */
TArray<FGameplayAbilitySpec, TInlineAllocator<2> > AbilityPendingAdds;

/**
 * 退出当前技能作用域锁时是否应移除所有技能。此设置优先级高于待添加的技能
 */
bool bAbilityPendingClearAll;

/**
 * 上次技能激活的本地世界时间。用于检测玩家是否处于闲置状态
 */
float AbilityLastActivatedTime;

/**
 * 当可激活技能列表复制时调用
 */
UFUNCTION()
virtual void OnRep_ActivateAbilities();

/**
 * 服务器端函数,尝试激活指定的技能
 * @param AbilityToActivate 要激活的技能规格句柄
 * @param InputPressed 输入是否被按下
 * @param PredictionKey 预测键
 */
UFUNCTION(Server, reliable, WithValidation)
void ServerTryActivateAbility(FGameplayAbilitySpecHandle AbilityToActivate, bool InputPressed, FPredictionKey PredictionKey);

/**
 * 服务器端函数,尝试激活指定的技能并附带事件数据
 * @param AbilityToActivate 要激活的技能规格句柄
 * @param InputPressed 输入是否被按下
 * @param PredictionKey 预测键
 * @param TriggerEventData 触发事件数据
 */
UFUNCTION(Server, reliable, WithValidation)
void ServerTryActivateAbilityWithEventData(FGameplayAbilitySpecHandle AbilityToActivate, bool InputPressed, FPredictionKey PredictionKey, FGameplayEventData TriggerEventData);

/**
 * 客户端函数,尝试激活指定的技能
 * @param AbilityToActivate 要激活的技能规格句柄
 */
UFUNCTION(Client, reliable)
void ClientTryActivateAbility(FGameplayAbilitySpecHandle AbilityToActivate);
/** 上一次技能激活的本地世界时间。这用于检测玩家是否处于挂机 / 闲置状态。 */

    /** Local World time of the last ability activation. This is used for AFK/idle detection */
    float AbilityLastActivatedTime;

    UFUNCTION()
    virtual void OnRep_ActivateAbilities();

    UFUNCTION(Server, reliable, WithValidation)
    void    ServerTryActivateAbility(FGameplayAbilitySpecHandle AbilityToActivate, bool InputPressed, FPredictionKey PredictionKey);

    UFUNCTION(Server, reliable, WithValidation)
    void    ServerTryActivateAbilityWithEventData(FGameplayAbilitySpecHandle AbilityToActivate, bool InputPressed, FPredictionKey PredictionKey, FGameplayEventData TriggerEventData);

    UFUNCTION(Client, reliable)
    void    ClientTryActivateAbility(FGameplayAbilitySpecHandle AbilityToActivate);

    /** Called by ServerEndAbility and ClientEndAbility; avoids code duplication. */
    void    RemoteEndOrCancelAbility(FGameplayAbilitySpecHandle AbilityToEnd, FGameplayAbilityActivationInfo ActivationInfo, bool bWasCanceled);

    UFUNCTION(Server, reliable, WithValidation)
    void    ServerEndAbility(FGameplayAbilitySpecHandle AbilityToEnd, FGameplayAbilityActivationInfo ActivationInfo, FPredictionKey PredictionKey);

    UFUNCTION(Client, reliable)
    void    ClientEndAbility(FGameplayAbilitySpecHandle AbilityToEnd, FGameplayAbilityActivationInfo ActivationInfo);

    UFUNCTION(Server, reliable, WithValidation)
    void    ServerCancelAbility(FGameplayAbilitySpecHandle AbilityToCancel, FGameplayAbilityActivationInfo ActivationInfo);

    UFUNCTION(Client, reliable)
    void    ClientCancelAbility(FGameplayAbilitySpecHandle AbilityToCancel, FGameplayAbilityActivationInfo ActivationInfo);

    UFUNCTION(Client, Reliable)
    void    ClientActivateAbilityFailed(FGameplayAbilitySpecHandle AbilityToActivate, int16 PredictionKey);
    int32    ClientActivateAbilityFailedCountRecent;
    float    ClientActivateAbilityFailedStartTime;

    
    void    OnClientActivateAbilityCaughtUp(FGameplayAbilitySpecHandle AbilityToActivate, FPredictionKey::KeyType PredictionKey);

    UFUNCTION(Client, Reliable)
    void    ClientActivateAbilitySucceed(FGameplayAbilitySpecHandle AbilityToActivate, FPredictionKey PredictionKey);

    UFUNCTION(Client, Reliable)
    void    ClientActivateAbilitySucceedWithEventData(FGameplayAbilitySpecHandle AbilityToActivate, FPredictionKey PredictionKey, FGameplayEventData TriggerEventData);

 

/**
 * ServerTryActivateAbility 函数的具体实现
 * @param AbilityToActivate 要激活的技能规格句柄
 * @param InputPressed 输入是否被按下
 * @param PredictionKey 预测键
 * @param TriggerEventData 触发事件数据指针
 */
virtual void InternalServerTryActivateAbility(FGameplayAbilitySpecHandle AbilityToActivate, bool InputPressed, const FPredictionKey& PredictionKey, const FGameplayEventData* TriggerEventData);

/**
 * 当播放蒙太奇的预测键被拒绝时调用
 * @param PredictiveMontage 预测的蒙太奇动画
 */
void OnPredictiveMontageRejected(UAnimMontage* PredictiveMontage);

/**
 * 将本地蒙太奇动画信息复制到用于网络复制的蒙太奇动画信息中
 */
void AnimMontage_UpdateReplicatedData();

/**
 * 将本地蒙太奇动画信息复制到指定的用于网络复制的蒙太奇动画信息结构中
 * @param OutRepAnimMontageInfo 输出的用于网络复制的蒙太奇动画信息结构
 */
void AnimMontage_UpdateReplicatedData(FGameplayAbilityRepAnimMontage& OutRepAnimMontageInfo);

/**
 * 为重复的动画数据复制播放标志
 * @param OutRepAnimMontageInfo 输出的用于网络复制的蒙太奇动画信息结构
 */
void AnimMontage_UpdateForcedPlayFlags(FGameplayAbilityRepAnimMontage& OutRepAnimMontageInfo);

/**
 * 此属性自 4.26 版本起已弃用,在未来的引擎版本中会设为私有。请使用 SetRepAnimMontageInfo、GetRepAnimMontageInfo 或 GetRepAnimMontageInfo_Mutable 替代。
 * 用于向模拟客户端复制蒙太奇动画信息的数据结构
 */
UE_DEPRECATED(4.26, "This will be made private in future engine versions. Use SetRepAnimMontageInfo, GetRepAnimMontageInfo, or GetRepAnimMontageInfo_Mutable instead.")
UPROPERTY(ReplicatedUsing=OnRep_ReplicatedAnimMontage)
FGameplayAbilityRepAnimMontage RepAnimMontageInfo;

/**
 * 设置用于网络复制的蒙太奇动画信息
 * @param NewRepAnimMontageInfo 新的用于网络复制的蒙太奇动画信息
 */
void SetRepAnimMontageInfo(const FGameplayAbilityRepAnimMontage& NewRepAnimMontageInfo);

/**
 * 获取可修改的用于网络复制的蒙太奇动画信息引用
 * @return 可修改的用于网络复制的蒙太奇动画信息引用
 */
FGameplayAbilityRepAnimMontage& GetRepAnimMontageInfo_Mutable();

/**
 * 获取用于网络复制的蒙太奇动画信息的常量引用
 * @return 用于网络复制的蒙太奇动画信息的常量引用
 */
const FGameplayAbilityRepAnimMontage& GetRepAnimMontageInfo() const;

/**
 * 缓存此对象是否为模拟角色的布尔值
 */
UPROPERTY()
bool bCachedIsNetSimulated;

/**
 * 如果在还未关联动画实例时就进行蒙太奇动画信息复制,则设置此标志
 */
UPROPERTY()
bool bPendingMontageRep;

/**
 * 本地发起的蒙太奇动画的数据结构(服务器端为所有蒙太奇,客户端为预测蒙太奇,模拟代理为复制蒙太奇)
 */
UPROPERTY()
FGameplayAbilityLocalAnimMontage LocalAnimMontageInfo;

/**
 * 当用于网络复制的蒙太奇动画信息更新时调用
 */
UFUNCTION()
virtual void OnRep_ReplicatedAnimMontage();

/**
 * 如果已准备好处理网络复制的蒙太奇动画信息,则返回 true
 * @return 是否准备好处理网络复制的蒙太奇动画信息
 */
virtual bool IsReadyForReplicatedMontage();

/**
 * 从 CurrentMontageSetNextSectionName 调用的 RPC 函数,将信息复制到其他客户端
 * @param ClientAnimation 客户端的动画序列
 * @param ClientPosition 客户端的动画位置
 * @param SectionName 当前动画片段名称
 * @param NextSectionName 下一个动画片段名称
 */
UFUNCTION(reliable, server, WithValidation)
void ServerCurrentMontageSetNextSectionName(UAnimSequenceBase* ClientAnimation, float ClientPosition, FName SectionName, FName NextSectionName);

/**
 * 从 CurrentMontageJumpToSection 调用的 RPC 函数,将信息复制到其他客户端
 * @param ClientAnimation 客户端的动画序列
 * @param SectionName 要跳转的动画片段名称
 */
UFUNCTION(reliable, server, WithValidation)
void ServerCurrentMontageJumpToSectionName(UAnimSequenceBase* ClientAnimation, FName SectionName);

/**
 * 从 CurrentMontageSetPlayRate 调用的 RPC 函数,将信息复制到其他客户端
 * @param ClientAnimation 客户端的动画序列
 * @param InPlayRate 动画播放速率
 */
UFUNCTION(reliable, server, WithValidation)
void ServerCurrentMontageSetPlayRate(UAnimSequenceBase* ClientAnimation, float InPlayRate);

/**
 * 由游戏玩法事件触发的技能映射,键为游戏玩法标签,值为技能规格句柄数组
 */
TMap<FGameplayTag, TArray<FGameplayAbilitySpecHandle > > GameplayEventTriggeredAbilities;

/**
 * 由所有者添加特定标签触发的技能映射,键为游戏玩法标签,值为技能规格句柄数组
 */
TMap<FGameplayTag, TArray<FGameplayAbilitySpecHandle > > OwnedTagTriggeredAbilities;

/**
 * 当绑定到技能的所有者标签发生变化时调用的回调函数
 * @param Tag 发生变化的游戏玩法标签
 * @param NewCount 标签的新计数
 */
virtual void MonitoredTagChanged(const FGameplayTag Tag, int32 NewCount);

/**
 * 判断在当前网络模式下,指定的技能是否应从事件中激活
 * @param Spec 技能规格
 * @return 如果有网络权限激活该触发的技能,则返回 true
 */
bool HasNetworkAuthorityToActivateTriggeredAbility(const FGameplayAbilitySpec &Spec) const;

/**
 * 此函数自 5.3 版本起已弃用。请直接使用 OnImmunityBlockGameplayEffectDelegate,它由 UImmunityGameplayEffectComponent 触发。如果需要不同的功能,可以创建自己的 GameplayEffectComponent。
 * 当免疫效果阻止游戏玩法效果应用时调用
 * @param Spec 被阻止应用的游戏玩法效果规格
 * @param ImmunityGE 免疫游戏玩法效果实例
 */
UE_DEPRECATED(5.3, "Use OnImmunityBlockGameplayEffectDelegate directly.  It is trigger from a UImmunityGameplayEffectComponent.  You can create your own GameplayEffectComponent if you need different functionality.")
virtual void OnImmunityBlockGameplayEffect(const FGameplayEffectSpec& Spec, const FActiveGameplayEffect* ImmunityGE);

// 内部游戏玩法提示函数

/**
 * 内部函数,向指定的活动游戏玩法提示容器添加游戏玩法提示
 * @param GameplayCueTag 游戏玩法提示标签
 * @param EffectContext 游戏玩法效果上下文句柄
 * @param GameplayCueContainer 活动游戏玩法提示容器
 */
virtual void AddGameplayCue_Internal(const FGameplayTag GameplayCueTag, FGameplayEffectContextHandle& EffectContext, FActiveGameplayCueContainer& GameplayCueContainer);

/**
 * 内部函数,向指定的活动游戏玩法提示容器添加带有参数的游戏玩法提示
 * @param GameplayCueTag 游戏玩法提示标签
 * @param GameplayCueParameters 游戏玩法提示参数
 * @param GameplayCueContainer 活动游戏玩法提示容器
 */
virtual void AddGameplayCue_Internal(const FGameplayTag GameplayCueTag, const FGameplayCueParameters& GameplayCueParameters, FActiveGameplayCueContainer& GameplayCueContainer);

/**
 * 内部函数,从指定的活动游戏玩法提示容器移除游戏玩法提示
 * @param GameplayCueTag 游戏玩法提示标签
 * @param GameplayCueContainer 活动游戏玩法提示容器
 */
virtual void RemoveGameplayCue_Internal(const FGameplayTag GameplayCueTag, FActiveGameplayCueContainer& GameplayCueContainer);

/**
 * 实际将最终属性值推送到属性集的属性中。外部代码不应调用此函数,因为它不通过属性聚合器系统。
 * @param Attribute 游戏玩法属性
 * @param NewFloatValue 新的属性浮点值
 */
void SetNumericAttribute_Internal(const FGameplayAttribute &Attribute, float& NewFloatValue);

/**
 * 判断在当前网络模式下是否有应用游戏玩法效果的权限
 * @param PredictionKey 预测键
 * @return 如果有网络权限应用游戏玩法效果,则返回 true
 */
bool HasNetworkAuthorityToApplyGameplayEffect(FPredictionKey PredictionKey) const;

/**
 * 执行周期性的游戏玩法效果
 * @param Handle 活动游戏玩法效果句柄
 */
void ExecutePeriodicEffect(FActiveGameplayEffectHandle    Handle);

/**
 * 执行游戏玩法效果
 * @param Spec 游戏玩法效果规格
 * @param PredictionKey 预测键
 */
void ExecuteGameplayEffect(FGameplayEffectSpec &Spec, FPredictionKey PredictionKey);

/**
 * 检查活动游戏玩法效果的持续时间是否已过期
 * @param Handle 活动游戏玩法效果句柄
 */
void CheckDurationExpired(FActiveGameplayEffectHandle Handle);

/**
 * 获取指定技能的活动游戏玩法任务数组引用
 * @param Ability 游戏玩法技能
 * @return 活动游戏玩法任务数组引用
 */
TArray<TObjectPtr<UGameplayTask>>&    GetAbilityActiveTasks(UGameplayAbility* Ability);

/**
 * 一个允许在没有权限时移除活动游戏玩法效果的版本
 * @param Handle 活动游戏玩法效果句柄
 * @param StacksToRemove 要移除的堆叠层数,-1 表示移除所有堆叠
 */
void RemoveActiveGameplayEffect_AllowClientRemoval(FActiveGameplayEffectHandle Handle, int32 StacksToRemove = -1);

/**
 * 包含此组件上当前所有活动的游戏玩法效果的容器
 */
UPROPERTY(Replicated)
FActiveGameplayEffectsContainer ActiveGameplayEffects;

/**
 * 所有活动的游戏玩法提示列表(在游戏玩法效果之外执行的提示)
 */
UPROPERTY(Replicated)
FActiveGameplayCueContainer ActiveGameplayCues;

/**
 * 在最小复制模式下复制的游戏玩法提示。这些提示通常来自活动游戏玩法效果(但由于在最小模式下不复制活动游戏玩法效果,所以必须通过这里进行复制)
 */
UPROPERTY(Replicated)
FActiveGameplayCueContainer MinimalReplicationGameplayCues;

/**
 * 带有这些标签的技能无法被激活
 */
FGameplayTagCountContainer BlockedAbilityTags;

/**
 * 此属性自 4.26 版本起已弃用,在未来的引擎版本中会设为私有。请使用 SetBlockedAbilityBindings、GetBlockedAbilityBindings 或 GetBlockedAbilityBindings_Mutable 替代。
 * 跟踪基于输入绑定被阻止的技能。如果 BlockedAbilityBindings[InputID] > 0,则对应的技能被阻止
 */
UE_DEPRECATED(4.26, "This will be made private in future engine versions. Use SetBlockedAbilityBindings, GetBlockedAbilityBindings, or GetBlockedAbilityBindings_Mutable instead.")
UPROPERTY(Transient, Replicated)
TArray<uint8> BlockedAbilityBindings;

/**
 * 设置被阻止的技能输入绑定数组
 * @param NewBlockedAbilityBindings 新的被阻止的技能输入绑定数组
 */
void SetBlockedAbilityBindings(const TArray<uint8>& NewBlockedAbilityBindings);

/**
 * 获取可修改的被阻止的技能输入绑定数组引用
 * @return 可修改的被阻止的技能输入绑定数组引用
 */
TArray<uint8>& GetBlockedAbilityBindings_Mutable();

/**
 * 获取被阻止的技能输入绑定数组的常量引用
 * @return 被阻止的技能输入绑定数组的常量引用
 */
const TArray<uint8>& GetBlockedAbilityBindings() const;

/**
 * 调试循环属性聚合器广播
 * @param Aggregator 属性聚合器结构体指针
 */
void DebugCyclicAggregatorBroadcasts(struct FAggregator* Aggregator);

/**
 * 所有游戏玩法标签(来自游戏玩法效果的拥有标签和显式的游戏玩法提示标签)的加速映射
 */
FGameplayTagCountContainer GameplayTagCountContainer;
/**
 * 此属性自 4.26 版本起已弃用,在未来的引擎版本中会设为私有。请使用 SetMinimalReplicationTags、GetMinimalReplicationTags 或 GetMinimalReplicationTags_Mutable 替代。
 * 用于网络复制的最小复制标签计数映射
 */
UE_DEPRECATED(4.26, "This will be made private in future engine versions. Use SetMinimalReplicationTags, GetMinimalReplicationTags, or GetMinimalReplicationTags_Mutable instead.")
UPROPERTY(Replicated)
FMinimalReplicationTagCountMap MinimalReplicationTags;

/**
 * 设置最小复制标签计数映射
 * @param NewMinimalReplicationTags 新的最小复制标签计数映射
 */
void SetMinimalReplicationTags(const FMinimalReplicationTagCountMap& NewMinimalReplicationTags);

/**
 * 获取可修改的最小复制标签计数映射引用
 * @return 可修改的最小复制标签计数映射引用
 */
FMinimalReplicationTagCountMap& GetMinimalReplicationTags_Mutable();

/**
 * 获取最小复制标签计数映射的常量引用
 * @return 最小复制标签计数映射的常量引用
 */
const FMinimalReplicationTagCountMap& GetMinimalReplicationTags() const;

/**
 * 获取可修改的复制松散标签计数映射引用
 * @return 可修改的复制松散标签计数映射引用
 */
FMinimalReplicationTagCountMap& GetReplicatedLooseTags_Mutable();

/**
 * 获取复制松散标签计数映射的常量引用
 * @return 复制松散标签计数映射的常量引用
 */
const FMinimalReplicationTagCountMap& GetReplicatedLooseTags() const;

/**
 * 重置标签映射
 */
void ResetTagMap();

/**
 * 通知标签映射的堆叠计数发生变化
 * @param Container 游戏玩法标签容器
 */
void NotifyTagMap_StackCountChange(const FGameplayTagContainer& Container);

/**
 * 当标签更新时调用的虚函数,可在派生类中重写
 * @param Tag 发生更新的游戏玩法标签
 * @param TagExists 标签是否存在
 */
virtual void OnTagUpdated(const FGameplayTag& Tag, bool TagExists) {};

/**
 * 获取指定属性集类的属性集子对象指针,如果不存在则返回 nullptr
 * @param AttributeClass 属性集类
 * @return 属性集子对象指针
 */
const UAttributeSet* GetAttributeSubobject(const TSubclassOf<UAttributeSet> AttributeClass) const;

/**
 * 获取指定属性集类的属性集子对象指针,若不存在则触发断言
 * @param AttributeClass 属性集类
 * @return 属性集子对象指针
 */
const UAttributeSet* GetAttributeSubobjectChecked(const TSubclassOf<UAttributeSet> AttributeClass) const;

/**
 * 获取或创建指定属性集类的属性集子对象
 * @param AttributeClass 属性集类
 * @return 属性集子对象指针
 */
const UAttributeSet* GetOrCreateAttributeSubobject(TSubclassOf<UAttributeSet> AttributeClass);

/**
 * 内部函数,更新标签映射
 * @param Container 游戏玩法标签容器
 * @param CountDelta 计数变化量
 */
void UpdateTagMap_Internal(const FGameplayTagContainer& Container, int32 CountDelta);

// 声明友元结构体和类,允许它们访问私有和受保护成员
friend struct FActiveGameplayEffect;
friend struct FActiveGameplayEffectAction;
friend struct FActiveGameplayEffectsContainer;
friend struct FActiveGameplayCue;
friend struct FActiveGameplayCueContainer;
friend struct FGameplayAbilitySpec;
friend struct FGameplayAbilitySpecContainer;
friend struct FAggregator;
friend struct FActiveGameplayEffectAction_Add;
friend struct FGameplayEffectSpec;
friend class AAbilitySystemDebugHUD;
friend class UAbilitySystemGlobals;

private:

/**
 * 当修改 SpawnedAttributes 数组时需要调用此函数,以使更改能够被网络复制
 */
void SetSpawnedAttributesListDirty();

/**
 * 私有访问器,用于访问 AllReplicatedInstancedAbilities 数组,直到该数组的弃用标签移除,可再次直接引用该数组
 * @return 可修改的所有复制实例化技能数组引用
 */
PRAGMA_DISABLE_DEPRECATION_WARNINGS
TArray<TObjectPtr<UGameplayAbility>>& GetReplicatedInstancedAbilities_Mutable() { return AllReplicatedInstancedAbilities; }
PRAGMA_ENABLE_DEPRECATION_WARNINGS

private:

/**
 * 已生成的属性集列表
 */
UPROPERTY(Replicated, ReplicatedUsing = OnRep_SpawnedAttributes, Transient)
TArray<TObjectPtr<UAttributeSet>> SpawnedAttributes;

/**
 * 当已生成的属性集列表复制更新时调用
 * @param PreviousSpawnedAttributes 之前的已生成属性集列表
 */
UFUNCTION()
void OnRep_SpawnedAttributes(const TArray<UAttributeSet*>& PreviousSpawnedAttributes);

/**
 * 监控标签变化委托句柄
 */
FDelegateHandle MonitoredTagChangedDelegateHandle;

/**
 * OnRep_ActivateAbilities 定时器句柄
 */
FTimerHandle OnRep_ActivateAbilitiesTimerHandle;

/**
 * 用于复制松散游戏玩法标签的容器
 */
UPROPERTY(Replicated)
FMinimalReplicationTagCountMap ReplicatedLooseTags;

/**
 * 指示是否已发起销毁活动状态的操作,使用 1 位存储
 */
uint8 bDestroyActiveStateInitiated : 1;

public:

/**
 * 缓存此组件是否具有网络模拟状态的标志
 */
void CacheIsNetSimulated();

/**
 * 预测键映射,更多信息见 GameplayPrediction.h。此属性必须位于 AbilitySystemComponent 的所有复制属性的最后,以确保 OnRep 和回调的顺序正确
 */
UPROPERTY(Replicated, Transient)
FReplicatedPredictionKeyMap ReplicatedPredictionKeyMap;

protected:

/**
 * 技能列表锁定活动更改结构体,用于管理在技能列表锁定期间的添加和移除操作
 */
struct FAbilityListLockActiveChange
{
    /**
     * 构造函数,将当前更改结构体添加到 AbilitySystemComp 的活动更改列表中
     * @param InAbilitySystemComp 能力系统组件引用
     * @param PendingAdds 待添加的技能规格数组
     * @param PendingRemoves 待移除的技能规格句柄数组
     */
    FAbilityListLockActiveChange(UAbilitySystemComponent& InAbilitySystemComp,
                                 TArray<FGameplayAbilitySpec, TInlineAllocator<2> >& PendingAdds,
                                 TArray<FGameplayAbilitySpecHandle, TInlineAllocator<2> >& PendingRemoves) :
        AbilitySystemComp(InAbilitySystemComp),
        Adds(MoveTemp(PendingAdds)),
        Removes(MoveTemp(PendingRemoves))
    {
        AbilitySystemComp.AbilityListLockActiveChanges.Add(this);
    }

    /**
     * 析构函数,将当前更改结构体从 AbilitySystemComp 的活动更改列表中移除
     */
    ~FAbilityListLockActiveChange()
    {
        AbilitySystemComp.AbilityListLockActiveChanges.Remove(this);
    }

    /**
     * 能力系统组件引用
     */
    UAbilitySystemComponent& AbilitySystemComp;

    /**
     * 待添加的技能规格数组
     */
    TArray<FGameplayAbilitySpec, TInlineAllocator<2> > Adds;

    /**
     * 待移除的技能规格句柄数组
     */
    TArray<FGameplayAbilitySpecHandle, TInlineAllocator<2> > Removes;
};

/**
 * 技能列表锁定活动更改列表
 */
TArray<FAbilityListLockActiveChange*> AbilityListLockActiveChanges;

};

 

posted @ 2025-02-19 18:28  mcwhirr  阅读(421)  评论(0)    收藏  举报