みつまめ杏仁

アンリアルエンジン(UE4)でGUIを作るためにゴニョゴニョしてます。UIデザイナーの皆様の助けになれば幸いです。

UMGでのアニメーション終了検出について

 今回の記事はEditorUtilityWidgetを触ってみようとアレコレしているうちに、気が付くとアニメーションの終了検出について調べる流れになったのでそのメモです。

 UIはユーザがゲームと対話するために存在しているので、何かとリアクションするのが重要です。そのリアクションには何かしらの変化(動き)が起きるので、リアクションとして認識させるには、それなりの「間」が必要になります。

 この見た目の挙動を「ふるまい」とか「マイクロインタラクション」などと呼ばれることもあります。

 UIデザイナーが設計して作るリアクションは、細かいパーツの構成を熟知しているUIデザイナーがコントロールできる環境が理想。

 見た目の部分なんで、UIデザイナーに苦情が来やすいのもありますし、アニメーションに関しては表層部分なのでUIデザイナー自身が修正&調整したほうが、プログラマへの負担は減るはずです。

 UIのアニメーションがプログラマ制御だった時代は、いちいち細かい値(12フレームでアルファ0.12ゆうてましけど、やっぱり0.125に変えてもらっていいすか、みたいな)を伝えて修正をお願いしていました。いい時代になったものです。個人的にそんな時代を経験しているので、UE4のUMGがとても素晴らしく映っています。

 

 さてさて、前置きが長くなりました。

 私はUIのアニメーションは大きく2種類に分けられると考えています。

 

  • 終了を待つ必要のない演出やエフェクト的なアニメーション
  • 終了しないと次のフェーズに進まないアニメーション

 

 これはアニメーションの再生と終了のタイミングをどう扱うか、という視点で分類しています。

 前者は、選択中のアイテムがハイライトされている状態や、バックグラウンドで「賑やかし」として動いているようなもの、ローディングなどの待機アニメーション。途中でインタラプトされても問題ないようなシンプルで繰り返す動きのものが多いです。

 後者は、画面のフェードや遷移、決定演出、モーダルウィンドウの開閉演出、など起承転結があり、ゲームの呼吸とかテンポ感に関わる大事な要素です。さらにこの時間はゲームの進行を統括するプログラマにとっては待ち時間となります。

 UIデザイナーがパーツを動かしている間、待ってくれているわけです。こまめに再生中かどうかチェックして終わったら通知、または事前に掛かる時間をタイマー予約するか。という仕組みになると思うのですが、UE4ではアニメーションが終了したことを通知する仕組みをブループリントで用意することになります。その方法をこのブログで何度か書いてきましたが、最近のバージョンでは、その辺りが少し進化しているようなので、自身のアップデートのためにちょっと調べてみました。

 

 その結果、アニメーションの終了を検出する方法を4パターン見つけました。

  追記)Twitterに更新報告をした後に教えてもらった方法も加えて計 6パターンになりました。

 

 

 Get End Time ノードを使う

f:id:hiyokosabrey:20200517173803p:plain

 結構以前から利用させてもらっています。私にとっては馴染み深い方法。

 上の緑色やつが Get End Time ノード。事前にアニメーションの尺を調べておいて、タイマーをセットする方法。アニメーションごとに専用のカスタムイベントが必要になります。ちょっと複雑でノードが多いのが面倒だなと思ってました。

 残念ながら、ポーズ中(4.25で、set Game Paused 使用で試してみました)だと、Set Timer by Eventノードが動かないようなので、ポーズ中ではこのしくみは使えません。Tickは動いているので、自前で時間計測すれば可能ではありますが、この後の方で紹介するイベントを使ったほうが良さそうです。

 

 

Play Animation with Finished Event ノードを使う

f:id:hiyokosabrey:20200517192608p:plain

  4.23で追加された比較的新しめのノード。終了したときの処理をシンプルにつなぐことができるのでノードが少なくてスッキリ。これはいいですね。

 ちなみにこのノードは右上に時計のバッジがついてるので、Delayノード同様に関数では使えません。

 ポーズ中(4.25で、set Game Paused 使用で試してみました)だと、アニメーションは再生されますが、Finishedのピンはポーズ解除後に実行されるので、ポーズ中のUIではこのノードは使えないのが痛い。未来のVerに期待したいところ。

 ブログのコメントでご指摘いただきました。upota さんありがとうございます。

 

 

on Animation Finished イベントを使う

f:id:hiyokosabrey:20200517174050p:plain

 結構古いバージョンから存在は知っていたけど、再生するアニメーションの内容に関係なく終了したらとにかく呼び出されるので、アニメーションを複数抱えたWidgetの場合、対象のアニメーションかどうかの判別や仕分けが必要になったり、Play Animation ノードから離れた場所にこのノードを置くことになるので、若干イベントグラフが読みにくくなる印象があって、なんとなく避けていました。

 

 

Eventキーを利用

f:id:hiyokosabrey:20200517195627p:plain

 タイムラインにイベントトラックを追加して、トリガーとしてキーを打ちます。そこで関数やイベントを呼び出すようにする方法。個人的にUMGのタイムラインの操作方法がやや癖があって難しく感じるのと、操作的なうっかりが起こりやすかったり、呼び出す関数名やイベント名を探してセットするのがちょっと面倒な気がするので普段使わないのですが、好みの分かれるところかもしれません。

 

 

事前にバインドしておく方法

f:id:hiyokosabrey:20200518143826p:plain

K.Y.さんに教えてもらった方法その1

事前に対象のアニメーションオブジェクトに対してバインドしておいて、あとは好きなタイミングで再生するだけ、というタイプ。これなら個別に終了を検出できる。

常駐するWidgetの場合 Unbindのしくみも合わせてコントロールする必要がありそう。

 

 

 

対象のアニメーション専用のイベントを使う

f:id:hiyokosabrey:20200518144333p:plain

K.Y.さんに教えてもらった方法その2

アニメーション名でノード検索すると見つかるやつ。

f:id:hiyokosabrey:20200518144603p:plain

グラフ上でノード検索しないと出てこない代物です。

on Animation Finished イベントはザックリしてますが、これなら確実に対象のアニメーション終了を検出できて、さらにグラフ上でもわかりやすく配置できそうなのはよさそうですね。

 

K.Y.さんありがとうございます!

 

 

 まだこのパターン以外の方法があるかもしれません。Play Animation with Finished Eventノードか、個別の AnimationFinished(xxx) はフロー的にわかりやすくなる感じなので、終了を待ちたい時には積極的に使っていこうと思います。

 ただポーズ中はうまく動かないのがあるので注意。

 こうして挙げてみると選択の幅が広がったので、なるべく可読性とかメンテナンスのしやすさを基準に選んでいきたいなと思うのですが、パフォーマンス的にはどうなのかが気になるところ。引き続き情報のアップデートはしていこうと思います。

 

 アニメーションが終わったことを通知できる仕組みさえ作っておけば、あとからアニメーションのクオリティアップや尺調整は、ずっとUIデザイナーのターンです。自分で責任を持つ範囲が明確になれば、いい仕事ができるはずです。

 確認ダイアログなどで登場演出が終わってないのにキー入力を受け付けてしまったり、フェードアウトしてる途中でバックで絵が変化していたりというカッコ悪いUIにしないためにも、イベントドリブンなUI表示を作れるようにしておきたいものです。

 

ではでは 今回はこの辺で

ステキなイベントドリブンライフを!