やあ
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
について調べてみます。