Help us understand the problem. What is going on with this article?

UE4 GameplayAbility Pluginについてのメモ

概要

UnrealEngine のプラグイン GameplayAbility についてのメモです。
多機能なため、まとまっていません。

修正履歴

日付 内容
2019/11/06 GameplayAbilitySpecについて追記、GameplayEffectのカーブテーブルについて追記
2019/11/23 GameplayEffect の記述を追加
2019/12/13 スケルタルメッシュ切り替えについて追記

環境

Windows10
Visual Studio 2017
UnrealEngine 4.22

参考

以下を参考にさせて頂きました、ありがとうございます。

GameplayAbilitiesの使い方
GameplayAbility - GameplayAbility と コンポーネント の準備編
UnrealEngineマニュアル-ゲームプレイアビリティシステム
UnrealEngineマニュアル-ActionRPG のゲームプレイアビリティ
GASDocumentation

GameplayAbilityBP

AbilityTag

アビリティ同士の排他処理や条件管理ができます。
このAbilityTagは AbilitySystemComponentクラスにて管理されています。

AbilityTag.png

  • Ability Tags - アビリティ自身が持っているタグ
  • Cancel Abilities with Tag - このタグを持っている実行中のアビリティを中止させる
  • Block Abilities with Tag - このタグを持っているアビリティを実行させない
  • Activation Owned Tags - アビリティが有効な時のみ持つタグ
  • Activation Required Tags - 全てのタグを持っていないとアビリティを有効にしない
  • Activation Blocked Tags - いずれかのタグを持っていた場合、アビリティを有効にしない。

AbilityTagでの制御について追記

以下のメソッドで、BPにて設定されたAbilityTagそのものの設定ができるようです。
インスタンス化されたアビリティのみ有効です。

// ブロックタグセットを有効/無効に設定
virtual void SetShouldBlockOtherAbilities(bool bShouldBlockAbilities);

// キャンセルタグセットを有効/無効に設定
virtual void SetCanBeCanceled(bool bCanBeCanceled);

コストやクールダウンの設定

Costs や Cooldowns に GameplayEffectのBPを設定できます。

Costs は DurationPolicy を Instant で必要なコストをアトリビュート経由で計算させます。
GE_Cost.png

Cooldowns は DurationPolicy を HasDuration で時間設定と GrantedTags にブロックするタグを追加する。
GE_Cooldown.png

GamePlay Effect の Executions

[GamePlay Effect] の [Executions] -> [Calculation Class] を設定すると計算用のクラス(UGameplayEffectExecutionCalculationを継承したクラス)を使うことができます。

  • ARPGサンプルのGE_DamageBase GE_DamageBase.png

UGameplayAbilityクラス

アビリティ情報のもとになるクラス。
継承可能なメソッドについての記載します。

CanActivateAbility

アビリティが実行可能かを調べるメソッドです。
アビリティ実行前に呼ばれる事を想定されているため、アビリティのプロパティ[Advanced-Instancing Policy]が[Instanced Per Execution]の場合はインスタンス化されていないため、コンストラクタ等で設定したメンバ変数の値になっていないので要注意。

InstanceingPolicy.png

これを回避するには[Instanced Per Actor]にする等の対応が必要。

CommitAbility

アビリティのコストやクールダウン情報をコミットする処理。

CommitAbility.png

PreActivate

アビリティの実行前処理。TryActivateAbilityが実行成功すると直ちに呼ばれる?

ActivateAbility

アビリティ実行処理。

ActivateAbility.png

EndAbility

アビリティの終了、BP内などで呼ぶ。

EndAbility.png

CancelAbility

外部からのアビリティの中断時に呼ばれる。

CancelAbility.png

CheckCooldown

アビリティ使用のためのクールダウン時間が明けているかのチェック関数

ApplyCooldown

CommitAbility で呼ばれる。
クールダウン情報(gameplayEffect)の適用

CheckCost

アビリティ使用のためのコストが足りているかのチェック関数

ApplyCost

CommitAbility で呼ばれる。
コスト情報(gameplayEffect)の適用

アビリティシステムコンポーネントクラス(AbilitySystemComponent)

