概要
UnrealEngine のプラグイン GameplayAbility についてのメモです。
多機能なため、まとまっていません。
修正履歴
日付 | 内容 |
---|---|
2019/11/06 | GameplayAbilitySpecについて追記、GameplayEffectのカーブテーブルについて追記 |
2019/11/23 | GameplayEffect の記述を追加 |
2019/12/13 | スケルタルメッシュ切り替えについて追記 |
2021/09/06 | アビリティタグ追記 |
2024/10/04 | GameplayAbilityメソッドについて追記 |
2024/11/07 | Retrigger Instanced Ability について追記 |
環境
Windows10
Visual Studio 2017
UnrealEngine 4.22, 4.26, 5.3
参考
以下を参考にさせて頂きました、ありがとうございます。
GameplayAbilitiesの使い方
GameplayAbility - GameplayAbility と コンポーネント の準備編
UnrealEngineマニュアル-ゲームプレイアビリティシステム
UnrealEngineマニュアル-ActionRPG のゲームプレイアビリティ
GASDocumentation
GameplayAbilityBP
AbilityTag
アビリティ同士の排他処理や条件管理ができます。
このAbilityTagは AbilitySystemComponentクラスにて管理されています。
- Ability Tags - アビリティ自身が持っているタグ
- Cancel Abilities with Tag - このタグを持っている実行中のアビリティを中止させる
- Block Abilities with Tag - このタグを持っているアビリティを実行させない
- Activation Owned Tags - アビリティが有効な時のみ持つタグ
- Activation Required Tags - 全てのタグを持っていないとアビリティを有効にしない
- Activation Blocked Tags - いずれかのタグを持っていた場合、アビリティを有効にしない。
AbilityTagでの制御について追記1
以下のメソッドで、BPにて設定されたAbilityTagのキャンセル/ブロック等の機能を有効/無効に設定ができるようです。
インスタンス化されたアビリティのみ有効です。
// ブロックタグセットを有効/無効に設定
virtual void SetShouldBlockOtherAbilities(bool bShouldBlockAbilities);
// キャンセルタグセットを有効/無効に設定
virtual void SetCanBeCanceled(bool bCanBeCanceled);
AbilityTagでの制御について追記2
キャンセル/ブロックタグの途中での追加で制御するには以下のメソッドで可能です。
/ **
*アビリティアクティベーションまたはネイティブコードから呼び出され、正しいアビリティブロックタグを適用し、既存のアビリティをキャンセルします。 サブクラスは動作をオーバーライドできます
* @paramAbilityTagsブロックフラグとキャンセルフラグを持つアビリティのタグ
* @param RequestingAbility変更を要求するゲームプレイアビリティは、ネイティブイベントの場合はNULLにすることができます
* @param bEnableBlockTags trueの場合はブロックタグが有効になり、falseの場合はブロックタグが無効になります
* @paramBlockTagsブロックするタグ
* @param bExecuteCancelTags trueの場合、タグに一致するアビリティをキャンセルします
* @paramCancelTagsキャンセルするタグ
* /
virtual void ApplyAbilityBlockAndCancelTags(const FGameplayTagContainer& AbilityTags, UGameplayAbility* RequestingAbility, bool bEnableBlockTags, const FGameplayTagContainer& BlockTags, bool bExecuteCancelTags, const FGameplayTagContainer& CancelTags);
UAbilitySystemComponent::ApplyAbilityBlockAndCancelTags(AbilityTags, this, true, BlockAbilitiesWithTag, true, CancelAbilitiesWithTag);
AbilityTagでの制御について追記3
自身のアビリティ起動中に再度アクティブにして再度動作させる(起動中のものは終了させる)には [Instancing Policy] が [Instanced Per Execution](起動させる毎)時はキャンセルタグに自身のタグを設定することで可能ですが、 [Instancing Policy] が [Instanced Per Actor](アクター生成時)時の場合は [Retrigger Instanced Ability]にチェックを入れる必要があります。
前述のキャンセルタグを使った方法は開始/終了タイミングが違う(後のアビリティの開始後に先のアビリティの終了がくる)ので注意が必要です。
コストやクールダウンの設定
Costs や Cooldowns に GameplayEffectのBPを設定できます。
Costs は DurationPolicy を Instant で必要なコストをアトリビュート経由で計算させます。
Cooldowns は DurationPolicy を HasDuration で時間設定と GrantedTags にブロックするタグを追加する。
GamePlay Effect の Executions
[GamePlay Effect] の [Executions] -> [Calculation Class] を設定すると計算用のクラス(UGameplayEffectExecutionCalculationを継承したクラス)を使うことができます。
UGameplayAbilityクラス
アビリティ情報のもとになるクラス。
継承可能なメソッドについての記載します。
CanActivateAbility
アビリティが実行可能かを調べるメソッドです。
アビリティ実行前に呼ばれる事を想定されているため、アビリティのプロパティ[Advanced-Instancing Policy]が[Instanced Per Execution]の場合はインスタンス化されていないため、コンストラクタ等で設定したメンバ変数の値になっていないので要注意。
これを回避するには[Instanced Per Actor]にする等の対応が必要。
CommitAbility
アビリティのコストやクールダウン情報をコミットする処理。
CostやCooldownを独自に実装する場合は、CommitAbility
も継承して書き換える必要がある場合があるので注意。
PreActivate
アビリティの実行前処理。TryActivateAbilityが実行成功すると直ちに呼ばれる?
ActivateAbility
アビリティ実行処理。
アビリティ開始時に即実行される処理のため、通信同期に必要なデータを送る場合はこれを継承して送る必要あり。
EndAbility
アビリティの終了、BP内などで呼ぶ。
CancelAbility
外部からのアビリティの中断時に呼ばれる。
CheckCooldown
アビリティ使用のためのクールダウン時間が明けているかのチェック関数
通常はAbilityでGameplayEffectを設定して使う。
必要なら継承して特殊な計算を行うことも可能。
その場合は、CheckCooldown
とApplyCooldown
を両方継承して計算を行う。
ApplyCooldown
CommitAbility で呼ばれる。
クールダウン情報(gameplayEffect)の適用
必要なら継承して特殊な計算を行うことも可能。
その場合は、CheckCooldown
とApplyCooldown
を両方継承して計算を行う。
CheckCost
アビリティ使用のためのコストが足りているかのチェック関数
通常はAbilityでGameplayEffectを設定して使う。
必要なら継承して特殊な計算を行うことも可能。
その場合は、CheckCost
とApplayCost
を両方継承する、以下コード例。
// コストチェック(コストが払えるならtrue)
bool UMyGameplayAbility::CheckCost(const FGameplayAbilitySpecHandle _Handle, const FGameplayAbilityActorInfo* _ActorInfo, OUT FGameplayTagContainer* _OptionalRelevantTags) const
{
const auto _ASC = Cast<UMyAbilitySystemComponent>(_ActorInfo->AbilitySystemComponent);
return( (_ASC->GetMyCostNum() > 0)?true:false );
}
ApplyCost
CommitAbility で呼ばれる。
コスト情報(gameplayEffect)の適用
必要なら継承して特殊な計算を行うことも可能。
その場合は、CheckCost
とApplayCost
を両方継承する、以下コード例。
// コスト適用
void UMyGameplayAbility::ApplyCost(const FGameplayAbilitySpecHandle _Handle, const FGameplayAbilityActorInfo* _ActorInfo, const FGameplayAbilityActivationInfo _ActivationInfo) const
{
const auto _ASC = Cast<UMyAbilitySystemComponent>(_ActorInfo->AbilitySystemComponent);
_ASC->SetMyCostNum( _ASC->GetMyCostNum()-1 );
}
アビリティシステムコンポーネントクラス(AbilitySystemComponent)
対象のアクターが所持する必要があるコンポーネントです。
このコンポーネントが各アビリティを管理しており、FGameplayTagContainerクラスではなく、カウントできるように拡張したFGameplayTagCountContainerでタグを管理しているようです。
FGameplayTagCountContainerクラスは、
"Engine\Plugins\Runtime\GameplayAbilities\Source\GameplayAbilities\Public\GameplayEffectTypes.h"
に定義されています。
コンポーネント有効化/無効化時のイベントをC++で登録する
ActorComponentを継承しているコンポーネントクラスは皆一緒です。
virtual void InitializeComponent() override;
// コンポーネントが有効化時に呼ばれる処理
UFUNCTION()
void OnActivated(UActorComponent* Component, bool bReset);
// コンポーネント無効化時に呼ばれる処理
UFUNCTION()
void OnDeactivated(UActorComponent* Component);
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]は指定した時間中のみとなります。
GameplayEffect適用時のAttributeSetのメソッド呼び出しについて
ApplyGameplayEffectToOwnerにて GameplayEffectを適用した場合、DurationPolicy が [Infinite]か[HasDuration] の時は UAttributeSet::PostGameplayEffectExecute() が呼ばれない?
GameplayEffect 適用例(Infinite)
[Duration Policy]を[Infinite]にして、任意のタイミングで[RemoveGameplayEffectFromOwnerWith...]で適用を外すと適用していたGameplayEffectでの[Modifiers]での値変更がリセットされ、UPlayerAttributeSet::PreAttributeChange()で値変更のメソッドが呼ばれます。
この時のアトリビュートで受け取るリセット値は 0.0f 固定となるようです。
GameplayEffect 適用例(Has Duration)
[Duration Policy]を[Has Duration]にして、適用時間を決めるとゲームプレイエフェクトが適用終了時に以下の様にタスクで受け取ることができます。
[On Removed]は適用時間の終了時、[Invalid Handle]は恐らく外部から消された時だと思われます。
Modifiersでのカーブテーブル適用について
適用時の[Gameplay Effect Level]を変更し、カーブテーブルを適用することによってパラメータを変えることができます。
以下はカーブテーブルの元になるjsonの例です。レベル1~3まで定義しています。
[
{
"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の情報へアクセスするコード例です。
// 指定のタグが付いたアビリティを調べる
FGameplayTagContainer _Container;
_Container.AddTag(FGameplayTag::RequestGameplayTag(FName("Ability.Test")));
TArray<FGameplayAbilitySpec*> AbilitiesSpcs;
GetActivatableGameplayAbilitySpecsByAllMatchingTags(_Container, AbilitiesSpcs);
for (FGameplayAbilitySpec* _Spec : AbilitiesSpcs) {
// アビリティレベルを2へ変更
_Spec->Level = 2;
}
指定したアビリティレベルは GameplayAbilityグラフで使えます。
アビリティタスク(AbilityTask)
アビリティに対し、任意のタスクをつくることができます。
BPでは以下の様にいろいろでてきます。
[WaitDelay]や[WaitGameplayEvent]あたりをよく使います。
アビリティタスクの作成
任意のアクターのメソッドを監視するアビリティタスクの作成例です。
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() というメソッドから終了を監視します。
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();
}
ゲームプレイキュー(GameplayCue)
アトリビュートの変化時などにエフェクトやサウンドなどを発生させる時などに使います。
作成例
サウンドを再生するゲームプレイキューを作成して、アビリティから呼び出しをしてみます。
-
作成したBPのBeginイベントにサウンドをスポーンするようにBPを組みます。なお自身はDestoryActorで始末します。また、[GameplayCueTag]にタグを設定します。
- アビリティから AddGameplayCueToOwnerで呼び出します、この時の GameplayCueTag はゲームプレイキューで設定したタグです。
これでアビリティが実行されるたびに、ゲームプレイキューが呼び出されます。
ゲームプレイエフェクトの[Display]に対応したGameplayCueTagを設定して呼び出す方法もあるようです。
その他
アビリティに対してイベントを発行する
アクターのアビリティに対し、イベントを発行します。アビリティ側は[Wait Gameplay Event]のタスクにて受け取りができます。
C++コードだと以下の様になります。
#include "AbilitySystemBlueprintLibrary.h"
FGameplayEventData _Payload;
_Payload.Target = MyActor;
UAbilitySystemBlueprintLibrary::SendGameplayEventToActor(MyActor, FGameplayTag::RequestGameplayTag(FName("Event.Fall")), _Payload);
複数のスケルタルメッシュを持つ場合のアニメ―ションモンタージュ再生先の切り替え方法
アクターが複数のスケルタルメッシュを持つ場合、UGameplayAbilityを継承したBPなどでPlayMontageなどを使ってアニメ―ションを再生する場合そのままだと1つのスケルタルメッシュに対してしか再生できません。
これはGameplayAbilityのアニメーションモンタージュ再生がAbilitySystemComponentが持つ情報で再生させているためです。
これを回避するには UGameplayAbilityクラスとUAbilitySystemComponentが持っているActorInfo情報(FGameplayAbilityActorInfo)を書き換える必要があります。
以下切り替えコード例です。
UCLASS(Abstract)
class TEST_API UMyPlayerGameplayAbility : public UGameplayAbility
{
GENERATED_BODY()
public:
// アクター情報
UPROPERTY()
FGameplayAbilityActorInfo Sub_ActorInfo;
FGameplayAbilityActorInfo* TmpCurrentActorInfo;
// 対象スケルタルメッシュ切り替えコード
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プラグインが多機能のためいろいろな機能に目が行きがちなのですが、アトリビュートのパラメータ管理とアビリティのステート管理から導入すると良いと思います。