十五、RPGCharacterBase (GAS related)

RPGCharacter主要负责Gameplay Ability System部分。直接上代码:

头文件:

// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.

#pragma once

#include "ActionRPG.h"
#include "GameFramework/Character.h"
#include "UObject/ScriptInterface.h"
#include "RPGInventoryInterface.h"
#include "AbilitySystemInterface.h"
#include "Abilities/RPGAbilitySystemComponent.h"
#include "Abilities/RPGAttributeSet.h"
#include "RPGCharacterBase.generated.h"

class URPGGameplayAbility;
class UGameplayEffect;



/** Base class for Character, Designed to be blueprinted */
UCLASS()
class ACTIONRPG_API ARPGCharacterBase : public ACharacter, public IAbilitySystemInterface
{
    GENERATED_BODY()

public:
    // Constructor and overrides
    ARPGCharacterBase();

/**   Controller    -------------------------------------------------------------------------------------------------- */
//角色被controller possess时调用:绑定背包的delegate,更新ability system component信息
    virtual void PossessedBy(AController* NewController) override;
    virtual void UnPossessed() override;
    virtual void OnRep_Controller() override;
/**   -------------------------------------------------------------------------------------------------- */
    
// replicate property
virtual void GetLifetimeReplicatedProps(TArray& OutLifetimeProps) const override;

    // Implement IAbilitySystemInterface
    UAbilitySystemComponent* GetAbilitySystemComponent() const override;

#pragma region Attributes Getter ( 属性getter方法 )
    /** Returns current health, will be 0 if dead */
    UFUNCTION(BlueprintCallable)
    float GetHealth() const;

    /** Returns maximum health, health will never be greater than this */
    UFUNCTION(BlueprintCallable)
    float GetMaxHealth() const;

    /** Returns current mana */
    UFUNCTION(BlueprintPure)
    float GetMana() const;

    /** Returns maximum mana, mana will never be greater than this */
    UFUNCTION(BlueprintCallable)
    float GetMaxMana() const;

    /** Returns current movement speed */
    UFUNCTION(BlueprintCallable)
    float GetMoveSpeed() const;
#pragma endregion

    /** Returns the character level that is passed to the ability system */
    UFUNCTION(BlueprintCallable)
    int32 GetCharacterLevel() const;

    /** Modifies the character level, this may change abilities. Returns true on success */
    UFUNCTION(BlueprintCallable)
    bool SetCharacterLevel(int32 NewLevel);

    /**
     * Attempts to activate any ability in the specified item slot. Will return false if no activatable ability found or activation fails
     * Returns true if it thinks it activated, but it may return false positives due to failure later in activation.
     * If bAllowRemoteActivation is true, it will remotely activate local/server abilities, if false it will only try to locally activate the ability
     */
 // 尝试激活item  slot里对应道具的ability
    UFUNCTION(BlueprintCallable, Category = "Abilities")
    bool ActivateAbilitiesWithItemSlot(FRPGItemSlot ItemSlot, bool bAllowRemoteActivation = true);

    /** Returns a list of active abilities bound to the item slot. This only returns if the ability is currently running */
//返回特定slot中正在生效中的ability list
    UFUNCTION(BlueprintCallable, Category = "Abilities")
    void GetActiveAbilitiesWithItemSlot(FRPGItemSlot ItemSlot, TArray& ActiveAbilities);

    /**
     * Attempts to activate all abilities that match the specified tags
     * Returns true if it thinks it activated, but it may return false positives due to failure later in activation.
     * If bAllowRemoteActivation is true, it will remotely activate local/server abilities, if false it will only try to locally activate the ability
     */
// 通过tag来激活activity
    UFUNCTION(BlueprintCallable, Category = "Abilities")
    bool ActivateAbilitiesWithTags(FGameplayTagContainer AbilityTags, bool bAllowRemoteActivation = true);

    /** Returns a list of active abilities matching the specified tags. This only returns if the ability is currently running */
    UFUNCTION(BlueprintCallable, Category = "Abilities")
    void GetActiveAbilitiesWithTags(FGameplayTagContainer AbilityTags, TArray& ActiveAbilities);

    /** Returns total time and remaining time for cooldown tags. Returns false if no active cooldowns found */
    UFUNCTION(BlueprintCallable, Category = "Abilities")
    bool GetCooldownRemainingForTag(FGameplayTagContainer CooldownTags, float& TimeRemaining, float& CooldownDuration);

protected:
    /** The level of this character, should not be modified directly once it has already spawned */
    UPROPERTY(EditAnywhere, Replicated, Category = "Abilities")
    int32 CharacterLevel;