対象のアクターが所持する必要があるコンポーネントです。
このコンポーネントが各アビリティを管理しており、FGameplayTagContainerクラスではなく、カウントできるように拡張したFGameplayTagCountContainerでタグを管理しているようです。

FGameplayTagCountContainerクラスは、
"Engine\Plugins\Runtime\GameplayAbilities\Source\GameplayAbilities\Public\GameplayEffectTypes.h"
に定義されています。

コンポーネント有効化/無効化時のイベントをC++で登録する

ActorComponentを継承しているコンポーネントクラスは皆一緒です。

MyAbilitySystemComponent.h
virtual void InitializeComponent() override;

// コンポーネントが有効化時に呼ばれる処理
UFUNCTION()
void OnActivated(UActorComponent* Component, bool bReset);
// コンポーネント無効化時に呼ばれる処理
UFUNCTION()
void OnDeactivated(UActorComponent* Component);
MyAbilitySystemComponent.cpp
void UMyAbilitySystemComponent::InitializeComponent()
{
    Super::BeginPlay();

    // デリゲート登録
    OnComponentActivated.AddDynamic(this, &UMyAbilitySystemComponent::OnActivated);
    OnComponentDeactivated.AddDynamic(this, &UMyAbilitySystemComponent::OnDeactivated);
}

上記のデリゲート登録処理ですが、コードを書き換え LiveCodingでコンパイル&パッチ適用したところうまく呼ばれなかったということが起きました。恐らくホットリロードも一緒かと思いますが、挙動が怪しい場合はビルドし直すと良いと思います。

HasMatchingGameplayTag

アビリティコンポーネントが管理するゲームプレイタグに対し、引き数で指定したゲームプレイタグを持っているか調べます。

AddLooseGameplayTag

GameplayEffectとは関係なくゲームプレイタグを付与するメソッドです。
タグコンテナを引き数に使う場合は、AddLooseGameplayTags になります。

RemoveLooseGameplayTag

GameplayEffectとは関係なくゲームプレイタグを削除するメソッドです。
タグコンテナを引き数に使う場合は、RemoveLooseGameplayTags になります。

ゲームプレイエフェクト(GameplayEffect) / アトリビュートセット(AttributeSet)

GameplayEffect のDurationPolicyについて

[Gameplay Effect]->[Duration Policy] は [Instant][Infinite][Has Duration]の3パターンあり、[Instant]は即時、[Infinite]は対象のゲームプレイエフェクトが適用中のみ、[Has Duration]は指定した時間中のみとなります。

DurationPolicy.jpg

GameplayEffect適用時のAttributeSetのメソッド呼び出しについて

ApplyGameplayEffectToOwnerにて GameplayEffectを適用した場合、DurationPolicy が [Infinite]か[HasDuration] の時は UAttributeSet::PostGameplayEffectExecute() が呼ばれない?

GE_DurationPolicy_Instant.png

GE_DurationPolicy.png

GameplayEffect 適用例(Infinite)

[Duration Policy]を[Infinite]にして、任意のタイミングで[RemoveGameplayEffectFromOwnerWith...]で適用を外すと適用していたGameplayEffectでの[Modifiers]での値変更がリセットされ、UPlayerAttributeSet::PreAttributeChange()で値変更のメソッドが呼ばれます。

RemoveGameplayEffectFromOwnerWithHandle.jpg

この時のアトリビュートで受け取るリセット値は 0.0f 固定となるようです。

GameplayEffect 適用例(Has Duration)

[Duration Policy]を[Has Duration]にして、適用時間を決めるとゲームプレイエフェクトが適用終了時に以下の様にタスクで受け取ることができます。
WaitForGameplayEffectRemoved.jpg
[On Removed]は適用時間の終了時、[Invalid Handle]は恐らく外部から消された時だと思われます。

Modifiersでのカーブテーブル適用について

適用時の[Gameplay Effect Level]を変更し、カーブテーブルを適用することによってパラメータを変えることができます。
GE_Modifier_CT.jpg

以下はカーブテーブルの元になるjsonの例です。レベル1~3まで定義しています。

CT_param.json
[
    {
        "Name": "MidiumAttack",
        "1": 10,
        "2": 15,
        "3": 20
    },
    {
        "Name": "HeveyAttack",
        "1": 20,
        "2": 25,
        "3": 30
    },
]

