ぼっちプログラマのメモ

Unreal Engineについて書いたりしてます

【UE5】猫でも分かるChooser(動的アセットの選択)ことはじめ Part 1 - Chooserとは?編 -

本記事はUnreal Engine (UE) Advent Calendar 2024 の13日目の記事です。

UE5.3(5.2だったかも)辺りで追加され、UE5.5ではProduction ReadyになったChooser(動的アセットの選択)機能に関して書いていきます!「なんか凄そうな機能だけど、具体的に何ができるのかよくわからんし、どう使ったらいいかもわからん!」という(半年前の僕のような)人にとって足がかりになればなぁというノリで書いてます。

なお、本記事はUE5.5.1 時点での内容をベースに書いています(ただ大半はUE5.4でもいけます)。また、UEの基本的な機能は軽く知っていること前提としています。もしChooser関連以外で知らない用語ができた際はその都度Google先生に聞くのが良いと思います。

UE5.4リリースノート:
https://dev.epicgames.com/documentation/ja-jp/unreal-engine/unreal-engine-5.4-release-notes#チューザーとプロキシテーブル(ベータ版))

UE5.5 リリースノート:
https://dev.epicgames.com/documentation/ja-jp/unreal-engine/unreal-engine-5.5-release-notes#チューザー

公式ドキュメント:
https://dev.epicgames.com/documentation/ja-jp/unreal-engine/dynamic-asset-selection-in-unreal-engine

公式サンプル:
- 機能別サンプル:Game/Maps/Animation/Animation_Choosers レベル (おすすめ!)
- GameAnimationサンプル:CHT_XXX でアセット検索

Chooserってなんなん?

超絶ざっくり言うと、「設定したパラメータと条件式をもとに、最適なアセット(クラス)を選択する」機能です!うーん、良さが伝わる気が全くしない!ということで、具体的な例をあげてみます。

例えば「落下中は落下モーション、そうでないときは待機モーションを取得したい」場合、普通に組むと下図のようになります。

できたー!と安心していたら「女性キャラの場合、待機モーションは別アセットにしてほしい」とオーダーが来ました。ふむふむ、なるほど。こんな感じでどうでしょう?

「あ、このキャラは空飛べるからその対応もよろしく!あ、性別でモーション分けてね!」はい…

この調子で増えていくとノードが大変なことになりそうですし…別関数やステートマシンなどに逃がすなどの手間が発生しそうです。何よりデータドリブンではないので、BPを毎回いじらないといけないのでイテレーションが非常に悪いです。

これがChooserだと…こうなります!!!

条件判定を別アセット(Chooser Table)で管理し、Chooserは各条件式を全て満たすアセット(クラス)を結果として返します。そして、BP側はそれを呼び出すだけという非常にシンプルな構成になります。

また、データドリブンなのでエンジニア以外でも条件を足したり、選択されるアセットを差し替えたりすることができます!

もちろんBoolだけでなく、float(範囲設定も可)・Enum・GameplayTagを条件式に使えたり、ランダム抽選もできたり。

更に、UE5.5のリリースノートにもある通り、アニメーション以外でも使うことができたり…

なんと、アセットだけでなく、値を設定する(正しくは返す)こともできます!!

他にも色々機能があり、Chooser導入することで従来よりも更にデータドリブンな設計を組むことが可能になります。これは…やるしかない…!!!

と僕の中では非常に盛り上がっていまして、Chooserを使う人がもっと増えてほしい!ということで布教記事を書いてたりします。

Chooserことはじめ

そんなChooserですが、正直な所…公式ドキュメントに書いてるチュートリアルは少々分かりづらいと個人的に思ってます。なぜかと言うと、このチュートリアルでは後述するProxyAsset, Tableを使っているのですが、これらはChooserアセットの流用・量産のためのものであり、「そもそもChooserとはなんぞや」という所を学ぶ段階ではまだ知らなくてもいいと思います。

なので、機能別サンプルのAnimation_Choosersレベルにある各サンプルを分析していくのが初手としていいのですが、それはそれで大変なので…ここでは↑で挙げた例を元に、Chooserアセットの作り・使い方を書いていこうと思います。

