美文网首页
四、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