美文网首页
四、RPGGameplayAbility.h/cpp & Gam

四、RPGGameplayAbility.h/cpp & Gam

作者: 珏_Gray | 来源:发表于2019-06-11 14:51 被阅读0次

    Gameplay Ability

    对于Gameplay Ability的官方说明:

    /**
     * UGameplayAbility
     *  
     *  Abilities define custom gameplay logic that can be activated or triggered.
     *  
     *  The main features provided by the AbilitySystem for GameplayAbilities are: 
     *      -CanUse functionality:
     *          -Cooldowns
     *          -Costs (mana, stamina, etc)
     *          -etc
     *          
     *      -Replication support
     *          -Client/Server communication for ability activation
     *          -Client prediction for ability activation
     *          
     *      -Instancing support
     *          -Abilities can be non-instanced (native only)
     *          -Instanced per owner
     *          -Instanced per execution (default)
     *          
     *      -Basic, extendable support for:
     *          -Input binding
     *          -'Giving' abilities (that can be used) to actors
     *  
     *
     *  See GameplayAbility_Montage for an example of a non-instanced ability
     *      -Plays a montage and applies a GameplayEffect to its target while the montage is playing.
     *      -When finished, removes GameplayEffect.
     *  
     *  Note on replication support:
     *      -Non instanced abilities have limited replication support. 
     *          -Cannot have state (obviously) so no replicated properties
     *          -RPCs on the ability class are not possible either.
     *          
     *  To support state or event replication, an ability must be instanced. This can be done with the InstancingPolicy property.
     */
    

    整体上的概括就是:
    Abilities define custom gameplay logic that can be activated or triggered.
    Abilities定义可激活或触发的自定义游戏玩法逻辑。
    所以才有“Everything can be a gameplay ability”的说法。同样注意,这里说Ability是逻辑,也就是这个类主要是实现各种行为,比如:技能释放、撤销、目标选择等等。可以通过tag来影响Ability的运作逻辑,通过effect来影响actor的属性(attributes)。

    UGameplayAbility类中重要的函数有:

    // ----------------------------------------------------------------------------------------------------------------
        //
        //  The important functions:
        //  
        //      CanActivateAbility()    - const function to see if ability is activatable. Callable by UI etc
        //
        //      TryActivateAbility()    - Attempts to activate the ability. Calls CanActivateAbility(). Input events can call this directly.
        //                              - Also handles instancing-per-execution logic and replication/prediction calls.
        //      
        //      CallActivateAbility()   - Protected, non virtual function. Does some boilerplate 'pre activate' stuff, then calls ActivateAbility()
        //
        //      ActivateAbility()       - What the abilities *does*. This is what child classes want to override.
        //  
        //      CommitAbility()         - Commits reources/cooldowns etc. ActivateAbility() must call this!
        //      
        //      CancelAbility()         - Interrupts the ability (from an outside source).
        //
        //      EndAbility()            - The ability has ended. This is intended to be called by the ability to end itself.
        //  
        // ----------------------------------------------------------------------------------------------------------------
    

    之外,还有大量的行为(函数),头文件中对他们做了分类:

    • Accessors : 一系列的访问函数
    • CanActivateAbility :判断能否释放技能
    • CancelAbility : 撤销技能
    • CommitAbility : 确认技能释放
    • Input: 用户输入相关
    • Animation : 动画
    • Ability Levels and source object: 技能等级和技能来源
    • Interaction with ability system component:与Ability System Component的信息传递
    • EndAbility:结束技能
    • Ability Tasks: 异步任务
    • Apply/Remove gameplay effects: 应用或移除gameplay effects
      ............
      头文件含865行代码,内容较多,不一一列出了。
      我们的自定义逻辑,主要是ActivateAbility()中完成,该方法可支持蓝图实现。
    // --------------------------------------
        //  ActivateAbility
        // --------------------------------------
    
        /**
         * The main function that defines what an ability does.
         *  -Child classes will want to override this
         *  -This function graph should call CommitAbility
         *  -This function graph should call EndAbility
         *  
         *  Latent/async actions are ok in this graph. Note that Commit and EndAbility calling requirements speak to the K2_ActivateAbility graph. 
         *  In C++, the call to K2_ActivateAbility() may return without CommitAbility or EndAbility having been called. But it is expected that this
         *  will only occur when latent/async actions are pending. When K2_ActivateAbility logically finishes, then we will expect Commit/End to have been called.
         *  
         */
    
    

    在谈RPGGameplayAbility.h之前,我们需要先看RPGAbilityTypes.h。

    RPGAbilityType.h/cpp

    头文件:

    // Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
    
    #pragma once
    
    // ----------------------------------------------------------------------------------------------------------------
    // This header is for Ability-specific structures and enums that are shared across a project
    // Every game will probably need a file like this to handle their extensions to the system
    // This file is a good place for subclasses of FGameplayEffectContext and FGameplayAbilityTargetData
    // ----------------------------------------------------------------------------------------------------------------
    
    #include "ActionRPG.h"
    #include "GameplayEffectTypes.h"
    #include "Abilities/GameplayAbilityTargetTypes.h"
    #include "RPGAbilityTypes.generated.h"
    
    class URPGAbilitySystemComponent;
    class UGameplayEffect;
    class URPGTargetType;
    
    
    /**
     * Struct defining a list of gameplay effects, a tag, and targeting info
     * These containers are defined statically in blueprints or assets and then turn into Specs at runtime
     */
    USTRUCT(BlueprintType)
    struct FRPGGameplayEffectContainer
    {
        GENERATED_BODY()
    
    public:
        FRPGGameplayEffectContainer() {}
    
        /** Sets the way that targeting happens */
        UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = GameplayEffectContainer)
        TSubclassOf<URPGTargetType> TargetType;
    
        /** List of gameplay effects to apply to the targets */
        UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = GameplayEffectContainer)
        TArray<TSubclassOf<UGameplayEffect>> TargetGameplayEffectClasses;
    };
    
    /** A "processed" version of RPGGameplayEffectContainer that can be passed around and eventually applied */
    USTRUCT(BlueprintType)
    struct FRPGGameplayEffectContainerSpec
    {
        GENERATED_BODY()
    
    public:
        FRPGGameplayEffectContainerSpec() {}
    
        /** Computed target data */
        UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = GameplayEffectContainer)
        FGameplayAbilityTargetDataHandle TargetData;
    
        /** List of gameplay effects to apply to the targets */
        UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = GameplayEffectContainer)
        TArray<FGameplayEffectSpecHandle> TargetGameplayEffectSpecs;
    
        /** Returns true if this has any valid effect specs */
        bool HasValidEffects() const;
    
        /** Returns true if this has any valid targets */
        bool HasValidTargets() const;
    
        /** Adds new targets to target data */
        void AddTargets(const TArray<FHitResult>& HitResults, const TArray<AActor*>& TargetActors);
    };
    
    

    这个文件中定义了两个结构体,用来辅助ability。官方推荐玩家继承FGameplayEffectContextFGameplayAbilityTargetData来实现自己的ability复杂逻辑。这些结构体围绕的通常是这两个点:Effect Target,即技能携带的效果技能释放目标

    注意上面代码注解中的"processed version"。这很切合数据流的思维。FRPGGameplayEffectContainer的数据作为输入,经过某个系统的一系列行为处理之后,输出FRPGGameplayEffectContainerSpec供下一阶段使用。在Unreal Engine中可能所有带"Spec"后缀的都是类似这种情况(注:没有证明的猜测)。


    RPGGameplayAbility.h/cpp

    // Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
    
    #pragma once
    
    #include "ActionRPG.h"
    #include "GameplayAbility.h"
    #include "GameplayTagContainer.h"
    #include "Abilities/RPGAbilityTypes.h"
    #include "RPGGameplayAbility.generated.h"
    
    /**
     * Subclass of ability blueprint type with game-specific data
     * This class uses GameplayEffectContainers to allow easier execution of gameplay effects based on a triggering tag
     * Most games will need to implement a subclass to support their game-specific code
     */
    UCLASS()
    class ACTIONRPG_API URPGGameplayAbility : public UGameplayAbility
    {
        GENERATED_BODY()
    
    public:
        // Constructor and overrides
        URPGGameplayAbility();
    
        /** Map of gameplay tags to gameplay effect containers */
        UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = GameplayEffects)
        TMap<FGameplayTag, FRPGGameplayEffectContainer> EffectContainerMap;
    
        /** Make gameplay effect container spec to be applied later, using the passed in container */
        UFUNCTION(BlueprintCallable, Category = Ability, meta=(AutoCreateRefTerm = "EventData"))
        virtual FRPGGameplayEffectContainerSpec MakeEffectContainerSpecFromContainer(const FRPGGameplayEffectContainer& Container, const FGameplayEventData& EventData, int32 OverrideGameplayLevel = -1);
    
        /** Search for and make a gameplay effect container spec to be applied later, from the EffectContainerMap */
        UFUNCTION(BlueprintCallable, Category = Ability, meta = (AutoCreateRefTerm = "EventData"))
        virtual FRPGGameplayEffectContainerSpec MakeEffectContainerSpec(FGameplayTag ContainerTag, const FGameplayEventData& EventData, int32 OverrideGameplayLevel = -1);
    
        /** Applies a gameplay effect container spec that was previously created */
        UFUNCTION(BlueprintCallable, Category = Ability)
        virtual TArray<FActiveGameplayEffectHandle> ApplyEffectContainerSpec(const FRPGGameplayEffectContainerSpec& ContainerSpec);
    
        /** Applies a gameplay effect container, by creating and then applying the spec */
        UFUNCTION(BlueprintCallable, Category = Ability, meta = (AutoCreateRefTerm = "EventData"))
        virtual TArray<FActiveGameplayEffectHandle> ApplyEffectContainer(FGameplayTag ContainerTag, const FGameplayEventData& EventData, int32 OverrideGameplayLevel = -1);
    };
    

    官方建议,大多游戏通常要拓展UGameplayAbility。因为不同游戏的Ability可能需要额外的不同的数据。
    ActionRPG中添加了

     TMap<FGameplayTag, FRPGGameplayEffectContainer> EffectContainerMap;
    

    可以方便在蓝图中设定GameplayTag、TargetType(TargetType是项目自定义的,继承自UObject)和GameplayEffect。

    添加的行为主要是:

    /** 将Container的数据处理成ContainerSpec,只有当数据变为ContainSpec时,才做好了释放的准备。
    会调用到  UGameplayAbility::MakeOutgoingGameplayEffectSpec(...)*/
    virtual FRPGGameplayEffectContainerSpec MakeEffectContainerSpecFromContainer
    (const FRPGGameplayEffectContainer& Container, const FGameplayEventData& EventData, int32 OverrideGameplayLevel = -1);
    
    /** 应用之前的Spec数据中 GameplaySpecHandle对应的GameplayEffect。
    会调用到 UGameplayAbility::K2_ApplyGameplayEffectSpecToTarget(...)
    K2 可能代表着:kismet 2代,也就是蓝图系统。1代源自虚幻3 */
    virtual TArray<FActiveGameplayEffectHandle> ApplyEffectContainerSpec(const FRPGGameplayEffectContainerSpec& ContainerSpec);
    

    我们会发现在GameplayAbility中会大量的和GameplayEffect打交道,毕竟最终作用于Gameplay Attributes的是GameplayEffect。

    相关文章

      网友评论

          本文标题:四、RPGGameplayAbility.h/cpp & Gam

          本文链接:https://www.haomeiwen.com/subject/zrrqfctx.html