Chooserアセットの作り・使い方の大まかな流れ

  1. Chooserプラグインを有効化
  2. Chooser側に渡すパラメータを準備
    1. パラメータを管理するBP or 構造体を作成
    2. a に 各パラメータを追加
  3. Chooser Table アセットを作成・初期設定
    1. 2.a で作成したBP or 構造体を指定
    2. 返すアセットのクラス(型)を指定
  4. 各条件式と結果として返すアセットを設定
  5. Chooserからアセットを取得

Chooserプラグインを有効化

UE5.5でProduction ReadyになったChooserくんですが、UE5.5.1時点ではデフォルト無効になっているので有効にします(デフォルト有効になったらいいなぁ…)

Chooser側に渡すパラメータを準備

Chooser自身はパラメータを持っていないため、条件判定で使うデータを外で管理する必要があります。

で、少しだけ厄介なのが「ChooserにはClass ( UObject継承)またはStruct(構造体)でしか渡せない」という縛りがあります。そのため、条件式で使うパラメータが float だけであっても、そのfloatプロパティを持つClass または Struct を用意する必要があります。

今回はこの部分は本筋ではないので、ThirdPersonTemplateのABP_Mannyに IsFemaleプロパティ(bool型)を追加したものを用います。

Chooser Table アセットを作成・初期設定

コンテンツブラウザ(ドロワー)の右クリックメニューからChooser Tableアセットを作成します。

そして、UE5.5からはChooserTableがパラメータとして扱うClass or Struct の指定や、最終的に返すアセットのクラス(型)の指定を専用のダイアログから設定します。

ABPの場合は、ChooserTypeを「Animation Chooser」にし、AnimClassにそのABPを指定すればOKです。

ABP以外の場合はChooser Typeを「Generic Chooser」にし、パラメータと返すアセットの型を設定します。ちなみに、もしABPを「Generic Chooser」で設定する場合は下図のようになります。

なお、ChooserTable作成後もパラメータと返すアセットの型は変更・追加可能です(ちなみに、UE5.4ではこの方法でしか設定できませんでした…5.5で楽になってうれしい…)。

各条件式と結果として返すアセットを設定

条件式から設定しても良いですが、分かりやすさ重視でまずは結果として返すアセットを追加・設定していきます。

この追加時、なにやら見慣れない項目がいくつかあります。

軽く説明しますと…

  • Fallback Result
    • 各条件式をすべて満たすものがなかったときに選ばれる項目
    • これを設定しておかないと結果が空になる可能性があるので注意
      • 特にアニメーションを返すChooserTableの場合はAポーズ(Tポーズ)になる可能性が出てくるので、何かしら設定しておいたほうが無難だと思います。
  • Evaluate Chooser
    • 別のChooser Tableを設定する項目。設定した条件式を満たした場合、更にその外部ChooserTable内で条件判定が行われ、そこで決まったアセットを最終結果として返すという階層構造が可能になります。
    • この機能を活用することで1つのChooserTableが肥大化することを抑制したり、複数人で作業する際の分担がしやすくなります
    • Evaluate Chooserを設定する・される側はどちらも返すアセットの型は同じである必要があります
  • Nested Chooser
    • 1つのChooser Table内で階層構造を作る項目。Evaluate Chooserとやってることはほぼ同じですが、別アセットではないという大きな違いがあります
    • 1つのアセットに集約されるため、複数人同時作業はできなくなるので注意
  • Lookup Proxy
    • 後述のProxy Assetを設定するための項目

文字だけだとイメージしづらい部分かなので…機能別サンプルの

Game/ExampleContent/Choosers/2-1/2-1_Damage_Reaction_Base_Chooser 辺りを見るのが良いと思います!

少し脱線しましたが、次は条件式を入れていきます。今回は下記の条件で組んでいきます。

  • 男性 & 落下中でない ▶ MM_Idle
  • 女性 & 落下中でない ▶ MF_Idle
  • 落下中 ▶ MM_Fall_Loop

操作としては、まず条件判定で使うパラメータの型を指定します。

なお「Output ~」は条件判定ではなく、Chooserが返す値を設定するための項目です。そのため、今回の例では使用しません。

次に、どのClass or Struct が持つ どのプロパティを判定に使うか?を指定します。