ゲームプレイアビリティスペック(FGameplayAbilitySpec)

アビリティに付加される情報(クラス、レベル、入力など)を保持するクラスです。ソースは
"\Engine\Plugins\Runtime\GameplayAbilities\Source\GameplayAbilities\Public\GameplayAbilitySpec.h"
です。

以下はアビリティコンポーネントから AlilitySpecの情報へアクセスするコード例です。

AbilitySystemComponent.cpp
// 指定のタグが付いたアビリティを調べる
FGameplayTagContainer _Container;
_Container.AddTag(FGameplayTag::RequestGameplayTag(FName("Ability.Test")));
TArray<FGameplayAbilitySpec*> AbilitiesSpcs;
GetActivatableGameplayAbilitySpecsByAllMatchingTags(_Container, AbilitiesSpcs);
for (FGameplayAbilitySpec* _Spec : AbilitiesSpcs) {
    // アビリティレベルを2へ変更
    _Spec->Level = 2;
}

指定したアビリティレベルは GameplayAbilityグラフで使えます。
GetabilityLevel.png

アビリティタスク(AbilityTask)

アビリティに対し、任意のタスクをつくることができます。
BPでは以下の様にいろいろでてきます。

AbilityTask.jpg

[WaitDelay]や[WaitGameplayEvent]あたりをよく使います。

アビリティタスクの作成

任意のアクターのメソッドを監視するアビリティタスクの作成例です。

.h
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FWaitMyTaskDelegate);

UCLASS()
class TEST_API UMyAbilityTask_WaitMyTask : public UAbilityTask
{
    GENERATED_UCLASS_BODY()

    UPROPERTY(BlueprintAssignable)
    FWaitMyTaskDelegate OnFinish;

    // アクティベート
    virtual void Activate() override;

    // タスク生成
    UFUNCTION(BlueprintCallable, Category="Ability|Tasks", meta = (HidePin = "OwningAbility", DefaultToSelf = "OwningAbility", BlueprintInternalUseOnly = "TRUE"))
    static UMyAbilityTask_WaitMyTask* WaitMyTask(UGameplayAbility* OwningAbility, class AActor* MyActor);

    // Tick処理(bTickingTask が true の時)
    virtual void TickTask(float DeltaTime) override;

private:
    // タスク終了時に呼ばれる処理
    void OnTaskFinish();

    class   AActor* MyActor;
    bool    bIsFinished;
};

MyActor の IsMyTask() というメソッドから終了を監視します。

.cpp
UMyAbilityTask_WaitMyTask::UMyAbilityTask_WaitMyTask(const FObjectInitializer& ObjectInitializer)
    : Super(ObjectInitializer)
{
    bTickingTask = true;
    bIsFinished = false;

}

// タスク生成
UMyAbilityTask_WaitMyTask*
UMyAbilityTask_WaitMyTask::WaitMyTask(UGameplayAbility* OwningAbility, class AActor* MyActor)
{
    UMyAbilityTask_WaitMyTask* MyObj = NewAbilityTask<UMyAbilityTask_WaitMyTask>(OwningAbility);
    MyObj->MyActor = MyActor;

    return(MyObj);
}

void UMyAbilityTask_WaitMyTask::Activate()
{
}

// Tick
void UMyAbilityTask_WaitMyTask::TickTask(float DeltaTime)
{
    if (bIsFinished){ return; }

    Super::TickTask(DeltaTime);

    if(MyActor){
        // メソッドチェック
        if( !MyActor->IsMyTask() ){
            // 終了
            bIsFinished = true;
            OnTaskFinish();
        }
    }

}

// タスク終了時に呼ばれる処理
void UMyAbilityTask_WaitMyTask::OnTaskFinish()
{
    if (ShouldBroadcastAbilityTaskDelegates())
    {
        OnFinish.Broadcast();
    }
    EndTask();
}

BPノードは以下の様に表示されます。
AbilityTask_WaitMyTask.png

ゲームプレイキュー(GameplayCue)

アトリビュートの変化時などにエフェクトやサウンドなどを発生させる時などに使います。

作成例

