UE4 C++ GameplayAbilitiesを勉強していくPart.4 GameplayAbility

やあ

今回から今まで散々出てきたGameplayAbilityについて勉強していくよ。

間違いなどあれば教えてくださると助かります!

前回

pto8913.hatenablog.com

GameplayAbilityについて

f:id:pto8913:20200601131126p:plain
・アクターがゲーム内でできるアクションやスキルのこと!
例えば、銃を撃つや魔法を使う、全力疾走など。
・複数のGameplayAbilityを一度にアクティブにすることができる!
・BlueprintでもC++でも作れる!

GameplayAbilityの処理の流れ

  1. Actor->AbilitySystemComponent->TryActivateAbility : 指定されたアビリティのコストと要件をチェックして起動できるかどうか判定する。
  2. GameplayAbility->CanActivateAbility : このアビリティがすぐに起動できるかチェック。
    true -> 3
    false -> 6
  3. GameplayAbility->ActivateAbility : 実際にアビリティを起動する。
  4. GameplayAbility->CommitAbility : GameplayEffectを適用する前の最後のチェック。コストやクールダウンをチェックする。
    true -> 5
    false -> 6
  5. GameplayAbility->ApplyGameplayEffect : GameplayEffectを適用
  6. GameplayAbility->EndAbility : OnEndAbilityを呼ぶ終了時の処理
  7. GameplayAbility->OnEndAbility : BPイベントグラフで書かれた終了時の処理を呼び出す。
  8. 終了。

実際には、任意の処理の間でAbility Taskを呼び出せるよ。

Tags

GameplayAbilityに付属する、ロジックが組み込まれたGamepalyTagContainers

GameplayTagContainer 説明
Ability Tags GameplayAbility自体が持つGameplayTags、これらのタグ自体が何かをするのではなくて、どのタグを持つかを説明するためにある。
Cancel Abilities With Tag このGameplayAbilityを起動したとき、これらのタグがAbility Tagsに含まれる他のGameplayAbilityはキャンセルされる。
Block Abilities with Tag このGameplayAbilityが実行中、これらのタグがAbility Tagsに含まれる他のGameplayAbilityの実行をブロックする。
Activation Owned Tags このGameplayAbilityが実行中、これらのタグをGameplayAbilityのオーナーに与えられます。
Activation Required Tags これらのGameplayTagsをこのGameplayAbilityのオーナーがすべて持っているなら起動できる。
Activation Blocked Tags これらのGameplayTagsをこのGameplayAbilityのオーナーがひとつも持っていないなら起動できる。
Source Required Tags (Activation Required Tagsと何が違うのかわかってません。)
Source Blocked Tags (Activation Blocked Tagsと何が違うのかわかってません。)
Target Required Tags (Activation Required Tagsと何が違うのかわかってません。)
Target Blocked Tags (Activation Blocked Tagsと何が違うのかわかってません。)

Input

よくわかってません。
Replicate Input Directory : 入力のPressReleaseイベントが常にサーバーに複製される。(?)

Advanced

Replication Policy

・よくわからない。
・将来的にこのオプションは削除されるらしい。

Instancing Policy

Instancing Policy 説明
Non-Instanced GameplayAbilityClassDefaultObject上で動作します。インスタンスは作成されません。
Instanced Per Actor 各アクターがこのGameplayAbilityインスタンスを一つ持つ。
Instanced Per Execution このGameplayAbilityが起動するたびに、新しいインスタンスが作成される。

Server Respects Remote Ability Cancellation

よくわからないけど、無効にしておいたほうがいいらしい。

Retrigger Instanced Ability

もしtrueの場合、すでにアクティブになっているインスタンスのアビリティを起動しようとしている場合は、それを終了して再起動します。

Net Execute Policy

Net Execute Policy 説明
Local Predicted 最初にクライアントで起動し、次にサーバーで起動する。
Local Only クライアント上でのみ起動される。マルチプレイヤーゲームで使用する。
Server Initiated 最初にサーバーで起動し、次にクライアントで起動する。
Server Only サーバー上でのみ起動される。シングルプレイヤーで使用する。

Net Security Policy

Net Security Policy 説明
Client Or Server セキュリティなし。クライアントまたはサーバーは、この能力の実行と終了を自由にトリガーすることができます。
Server Only Execution クライアントからのアビリティ実行のリクエストは無視される。キャンセルまたは終了のリクエストのみ認められる。
Server Only Termination クライアントからのアビリティ実行のリクエストのみ認められる。
Server Only サーバーでのみ処理される。クライアントからのリクエストはすべて無視される。

GameplayAbilityをAbilitySystemComponentに登録する

登録されたGameplayAbilityは自由に実行できるよ

実際にGameplayAbilityを登録してみる

このあたりの詳細な説明はPart.1でやっているので、Part.1を読んだ方は飛ばしてね。
また、読んでいない方もPart.1から見ることをおすすめするよ。

AbilitySystemComponentについて

GameplayAbilitySystemを使う上で、なくてはならないコンポーネント!
GameplayAbilitiesを使用したり、Attributesを持っていたり、GameplayEffectを受け取ったりしたいアクターは必ず、AbilitySystemComponentを持っていないといけないよ。
・これを使うアクターはAbilitySystemInteracterを継承する必要があるよ。

AbilitySystemComponentを持ったキャラクターの作成

CharacterBase.hに追加

// Copyright(C)write by pto8913. 2020. All Rights Reserved.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "AbilitySystemInterface.h"
#include "CharacterBase.generated.h"

class UAbilitySystemComponent;

UCLASS()
class ABILITYTEST_API ACharacterBase 
    : public ACharacter, public IAbilitySystemInterface
{
    GENERATED_UCLASS_BODY()
public:
    // Begin Init
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Ability")
        UAbilitySystemComponent* AbilitySystemComp;
    /* Implement IAbilitySystemInterface */
    UAbilitySystemComponent* GetAbilitySystemComponent() const override;
    // End Init
};

CharacterBase.cppに追加

// Copyright(C)write by pto8913. 2020. All Rights Reserved.

#include "Abilities/Characters/Bases/CharacterBase.h"

#include "AbilitySystemComponent.h"

/////////////////////////////////////////
// Init

ACharacterBase::ACharacterBase(const FObjectInitializer& ObjectInitializer)
    : Super(ObjectInitializer)
{
    AbilitySystemComp = CreateDefaultSubobject<UAbilitySystemComponent>(TEXT("AbilityComp"));
}

UAbilitySystemComponent* ACharacterBase::GetAbilitySystemComponent() const
{
    return AbilitySystemComp;
}

アビリティをキャラクターに持たせる

どのアビリティを持たせるかは、Blueprint上で設定する。

CharacterBase.hに追加

class UGameplayAbility;

public:
    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Ability")
        TArray<TSubclassOf<UGameplayAbility>> GameplayAbilities;

f:id:pto8913:20200601205141p:plain

AbillitySystemComponentに登録する

CharacterBase.hに追加

public:
    virtual void PossessedBy(AController* NewController) override;
    void AddStartupGameplayAbilities();
protected:
    UPROPERTY(EditAnywhere, Replicated, Category = "Ability")
        int32 ExistsLevel = 1;

    UPROPERTY()
        int32 bAbilitiesInitialzied;

CharacterBase.cppに追加

void ACharacterBase::PossessedBy(AController* NewController)
{
    Super::PossessedBy(NewController);

    if (IsValid(AbilitySystemComp) == true)
    {
        AbilitySystemComp->InitAbilityActorInfo(this, this);
        AddStartupGameplayAbilities();
    }
}

void ACharacterBase::AddStartupGameplayAbilities()
{
    if (IsValid(AbilitySystemComp) == true)
    {
        for (TSubclassOf<UGameplayAbility>& StartupAbility : GameplayAbilities)
        {
            AbilitySystemComp->GiveAbility(FGameplayAbilitySpec(StartupAbility, ExistsLevel, INDEX_NONE, this));
        }

        bAbilitiesInitialized = true;
    }
}

登録したGameplayAbilityを実行する方法

アビリティを実行する方法は、4つあります。
GameplayTagを使う方法。
GameplayAbilityClassを使う方法。
GameplayAbilitySpecHandleを使う方法。(C++)
・イベント。
イベントによってアビリティを実行するとイベントと一緒にたくさんの情報を渡すことができる!

GameplayTagを使って実行してみる

もしかしたらPart2~3.5を読んでいない方がいるかもしれないので、
GameplayAbilityを呼び出したら画面に文字を表示するだけにするよ。

適当なGameplayAbilityを作成(GA_CalledByTag)

f:id:pto8913:20200601211722p:plain

AbilitySystemComponentに登録しないで実行してみる

詳細タブのGameplayAbilitiesGA_CalledByTagを設定していないね。
f:id:pto8913:20200601210126p:plain

登録していない状態でのテスト(GameplayTag)

f:id:pto8913:20200601210404g:plain
アビリティを実行しようとしても、AbilitySystemComponentに登録していないから実行できないね。

登録してテスト(GameplayTag)

f:id:pto8913:20200601210834p:plain
f:id:pto8913:20200601212149g:plain
できてるね。
アビリティを登録することの大事さが分かったかな。
(ところでなぜ二回呼ばれているのでしょうか...)

GameplayAbilityClassを使って実行してみる

・一番使われるんじゃないかな?
知らんけど。
・もしかしたらPart2~3.5を読んでいない方がいるかもしれないので、
GameplayAbilityを呼び出したら画面に文字を表示するだけにするよ。

適当なGameplayAbilityを作成

f:id:pto8913:20200601211824p:plain

GameplayAbilityの登録

f:id:pto8913:20200601211451p:plain

テスト(GameplayAbilityClass)

f:id:pto8913:20200601212317g:plain

GameplayAbilitySpecHandleを使って実行してみる

GameplayAbilityAbilitySystemComponentに登録するときに、そのアクターに適用したいバフ(HPリジェネとか)があるときに使うのがよさそう。
・もしかしたらPart2~3.5を読んでいない方がいるかもしれないので、
GameplayAbilityを呼び出したら画面に文字を表示するだけにするよ。

CharacterBase.hに追加

public:
    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Ability")
        TArray<TSubclassOf<UGameplayAbility>> PassiveGameplayAbilities;

CharacterBase.cppに追加

void ACharacterBase::AddStartupGameplayAbilities()
{
    if (IsValid(AbilitySystemComp) == true)
    {
        for (TSubclassOf<UGameplayAbility>& PassiveAbility : PassiveGameplayAbilities)
        {
            FGameplayAbilitySpec Spec = FGameplayAbilitySpec(PassiveAbility, ExistsLevel, INDEX_NONE, this);
            AbilitySystemComp->GiveAbilityAndActivateOnce(Spec);
        }
        bAbilitiesInitialized = true;
    }
}

適当なGameplayAbilityを作成

f:id:pto8913:20200602095701p:plain

GameplayAbilityの登録

f:id:pto8913:20200602095827p:plain

テスト(GameplayAbilitySpecHandle)

f:id:pto8913:20200602095929p:plain
ゲーム開始時に文字が表示されてるね。

イベントで実行してみる

AbilitySystemBlueprintLibrary::SendGameplayEventToActor(AActor* Actor, FGameplayTag EventTag, FGameplayEventData Payload)を使う。
BPからでもC++からでも使えるよ。
注意:アクターはIAbilitySystemInterfaceを実装している必要があります。

今回は、敵にプレイヤーが敵にぶつかったら、ぶつかった敵の名前を表示するだけにするよ。

BPから使う場合

敵を作る

CharacterBaseのサブクラスBP_Enemyを作成。
f:id:pto8913:20200602101526p:plain

とくにやることはないよ。

GameplayEventを受け取るGameplayAbility(GA_GetEventData)を作成

f:id:pto8913:20200602102421p:plain
赤枠のタグでイベントを受け取るかを決めるので、超大事。

最初の呼び出し

これであってるのかわからない。。
f:id:pto8913:20200602101926p:plain

ヒット時にイベントデータを送る

f:id:pto8913:20200602102249p:plain
赤枠のタグでイベントを送るので、受け取り側と同じにする必要があるよ。超大事

テスト(イベント、BP版)

f:id:pto8913:20200602102728g:plain
ちょっと見づらいけどうまくいってるね。

C++から使う方法

について書こうと思ったんだけど、C++で同じ処理を書いても呼び出されないから諦めたよ。
分かる方いたら教えてください。

今回はここまで、ありがとうございました。

次回

pto8913.hatenablog.com