    /** Abilities to grant to this character on creation. These will be activated by tag or event and are not bound to specific inputs */
    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Abilities")
    TArray> GameplayAbilities;

    /** Map of item slot to gameplay ability class, these are bound before any abilities added by the inventory */
    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Abilities")
    TMap> DefaultSlottedAbilities;

    /** Passive gameplay effects applied on creation */
    UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Abilities")
    TArray> PassiveGameplayEffects;

    /** The component used to handle ability system interactions */
    UPROPERTY()
    URPGAbilitySystemComponent* AbilitySystemComponent;

    /** List of attributes modified by the ability system */
    UPROPERTY()
    URPGAttributeSet* AttributeSet;

    /** Cached pointer to the inventory source for this character, can be null */
    UPROPERTY()
    TScriptInterface InventorySource;

    /** If true we have initialized our abilities */
    UPROPERTY()
    int32 bAbilitiesInitialized;

    /** Map of slot to ability granted by that slot. I may refactor this later */
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Inventory)
    TMap SlottedAbilities;

    /** Delegate handles */
    FDelegateHandle InventoryUpdateHandle;
    FDelegateHandle InventoryLoadedHandle;

    /**
     * Called when character takes damage, which may have killed them
     *
     * @param DamageAmount Amount of damage that was done, not clamped based on current health
     * @param HitInfo The hit info that generated this damage
     * @param DamageTags The gameplay tags of the event that did the damage
     * @param InstigatorCharacter The character that initiated this damage
     * @param DamageCauser The actual actor that did the damage, might be a weapon or projectile
     */
    UFUNCTION(BlueprintImplementableEvent)
    void OnDamaged(float DamageAmount, const FHitResult& HitInfo, const struct FGameplayTagContainer& DamageTags, ARPGCharacterBase* InstigatorCharacter, AActor* DamageCauser);

    /**
     * Called when health is changed, either from healing or from being damaged
     * For damage this is called in addition to OnDamaged/OnKilled
     *
     * @param DeltaValue Change in health value, positive for heal, negative for cost. If 0 the delta is unknown
     * @param EventTags The gameplay tags of the event that changed mana
     */
    UFUNCTION(BlueprintImplementableEvent)
    void OnHealthChanged(float DeltaValue, const struct FGameplayTagContainer& EventTags);

    /**
     * Called when mana is changed, either from healing or from being used as a cost
     *
     * @param DeltaValue Change in mana value, positive for heal, negative for cost. If 0 the delta is unknown
     * @param EventTags The gameplay tags of the event that changed mana
     */
    UFUNCTION(BlueprintImplementableEvent)
    void OnManaChanged(float DeltaValue, const struct FGameplayTagContainer& EventTags);

    /**
     * Called when movement speed is changed
     *
     * @param DeltaValue Change in move speed
     * @param EventTags The gameplay tags of the event that changed mana
     */
    UFUNCTION(BlueprintImplementableEvent)
    void OnMoveSpeedChanged(float DeltaValue, const struct FGameplayTagContainer& EventTags);

    /** Called when slotted items change, bound to delegate on interface */
    void OnItemSlotChanged(FRPGItemSlot ItemSlot, URPGItem* Item);
    void RefreshSlottedGameplayAbilities();

    /** Apply the startup gameplay abilities and effects */
    void AddStartupGameplayAbilities();

    /** Attempts to remove any startup gameplay abilities */
    void RemoveStartupGameplayAbilities();

    /** Adds slotted item abilities if needed */
    void AddSlottedGameplayAbilities();

    /** Fills in with ability specs, based on defaults and inventory */
    void FillSlottedAbilitySpecs(TMap& SlottedAbilitySpecs);

    /** Remove slotted gameplay abilities, if force is false it only removes invalid ones */
    void RemoveSlottedGameplayAbilities(bool bRemoveAll);

    // Called from RPGAttributeSet, these call BP events above
    virtual void HandleDamage(float DamageAmount, const FHitResult& HitInfo, const struct FGameplayTagContainer& DamageTags, ARPGCharacterBase* InstigatorCharacter, AActor* DamageCauser);
    virtual void HandleHealthChanged(float DeltaValue, const struct FGameplayTagContainer& EventTags);
    virtual void HandleManaChanged(float DeltaValue, const struct FGameplayTagContainer& EventTags);
    virtual void HandleMoveSpeedChanged(float DeltaValue, const struct FGameplayTagContainer& EventTags);

    // Friended to allow access to handle functions above
      // 上面所有的Handle XXX函数,都会在URPGAttributeSet中被调用
    friend URPGAttributeSet;
};