サウンドを再生するゲームプレイキューを作成して、アビリティから呼び出しをしてみます。

  1. GameplayCueNotify_Actor を親クラスとしてBPを作成。
    gameplaycue.png

  2. 作成したBPのBeginイベントにサウンドをスポーンするようにBPを組みます。なお自身はDestoryActorで始末します。また、[GameplayCueTag]にタグを設定します。

gameplaycue_sample.png

  1. アビリティから AddGameplayCueToOwnerで呼び出します、この時の GameplayCueTag はゲームプレイキューで設定したタグです。

addgameplaycue.png

これでアビリティが実行されるたびに、ゲームプレイキューが呼び出されます。

gameplayeffect_display.png

ゲームプレイエフェクトの[Display]に対応したGameplayCueTagを設定して呼び出す方法もあるようです。

その他

アビリティに対してイベントを発行する

アクターのアビリティに対し、イベントを発行します。アビリティ側は[Wait Gameplay Event]のタスクにて受け取りができます。

SendGameplayEventToActor.png

C++コードだと以下の様になります。

.cpp
#include "AbilitySystemBlueprintLibrary.h"

FGameplayEventData _Payload;
_Payload.Target = MyActor;
UAbilitySystemBlueprintLibrary::SendGameplayEventToActor(MyActor, FGameplayTag::RequestGameplayTag(FName("Event.Fall")), _Payload);

受け取りアビリティタスク
WaitGameplayEvent.png

複数のスケルタルメッシュを持つ場合のアニメ―ションモンタージュ再生先の切り替え方法

アクターが複数のスケルタルメッシュを持つ場合、UGameplayAbilityを継承したBPなどでPlayMontageなどを使ってアニメ―ションを再生する場合そのままだと1つのスケルタルメッシュに対してしか再生できません。
これはGameplayAbilityのアニメーションモンタージュ再生がAbilitySystemComponentが持つ情報で再生させているためです。

これを回避するには UGameplayAbilityクラスとUAbilitySystemComponentが持っているActorInfo情報(FGameplayAbilityActorInfo)を書き換える必要があります。

以下切り替えコード例です。

UMyPlayerGameplayAbility.h
UCLASS(Abstract)
class TEST_API UMyPlayerGameplayAbility : public UGameplayAbility
{
    GENERATED_BODY()

public:
    // アクター情報
    UPROPERTY()
    FGameplayAbilityActorInfo Sub_ActorInfo;

    FGameplayAbilityActorInfo* TmpCurrentActorInfo;
UMyPlayerGameplayAbility.cpp
    // 対象スケルタルメッシュ切り替えコード
    auto _ASC = MyPlayer->GetAbilitySystemComponent();
    auto _SMC_Main = MyPlayer->GetSkeletalMeshComponent();
    auto _SMC_Sub = MyPlayer->GetSubSkeletalMeshComponent();

    if( _Enable){
        // 対象を切り替え後のスケルタルメッシュにする
        TmpCurrentActorInfo = (FGameplayAbilityActorInfo*)GetCurrentActorInfo();

        Sub_ActorInfo = *GetCurrentActorInfo();
        Sub_ActorInfo.SkeletalMeshComponent = TWeakObjectPtr<USkeletalMeshComponent>(_SMC_Sub);

        SetCurrentActorInfo(CurrentSpecHandle, &Sub_ActorInfo);
        _ASC->AbilityActorInfo->SkeletalMeshComponent = TWeakObjectPtr<USkeletalMeshComponent>(_SMC_Sub);

    }else{
        // 対象を戻す
        _ASC->ClearActorInfo();
        _ASC->InitAbilityActorInfo(MyPlayer, MyPlayer);

        SetCurrentActorInfo(CurrentSpecHandle, (const FGameplayAbilityActorInfo*)TmpCurrentActorInfo);
    }

GameplayAbilityComponentとGameplayAbility両方のクラスに対して行っています。恐らくGameplayAbilityはインスタンスタイプ([Instancing Policy] が [Instanced Per Execution])でないとダメかと思います。

まとめ

サンプルプロジェクトARPGを読んでみましたがわからない処理が多いです。
GameplayAbilityプラグインが多機能のためいろいろな機能に目が行きがちなのですが、アトリビュートのパラメータ管理とアビリティのステート管理から導入すると良いと思います。

unknown_ds
都内在住。 コロナ禍はいつまで続くのでしょうかね。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした