十五、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<FLifetimeProperty>& 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<URPGGameplayAbility*>& 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<URPGGameplayAbility*>& 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<TSubclassOf<URPGGameplayAbility>> 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<FRPGItemSlot, TSubclassOf<URPGGameplayAbility>> DefaultSlottedAbilities;

    /** Passive gameplay effects applied on creation */
    UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Abilities")
    TArray<TSubclassOf<UGameplayEffect>> 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<IRPGInventoryInterface> 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<FRPGItemSlot, FGameplayAbilitySpecHandle> 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<FRPGItemSlot, FGameplayAbilitySpec>& 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<URPGAbilitySystemComponent>(TEXT("AbilitySystemComponent"));
    AbilitySystemComponent->SetIsReplicated(true);
    
    // Create the attribute set, this replicates by default
    AttributeSet = CreateDefaultSubobject<URPGAttributeSet>(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<URPGGameplayAbility>& StartupAbility : GameplayAbilities)
        {
//赋予技能(技能是必须被赋予后才能激活的)
            AbilitySystemComponent->GiveAbility(FGameplayAbilitySpec(StartupAbility, GetCharacterLevel(), INDEX_NONE, this));
        }

        // Now apply passives
        for (TSubclassOf<UGameplayEffect>& 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<float, float> > DurationAndTimeRemaining = AbilitySystemComponent->GetActiveEffectsTimeRemainingAndDuration(Query);
  • 利用handle返回ability spec
FGameplayAbilitySpec* FoundSpec = AbilitySystemComponent->FindAbilitySpecFromHandle(*FoundHandle);
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,546评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,224评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,911评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,737评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,753评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,598评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,338评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,249评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,696评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,888评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,013评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,731评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,348评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,929评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,048评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,203评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,960评论 2 355