|
執筆バージョン: Unreal Engine 5.7.4
|
アニメーションにAnimNotifyStateを1フレーム目から設定し、そのアニメーションを2回以上連続で再生した際、NotifyBegin/NotifyEndが実行されず、NotifyStateが途切れることなく実行され続けることがあります。
今回は、明示的にNotifyEndを呼び出し、NotifyStateの状態を初期化する方法を紹介します。
(例)チャージしてパンチするアニメーションの「チャージ」中にTimedNiagaraEffectでエフェクトが再生されるようにし、同時にNotifyBeginとNotifyEndでPrintStringを実行するNotifyStateを設定しています。

アニメーションを1回だけ再生した際にはエフェクトがすぐに中断され、PrintStringによる出力も想定通りの挙動になっています。
しかし、設定したNotifyStateが最後まで実行される前に再度アニメーションを再生しなおすと、エフェクトが中断されずに再生され続け、最後まで再生されたあとは新しく再生されなくなってしまいました。
また、PrintStringによる出力を確認すると、連続で再生している間はNotifyBeginとNotifyEndが呼び出されていないことが分かります。
今回紹介する方法では、アニメーションを呼び出す度にNotifyEndを強制的に呼び出し、NotifyStateの状態を初期化することで、エフェクトが最初から再生されるようにします。
実装
今回はUE5.7.4でThirdPersonのテンプレートを使用します。

①キーを押す度にアニメーションを再生させる
BP_ThirdPersonCharacterのSkeletalMeshに設定されているアニメーションのABP_Unarmedを開きます。
AnimationGraphの中にあるMainStatesを開き、新しくStateとStateAliasを追加します。

どのアニメーションを再生していても、キーを押せば設定したアニメーションが再生されるように、MyAliasのStateAliasはMyStateCancel以外の全てにチェックを入れます。
MyStateCancelとMyStateには、今回再生させたいアニメーションを両方に設定します。今回はMM_ChargedAttackを設定しました。

また、MyAlias→MyStateCancelとMyStateCancel→MyStateのBlendのDurationを0.0sに設定します。
こうすることで、キーを押すたびにアニメーションがリセットされ、既に再生中でも連続で同じアニメーションを呼び出すことができるようになります。

次に、アニメーションを再生する条件を設定しましょう。新しいBool型の変数を追加し、bAttackと名付けます。

MyAliasからの遷移条件にbAttackを設定し、MyStateCancelからMyStateには無条件で遷移するようにTrueを設定しておきます。


MyStateの再生が終わったらLocomotionに戻るように、AutomaticRuleBasedonSequencePlayerinStateをTrueに設定します。

このままだとbAttackがTrueになると無限に遷移し続けてしまうため、MyStateに遷移したらbAttackをFalseにするようにします。
MyStateCancelからMyStateに遷移する際のTransitionEndにOnEndMyStateCancelという名前でNotifyを設定します。

一度コンパイルをし、EventGraphでAnimNotify_OnEndMyStateCancelからAttackをFalseにするようにブループリントを組みます。

これでアニメーション側の実装は完了です。
次に、キーを入力したらbAttackをTrueにし、アニメーションを再生するようにしましょう。
BP_ThirdPersonCharacterを開き、Eキーを押したらアニメーションブループリントのbAttackをTrueにするようにブループリントを組みます。キー入力が呼び出されたことが分かるようにPrintStringも入れておきましょう。

これで、Eキーを押すたびにパンチするキャラクターが完成しました。
②アニメーションにAnimNotifyStateを設定する
今回は動作していることを分かりやすくするために、NotifyBeginとNotifyEndでPrintStringを呼び出すだけのNotifyStateと、TimedNiagaraEffectでエフェクトを再生するように設定してみます。
まずはNotifyBeginとNotifyEndでPrintStringを呼び出すだけのAnimNotifyStateを実装しましょう。
コンテンツブラウザ上で右クリックし、BPを選択し親クラスとしてAnimNotifyStateを指定してMyNotifyStateを作成します。

MyNotifyStateを開き、関数ReceivedNotifyBeginとReceivedNotifyEndをオーバーライドし、それぞれでPrintStringを呼び出すようにします。



次に、エフェクトを実装しましょう。
コンテンツブラウザ上で右クリックし、NiagaraSystemを選択しMyNiagaraを作成します。今回はエフェクトが途切れることなく再生され続けていることが分かりやすいように、ParticlesUniqueIDを選択しましょう。

これでAnimNotifyの用意ができました。実際にアニメーションにAnimNotifyを設定しましょう。
MM_ChargedAttackを開き、Notifiesを右クリックしてAddNotifyStateからMyNotifyStateとTimedNiagaraEffectを追加しましょう。
TimedNiagaraEffectのMyNiagaraには先ほど作成したMyNiagaraを設定し、プレイヤーに追従するようにSocketNameにrootと入力します。

また、アニメージョンの前半部分でのみ再生されるように、0~30フレーム目に設定します。

これで、アニメーションを呼び出す度にエフェクトが再生され、エフェクトの始まりと終わりにPrintStringが呼び出されるようになりました。
しかし、この状態では連続でアニメーションを呼び出した時、エフェクトが途切れることなく再生され続けてしまい、PrintStringの出力を確認するとNotifyBeginとNotifyEndも1度しか呼び出されなくなっていることが分かります。
これは、AnimNotifyが1フレーム目から設定されていることが原因です。
エンジンのソースコードから、AnimInstance.cppの関数TriggerAnimNotifiesの中身を見てみると、現在再生中のAnimNotifyと、前フレームで再生されていたAnimNotifyのキャッシュが配列で管理されており、2つの配列の差分を見ることによってNotifyBegin、NotifyEnd、NotifyTickを呼び出すようになっています。

前のフレームにしか存在しないものはNotifyEndが呼ばれ、前のフレームには無かったものはNotifyBeginが呼ばれる
しかし、今回はAnimNotifyを1フレーム目から設定しているため、アニメーションを再生しなおした時に前のフレームと今のフレームの配列の中身が同じになり、同じNotifyが呼ばれ続けているだけだと誤認されてしまいます。

アニメーションを再生しなおして最初のフレームに戻ったが、配列の中身が変わらないためBeginとEndは呼ばれない
そのため、AnimNotifyをアニメーションの始めから少し遅らせてから設定するようにすることでも問題は解決しますが、それでは1フレーム目にAnimNotifyを設定することができません。
今回は別の方法で改善してみましょう。
③NotifyEndを明示的に呼び出し、NotifyStateの状態をリセットする
AnimInstanceには、配列にキャッシュしてある全てのAnimNotifyのNotifyEndを呼び出し、配列を空にするEndNotifyStateという関数があります。
キャッシュ配列を空にすることによって、次のフレームでまだAnimNotifyが再生されている場合はNotifyBeginから実行されるようになります。

この関数を好きなタイミングで呼び出せるようにしましょう。
AnimInstanceを親クラスにして新しいクラスを作成します。ToolsからNew C++ Classを選択し、AllClassesからAnimInstanceを選択し、MyAnimInstanceを作成します。

MyAnimInstance.hに関数ResetNotifyStatesを定義し、ブループリントで呼び出せるようにBlueprintCallableにします。
|
|
public: UFUNCTION(BlueprintCallable) void ResetNotifyState(); |
ResetNotifyStateの内部では、EndNotifyStateを呼び出すようにします。
|
|
void UMyAnimInstance::ResetNotifyState() { EndNotifyStates(); } |
ABP_Unarmedの親クラスを今回作成したMyAnimInstanceに変更します。

BP_ThirdPersonCharacterを開き、bAttackのフラグをTrueにする前に先程作った関数ResetNotifyStatesを呼び出すようにしましょう。

これで、キーを押すたびにAnimNotifyの状態を全て初期化してからアニメーション遷移するように実装ができました。