头文件的public方法表明,我们可以通过item slotgameplay tag来activate ability。
character具有初始的ability list,并且负责所有属性变化的回调。


源文件:

只摘取部分讲解:

构造函数

注意在此处创建AbilitySystemComponentAttributeSet作为Character的SubObject。

ARPGCharacterBase::ARPGCharacterBase()
{
    // Create ability system component, and set it to be explicitly replicated
    AbilitySystemComponent = CreateDefaultSubobject(TEXT("AbilitySystemComponent"));
    AbilitySystemComponent->SetIsReplicated(true);
    
    // Create the attribute set, this replicates by default
    AttributeSet = CreateDefaultSubobject(TEXT("AttributeSet"));
    
    CharacterLevel = 1;
    bAbilitiesInitialized = false;
}
实现GetAbilitySystemComponent()接口
UAbilitySystemComponent* ARPGCharacterBase::GetAbilitySystemComponent() const
{
    return AbilitySystemComponent;
}
初始阶段:赋予Ability、触发被动effect
void ARPGCharacterBase::AddStartupGameplayAbilities()
{
    check(AbilitySystemComponent);
    
// 确认是否在服务端
    if (Role == ROLE_Authority && !bAbilitiesInitialized)
    {
        // Grant abilities, but only on the server  
        for (TSubclassOf& StartupAbility : GameplayAbilities)
        {
//赋予技能(技能是必须被赋予后才能激活的)
            AbilitySystemComponent->GiveAbility(FGameplayAbilitySpec(StartupAbility, GetCharacterLevel(), INDEX_NONE, this));
        }

        // Now apply passives
        for (TSubclassOf& GameplayEffect : PassiveGameplayEffects)
        {
// 创建effect context ,即effect的施放者,施放者位置等等其他的数据
            FGameplayEffectContextHandle EffectContext = AbilitySystemComponent->MakeEffectContext();
            EffectContext.AddSourceObject(this);
//生成gameplay effect spec供应用
            FGameplayEffectSpecHandle NewHandle = AbilitySystemComponent->MakeOutgoingSpec(GameplayEffect, GetCharacterLevel(), EffectContext);
            if (NewHandle.IsValid())
            {
//正式的激活效果
                FActiveGameplayEffectHandle ActiveGEHandle = AbilitySystemComponent->ApplyGameplayEffectSpecToTarget(*NewHandle.Data.Get(), AbilitySystemComponent);
            }
        }

        AddSlottedGameplayAbilities();

        bAbilitiesInitialized = true;
    }
}
激活item slot里的技能
bool ARPGCharacterBase::ActivateAbilitiesWithItemSlot(FRPGItemSlot ItemSlot, bool bAllowRemoteActivation)
{
    FGameplayAbilitySpecHandle* FoundHandle = SlottedAbilities.Find(ItemSlot);

    if (FoundHandle && AbilitySystemComponent)
    {
        return AbilitySystemComponent->TryActivateAbility(*FoundHandle, bAllowRemoteActivation);
    }

    return false;
}
通过tag来激活技能
bool ARPGCharacterBase::ActivateAbilitiesWithTags(FGameplayTagContainer AbilityTags, bool bAllowRemoteActivation)
{
    if (AbilitySystemComponent)
    {
        return AbilitySystemComponent->TryActivateAbilitiesByTag(AbilityTags, bAllowRemoteActivation);
    }

    return false;
}

还有几个ability system component的函数值得注意:

  • 使用effect query来获取效果的持续时间
FGameplayEffectQuery const Query = FGameplayEffectQuery::MakeQuery_MatchAnyOwningTags(CooldownTags);
        TArray< TPair > DurationAndTimeRemaining = AbilitySystemComponent->GetActiveEffectsTimeRemainingAndDuration(Query);
  • 利用handle返回ability spec
FGameplayAbilitySpec* FoundSpec = AbilitySystemComponent->FindAbilitySpecFromHandle(*FoundHandle);

你可能感兴趣的:(十五、RPGCharacterBase (GAS related))