UE4 C++ GameplayAbilitiesを勉強していくPart.1
やあ
GameplayAbilitiesを使おうと思い立って、ActionRPGサンプルのコードを見てたら頭が痛くなったので、小さいところから徐々に勉強していくことにしたよ
完全に理解して、ActionRPGを解説できるようになるぞ!()

間違ってることはコメントで教えてください!
セットアップ


GameplayAbilitiesモジュールがビルドされるようにする。
xx.build.csに追加
// Copyright Epic Games, Inc. All Rights Reserved. using UnrealBuildTool; public class AbilityTest : ModuleRules { public AbilityTest(ReadOnlyTargetRules Target) : base(Target) { PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "HeadMountedDisplay", "GameplayAbilities", "GameplayTasks", "GameplayTags", }); } }
追加したら、(必要ないかもだけど).uprojectを右クリックして、Generated Visual Studio Project Files

キャラクターを作る

AbilitySystemComponentについて
・GameplayAbilitySystemを使う上で、なくてはならないコンポーネント!
・GameplayAbilitiesを使用したり、Attributesを持っていたり、GameplayEffectを受け取ったりしたいアクターは必ず、AbilitySystemComponentを持っていないといけないよ。
・これを使うアクターはAbilitySystemInteracterを継承する必要があるよ。
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")); AbilitySystemComp->ReplicationMode = EGameplayEffectReplicationMode::Full; } UAbilitySystemComponent* ACharacterBase::GetAbilitySystemComponent() const { return AbilitySystemComp; }
AbilitySystemComp->ReplicationMode = EGameplayEffectReplicationMode::Full;
これは、(多分)シングルプレイヤー用で、すべてのGameplayEffectをクライアントに複製するってこと。
初期化
AbilitySystemComponentはPawnがコントローラに所有された後に、初期化します。
PossessedByを使います。これはコントローラーが所有されたよ!って教えてくれるものです。
CharacterBase.hに追加
public: virtual void PossessedBy(AController* NewController) override;
CharacterBase.cppに追加
void ACharacterBase::PossessedBy(AController* NewController) { Super::PossessedBy(NewController); if (IsValid(AbilitySystemComp) == true) { AbilitySystemComp->InitAbilityActorInfo(this, this); } }
上のように書いたけど、環境次第でPawnの生成後にコントローラーの所有処理が起こることもあるようなので、OnRep_ControllerでActorInfoを更新するよ。
※ OnRep_ControllerについてはPawn及びControllerクラスを参照してください。
CharacterBase.hに追加
public: virtual void OnRep_Controller() override;
CharacterBase.cppに追加
void ACharacterBase::OnRep_Controller() { Super::OnRep_Controller(); if (IsValid(AbilitySystemComp) == true) { AbilitySystemComp->RefreshAbilityActorInfo(); } }
アビリティをもたせる
GameplayAbilityについて
・アクターがゲーム内でできるアクションやスキルのこと!
例えば、銃を撃つ、全力疾走など、複数のGameplayAbilityを一度に使用できる!
CharacterBase.hに追加
class UGameplayAbility; public: UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Ability") TArray<TSubclassOf<UGameplayAbility>> GameplayAbilities;
初期化
AbilitySystemComponentと同じタイミングでやるよ。
特に深い意味はないんだけど、処理の流れとしてActorInfoの登録->初期化のほうが自然な気がするからね。
CharacterBase.hに追加
public: UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Ability") TArray<TSubclassOf<UGameplayAbility>> GameplayAbilities; 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; } }
ここまでの処理を試してみる
TPテンプレートで自動的に作られるキャラクターの親クラスをCharacterBaseに変更。
AbilityTestCharacter.h
#include "Abilities/Characters/Bases/CharacterBase.h" class AAbilityTestCharacter : public ACharacterBase { };
ビルドしてエディタに移動。
※もしかしたらここで、GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps)のリンカエラーが出るかもしれません。一度クリーンして、再ビルドしてみてください。それでもエラーが出る場合はコードを追加してください。
適当にアビリティをつくる



キャラクターにアビリティを設定する


とりあえずプレイ。想定通りの動作をしているか確認。
今のところ1を押したらBegin Jumpが表示され、3秒後にEnd Jumpが表示されます。


表示された!
次に、アニメを再生するよー。
Jump_Startからアニムモンタージュを作成。

これだけだと短すぎてわからないので、Jump_LoopとJump_Endを追加

ThirdPerson_AnimBPのアニムグラフに追加。

GA_Jumpからモンタージュを実行する。

いざプレイ。

うまくいってるけど、キーを連打すると、最初から再生され、してほしい動作と違った動作になるね。
ここで役に立つのがGameplayTags!
GameplayTagsについて
GameplayTagsを使うことで、動作を制限できます。
GameplayTagsの設定
GameplayTagsの設定は、
GameplayAbilityクラスの子クラス

プロジェクト設定

DefaultGameplayTags.iniから直接設定

主に使用されるGameplayTagsの詳細
・Ability Tags : このアビリティがもつタグ
・Block Abilities with Tag : このアビリティがアクティブな間、これらのタグを持つアビリティがアクティブにならないようブロックする。
実際に設定する

.でつなぐことで自動的に階層ができるよ。

これでGA_JumpのAbility TagsにAction.Movement.Jumpタグが設定されたね。
このままでは、キーを連打した際の挙動は改善しないよ。
だから、Block Abilities with TagにAction.Movementを追加。

これで、ダイジョーブ!
GameplayTags設定の際の注意
Block Abilities with TagにAction.Movementを追加するのは、Action.Movement階層下にある、どのタグを持ったアビリティがアクティブでも、アビリティをブロックするようになるからだよ。
例えば、以下のようなGameplayTagsを設定しているとします。

全部ブロックするのに、一つずつGameplayTagsを設定するのはとんでもない労力が掛かるね。

後からAction.Movement下に、新しいGameplayTagsを追加したときも、さらに追加して...みたいなことになるね。
そのため、Action.MovementをBlock Abilities with Tagに追加するよ。
もちろんActionを追加してもいいよ。
これは各々の実装次第だね。

確認する

gifだとわかりにくいね。
実際に手元で動かしてみてー。
キーを連打しても、アビリティが終わるまで、次のアビリティがアクティブになることはないはずだよー。
今回はここまで、ありがとうございました。
聞きたいこと
わかる人がいたら教えていただきたいのですが、下記のような認識であっているのでしょうか?
サーバー上でダメージの計算や、エフェクトの発生、サウンドエフェクトを行い、それらの結果をクライアントに複製して実行する。
その過程で、キャンセルやブロック処理が働くことで、複製を阻止することもできる。
(と思っているだけで実際は全然違うのかも)
次回
AttributeSetについて調べてみます。