ここで設定する項目の候補は、ChooserTable作成時に設定したClass or Struct 自身とそれらが持つプロパティから自動的に作成されます。めちゃべんり

これでChooser側の準備OKです。

Chooserからアセットを取得

最後に、作成したChooserを実際に使う処理を組みます。といっても簡単で、

  1. Evaluate Chooserノードを配置
  2. 使用するChooserTableアセットを指定
  3. Chooserに渡すObject or Structを引数ピンに設定

でOKです!そして、このノードが呼び出されればChooserTableアセット内で設定した条件判定が行われて、決定されたアセットをノードのResultピンから取得することができます!

あとはChooserから取得したアニメーションアセットをSequence Player ノードで再生すれば…あ、あれ?

実は少し落とし穴があります!

ChooserTableを「Animation Chooser」で作成した場合、結果として渡されるアセットの型は AnimationAsset になります。一方、Sequence PlayerノードはAnimSequenceAnimCompositeにしか対応してません!(実際はAnimSequenceBaseという型)

そのため、SequencePlayerノードで使うことが前提なら、ChooserTableのResult ClassはAnimSequenceBaseに設定しておいた方が事故や手間は少なくなるのでオススメです。ChooserTableにてAnimSequence, AnimComposite以外のアセットは候補として設定することができなくなるためです。

ただデフォルトのAnimatonAssetだとアニメーションアセット系なら基本何でもChooserで設定でき、EvaluateChooserを使う側で判定するというフローを組めるというメリットもあります。この辺りは実装・実現したい内容次第なので、臨機応変にいきましょう!(融通が効きやすいというのもChooserの大きな強みの一つですね!)

ひとまず今回はResult ClassをAnimSequenceBaseにし、以下のようにABP_Mannyを編集します。ABPのAnimGraphにてステートマシンなどを使わず、SequencePlayerノード1つだけに置き換わってるのが注目ポイントです。

ちょっと長くなりましたが、これで準備完了です!ABP_Mannyを使っているCharacter BPをレベルに配置して実行してみます…設定した条件式とプロパティをもとにChooserが決定したアニメーションアセットが再生されてますね!

例えばManny君が空中にいるときは、Is Female = false, Is Falling = true なので 全てを満たす落下モーションが選択され、

着地後は Is Falling = true になるので、落下モーションではなくMM_Idle が選択されるようになります。

そう、Chooser Tableはどんな判定結果になっているのかを可視化してくれます!これが超便利!!!(そのChooserTableを使っているActorを選択する必要はあります)

Chooserことはじめを終えてみて

このように、Chooserを使うことで「BP・C++・ステートマシンを使って分岐処理などを色々実装していた部分」を「データ側で実装・制御」することができます!

つまりデータドリブンで動作するため、実装・データの追加・編集・削除がプログラマ以外でも簡単にできるようになります。アセットを選択・設定する箇所は量産時に特にコストが響いてくるので、そこが削減できるのは大きなメリットだと思います!

しかもなんと、Chooserはアニメーション以外にも使えます!(2回目)

どんなアセットでもChooserによる選択の対象にできますし、更にClassも対象にできます。そのため、例えば特定のキャラをどのBPでSpawnさせるかの判定にChooserを使うこともできます。

更に更に、パラメータ管理・設定にもChooserを使うことができます!

例えば、以下のように設定するとChooserに渡したクラスが持つGround Speedプロパティの値を直接変更できます。データ側からパラメータも変更できちゃうのはかなり面白いですね!

Chooserはこのように非常にパワフルな機能なのですが、リリースノートや公式ドキュメントだと少しイメージしづらいので記事を書いてみたのですが…どうでしょうか?「従来の処理・管理をChooserに置き換えれる部分はかなり多いかも!すごい!」とか少しでも感じたならすごくうれしいです!

…とここまでと見せかけて、Chooserはまだまだ凄い機能が沢山あるんです!そう!Proxy Asset, Tableがね!

ということで、この記事はつづきます。おたのしみに! (記事の完成が間に合わなかったとも言う)

そして、アドベントカレンダー14日目もおたのしみに!(@abricheese さん、いつもありがとうございます! qiita.com

追記

Part 2 かきました!

pafuhana1213.hatenablog.com