UE4 C++ GameplayAbilitiesを勉強していくPart.5 AbilityTasks
やあ
今回は、AbilityTasks
について勉強するよ。
間違いなどあれば教えてくださると助かります!
前回
AbilityTasksについて
すでに何回もでてきてるよ。
こんなのや
こんなの。
・GameplayAbility
は1フレームでしか実行されないので、AblilityTask
を使って、時間の経過で発生するアクションを実行したりして、いろんな状況に対処するよ。
たくさんのAbilityTask
があるので自分で見てみてね。
注意 : AbilityTask
はコンストラクタで、UAbilityTask
のコンストラクタで、ゲーム全体で同時に1000個までとハードコードされているので注意。
AbilityTaskを自分で作る
よく使う関数
・AbilityTask
の新しいインスタンスを返す静的関数
これだね。
・Activate
タスクを実行する。
・OnDestroy
クリーンアップのための関数。
・コールバック関数
注意 : コールバック関数に使うデリゲートは1つしか宣言できないです。
デリゲートって?
タスクの作成
MyAT_Test.h
に追加
// Copyright(C)write by pto8913. 2020. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "Abilities/Tasks/AbilityTask.h" #include "ptoAT_Test.generated.h" UCLASS() class ABILITYTEST_API UptoAT_Test : public UAbilityTask { GENERATED_BODY() public: UptoAT_Test(); /* @ OwningAbilit : このタスクを呼び出しているGameplayAbility @ TaskInstanceName : この関数によって返されるインスタンスの名前。なくてもいい。 */ UFUNCTION(BlueprintCallable, Category = "Ability|pto|Tasks", meta = (HidePin = "OwningAbility", DefaultToSelf = "OwningAbility", BlueprintInternalUseOnly = "TRUE")) static UptoAT_Test* ptoTest( UGameplayAbility* OwningAbility, FName TaskInstanceName ); };
MyAT_Test.cpp
に追加
// Copyright(C)write by pto8913. 2020. All Rights Reserved. #include "Abilities/Tasks/ptoAT_Test.h" UptoAT_Test::UptoAT_Test() { } UptoAT_Test* UptoAT_Test::ptoTest( UGameplayAbility* OwningAbility, FName TaskInstanceName ) { UptoAT_Test* MyObj = NewAbilityTask<UptoAT_Test>( OwningAbility, TaskInstanceName ); return MyObj; }
これで、タスクをGameplayAbility
のイベントグラフで呼び出せるようになったよ。
えぇ!?こんなに簡単に!?
GameplayAbilityについてはPart.4を見てね
適当なGameplayAbility
から確認してみてね。
アニメーションモンタージュを再生できるようにする
タスクを実行できるようにする
MyAT_Test.h
に追加
public: /* タスクを実行する */ virtual void Activate() override;
MyAT_Test.cpp
に追加
#include "Engine.h" void UptoAT_Test::Activate() { GEngine->AddOnScreenDebugMessage(-1, 10.f, FColor::Green, "Activate in ptoAT_Test"); }
タスクが実行できているかテスト
アビリティを登録して、実行できるようにする。
タスクの実行はできてるね。
再生するアニメーションモンタージュを登録できるようにする
MyAT_Test.h
に追加
public: /* @ OwningAbility : A GameplayAbility that calls this task @ TaskInstanceName : The Instance name return by this func. need not be @ MontageToPlay : Animation Montage */ UFUNCTION(BlueprintCallable, Category = "Ability|pto|Tasks", meta = (HidePin = "OwningAbility", DefaultToSelf = "OwningAbility", BlueprintInternalUseOnly = "TRUE")) static UptoAT_Test* ptoTest( UGameplayAbility* OwningAbility, FName TaskInstanceName, UAnimMontage* MontageToPlay, float Rate = 1.f, FName StartSectionName = NAME_None ); private: UPROPERTY() UAnimMontage* MontageToPlay; UPROPERTY() float Rate; UPROPERTY() FName StartSectionName;
MyAT_Test.cpp
に追加
UptoAT_Test::UptoAT_Test() { Rate = 1.f; } UptoAT_Test* UptoAT_Test::ptoTest( UGameplayAbility* OwningAbility, FName TaskInstanceName, UAnimMontage* MontageToPlay, float Rate, FName StartSectionName ) { UptoAT_Test* MyObj = NewAbilityTask<UptoAT_Test>( OwningAbility, TaskInstanceName ); MyObj->MontageToPlay = MontageToPlay; MyObj->Rate = Rate; MyObj->StartSectionName = StartSectionName; return MyObj; }
アニメーションモンタージュを再生する
MyAT_Test.cpp
に追加
void UptoAT_Test::Activate() { GEngine->AddOnScreenDebugMessage(-1, 10.f, FColor::Green, "Activate in ptoAT_Test"); if (!Ability) return; if (!AbilitySystemComponent) return; bool bPlayedMontage = false; const FGameplayAbilityActorInfo* ActorInfo = Ability->GetCurrentActorInfo(); UAnimInstance* AnimInstance = ActorInfo->GetAnimInstance(); if (AnimInstance) { AbilitySystemComponent->PlayMontage( Ability, Ability->GetCurrentActivationInfo(), MontageToPlay, Rate, StartSectionName ); } }
テストする
使用するモンタージュは、ThirdPersonCharacter
のジャンプの一連の動作をまとめたものだよ。
おーけー
コールバック関数を作ってみる
モンタージュが通常終了したときの処理
MyAT_Test.h
に追加
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FptoTestDelegate); class HOGE_API UMy_AT_Test : public UAbilityTest { public: UPROPERTY(BlueprintAssignable) FptoTestDelegate OnCompleted; private: void OnMontageEnd(UAnimMontage* Montage, bool bInterrupted); };
MyAT_Test.cpp
に追加
void UptoAT_Test::OnMontageEnd(UAnimMontage* Montage, bool bInterrupted) { if (!bInterrupted) { if (ShouldBroadcastAbilityTaskDelegates()) { OnCompleted.Broadcast(); } } EndTask(); }
とりあえずうまくいっているか確認するよ。
OnCompleted
が追加されてるね。
ただ、いまのところ追加されただけで、バインドもしていないし、呼び出しもしてないから、何も起こらないよ。
モンタージュの終わりにバインドする
MyAT_Test.cpp
に追加
void UptoAT_Test::Activate() { GEngine->AddOnScreenDebugMessage(-1, 10.f, FColor::Green, "Activate in ptoAT_Test"); if (!Ability) return; if (!AbilitySystemComponent) return; const FGameplayAbilityActorInfo* ActorInfo = Ability->GetCurrentActorInfo(); UAnimInstance* AnimInstance = ActorInfo->GetAnimInstance(); if (AnimInstance) { AbilitySystemComponent->PlayMontage( Ability, Ability->GetCurrentActivationInfo(), MontageToPlay, Rate, StartSectionName ); MontageEndDelegate.BindUObject( this, &UptoAT_Test::OnMontageEnd ); AnimInstance->Montage_SetEndDelegate(MontageEndDelegate, MontageToPlay); } }
テスト
いぇい
アビリティがキャンセルされたときの処理
MyAT_Test.h
に追加
public: virtual void ExternalCancel() override; UPROPERTY(BlueprintAssignable) FptoTestDelegate OnCancelled; private: void OnAbilityCancelled(); bool StopPlayingMontage(); FDelegateHandle CancelHandle;
MyAT_Test.cpp
に追加
void UptoAT_Test::Activate() { GEngine->AddOnScreenDebugMessage(-1, 10.f, FColor::Green, "Activate in ptoAT_Test"); if (!Ability) return; if (!AbilitySystemComponent) return; const FGameplayAbilityActorInfo* ActorInfo = Ability->GetCurrentActorInfo(); UAnimInstance* AnimInstance = ActorInfo->GetAnimInstance(); if (AnimInstance) { AbilitySystemComponent->PlayMontage( Ability, Ability->GetCurrentActivationInfo(), MontageToPlay, Rate, StartSectionName ); CancelHandle = Ability->OnGameplayAbilityCancelled.AddUObject( this, &UptoAT_Test::OnAbilityCancelled ); MontageEndDelegate.BindUObject( this, &UptoAT_Test::OnMontageEnd ); AnimInstance->Montage_SetEndDelegate(MontageEndDelegate, MontageToPlay); } } void UptoAT_Test::ExternalCancel() { check(AbilitySystemComponent); OnAbilityCancelled(); Super::ExternalCancel(); } void UptoAT_Test::OnAbilityCancelled() { if (StopPlayingMontage()) { if (ShouldBroadcastAbilityTaskDelegates()) { OnCancelled.Broadcast(); } } } bool UptoAT_Test::StopPlayingMontage() { const FGameplayAbilityActorInfo* ActorInfo = Ability->GetCurrentActorInfo(); if (!ActorInfo) return false; UAnimInstance* AnimInstance = ActorInfo->GetAnimInstance(); if (!AnimInstance) return false; if (AbilitySystemComponent && Ability) { if ((AbilitySystemComponent->GetAnimatingAbility() == Ability) && (AbilitySystemComponent->GetCurrentMontage() == MontageToPlay) ) { FAnimMontageInstance* MontageInst = AnimInstance->GetActiveInstanceForMontage(MontageToPlay); if (MontageInst) { MontageInst->OnMontageEnded.Unbind(); } AbilitySystemComponent->CurrentMontageStop(); return true; } } return false; }
テスト
めんどうだったからタスクを呼び出した後すぐアビリティをキャンセルするようにしたよ。
おーけー
こんなかんじでどんどんいい感じのタスクをつくってこー
今回はここまで、ありがとうございました。