エンジニアっぽくなりたい

UnityやUnrealEngine 4などでなにか役に立ちそうなことを発信していく。

UE4 シーケンサーで実行中に対象を置き換える

シーケンサーで設定したものを、ランタイムのキャラクターに置き換えなきゃいけないことって多々ありますよね。
着せ替えとかができるゲームだと、その時設定している衣装でカットシーンにでてきてほしい、みたいな。
一応公式のドキュメントにもやり方が書いてありましたが、「GetSequenceBinding」ノードを使う方法がなかなか納得できなかったので別の方法を検討してみました。


目標


シーケンサーで指定したキャラクターを実行時のキャラクターで置き換えてシーケンサーで指定した動作をさせる。

環境

・UE 4.27.0 (Gitのコミット:99b6e203a15d04fc7bbbf554c421a985c1ccb8f1 をビルド)
・VisualStudio 2019 16.11.2


準備

置き換え対応をするための準備をします。
今回はUE4のテンプレートから「サードパーソン」を選択した前提で進行します。(いつもサードパーソンですが。)

まずは以下の必要なデータを作成します。
・LevelSequence上で指定するキャラクター
・置き換え対象となるキャラクター
・置き換えの対象となるLevelSequence

LevelSequence上で指定するキャラクター

「ThirdPersonCharacter」とします。

置き換え対象となるキャラクター

「ThirdPersonCharacter」を継承したBPを作成します。
名前は「BP_PlayerCharacter」とし、今回は以下のような変更を行います。
・SkeletalMeshを「SK_Mannequin_Female」に変更。
・マテリアルを赤く設定。
こんな感じ
f:id:naoxgames:20211016143457p:plain

その後、ゲームモードのDefaultPawnを「BP_PlayerCharacter」にします。

置き換えの対象となるLevelSequence

今回は以下のようにただ右から左へ走るモーションをするだけのものを作りました。
名前は「LS_Run」とします。
元のままだとAnimBPにスロットの影響がないので、以下のように「DefaultSlot」を通るようにします。
f:id:naoxgames:20211016143657p:plain

シーケンサーで指定するキャラクターは「ThirdPersonCharacter」で、「Spawnable」で設定します。
f:id:naoxgames:20211016141550p:plain
f:id:naoxgames:20211016141626p:plain


簡単にやってみる(Level上で完結パターン)

まずは簡単にやってみる。
作戦としてはレベルに配置した「LevelSequenceActor」の詳細にある「BindingOverrides」を使用します。
f:id:naoxgames:20211016144616p:plain

メリットとしては
 簡単、詳細をいじる程度で完結する。

デメリットとしては
 レベル上にあらかじめすべての要素が揃っている必要があり、動的な変化に適用できない。
 シーケンサーで変更(追加削除など)をかけた場合関連個所すべてを修正する必要がある。
という感じ。


Level上に配置&設定をする

レベルに以下を設定して配置します。

BP_PlayerCharacter

レベルに配置し、詳細から「AutProcessPlayer」を「Player0」にする。(実行時にプレイヤーのキャラクターとして扱われる。※さっきまで知らなかった)
PlayerStartがあるなら消しておきます。

LS_Run

レベルに配置し、詳細から「自動プレイ」にチェックを入れます。(実行時即再生されます。)
詳細から「BindingOverrides」に以下のように設定します。
f:id:naoxgames:20211016145651p:plain
「ObjectBindingId」がシーケンサー上のBinding名で、複数置き換えたい対象がある場合は「+」でそれぞれ指定します。
「BoundActor」はレベルに配置されているActorです。


実行!!!!


f:id:naoxgames:20211022002952p:plain
※画像でも動画でも伝わらない気がしますが置き換えできてるんです。

という具合で置き換えできました!
アニメーションはスケルトンが一致していないといけないとか、コンポーネントシーケンサーで設定している場合はそれが付いていないといけないので、気を付ける必要はあります。

できたけど、もはやこのやり方が通用するシチュエーションがいまいち浮かびません。
「GetSequenceBinding」ノード使うパターンのほうがまだましな気がする。



これだと全然ダメなのでもう少し良い方法を検討。


やってみる(BPで処理するパターン)

作戦としては同じく「LevelSequenceActor」の詳細にある「BindingOverrides」を使用します。
f:id:naoxgames:20211016144616p:plain

今回は各「LevelSequence」をBP化し、あらかじめBP側で必要な設定をしておくパターンです。
また、動的に生成されたオブジェクトに対する置き換えも検討した対応になります。

メリットとしては
 シーケンサーの担当者はBPの詳細をいじるだけ。
 動的な変化にも対応できる。

デメリットとしては
 LevelSequence毎にBPを作成する必要があるため、アセットが増える。
 シーケンサーで変更(追加削除など)をかけた場合関連個所すべてを修正する必要がある。

という感じ。


LevelSequenceActorのBPを作成、設定する

コンテンツブラウザから「LevelSequenceActor」のBPを作成します。
ここでは「BP_LS_Run」とします。
f:id:naoxgames:20211020005540p:plain
作成したBPを開き、詳細から「LevelSequence」の項目に対象の「LevelSequence」を指定します。
f:id:naoxgames:20211020005635p:plain
ここでは「LS_Run」とします。

BindingOverridesの対象を指定する

置き換え対象の「ObjectBindingID」を「BindingOverrides」に設定する必要があります。
「LevelSequenceActor」では「MovieSceneObjectBindingID」型の変数を作ると、設定されている「LevelSequence」の「ObjectBinding」の情報を指定できるようになります。
f:id:naoxgames:20211020010345p:plain
f:id:naoxgames:20211020010847p:plain

シーケンサーが設定されている場合もそれぞれの中から指定することができます
f:id:naoxgames:20211020011425p:plain
これは便利!!!!

※「FMovieSceneObjectBindingIDCustomization」というクラスでエディター拡張されているようで、親クラスである「FMovieSceneObjectBindingIDPicker」あたりで関連する処理が行われているようです。
このあたりは調べて後ほど書こうと思います。


ここでは「PawnBindingID」という変数を作り、シーケンサーの置き換えたいキャラクターの「ObjectBindingID」(この場合は「ThirdPersonCharacter」)を指定しましょう。
f:id:naoxgames:20211021225543p:plain


次に、公式のドキュメントでも使ってる「AddBinding」ノードで、「BindingOverrides」に置き換えたい対象を設定します。

LevelSequenceActor.cpp
void ALevelSequenceActor::AddBinding

を見るとわかりますが、「AddBinding」の中で「BindingOverrides」に置き換え対象の設定を行っています。
なので、「AddBinding」を使うことで動的に置き換え対象を指定できます。ヤッタゼ!

ここでは試しに以下のようなBPノードを設定してみます。
f:id:naoxgames:20211021231733p:plain
これは現在レベルに存在する「BP_PlayerCharacter」クラスのBPを探し、一つ目に見つけたものを「PawnBindingID」で指定した「ObjectBindingID」に置き換える、というような内容になります。


実行!!!!

上で設定した「BP_LS_Run」をレベルに配置し、「自動プレイ」にチェックを入れて実行してみましょう。

f:id:naoxgames:20211022003137p:plain
※「BP_LS_Run」はレベルに配置時「BindingOverrides」は未設定ですが、実行されるとBPで処理された置き換え設定により「BindingOverrides」が設定されます。

置き換わった!!!!

すべてのシーケンサーをBP化(サブシーケンサーは不要)する必要がありますが、こうすることで、実行前とかで置き換え対象を取得し、あらかじめ設定しておいた「ObjectBindingID」を使って置き換える設定、というのが可能になります。
普通に作ってると複数のサブシーケンサーがあって、各カット毎に置き換えたいキャラクターが設定されているので、今回の「PawnBindingID」みたいなものは配列で持ち、その分設定する必要が出てくると思います。
また、その時の会話対象や敵とかも置き換えたいとかあれば、それも別変数で設定する必要がありそうです。


まだまだ課題はありますが、ひとまず状況に応じた置き換えをしたい場合に対応可能で、「GetSequenceBinding」ノードを使うパターンよりはいいのかなと思っています。


Tagという男

別の方法として、

LevelSequenceActor.cpp
void ALevelSequenceActor::AddBindingByTag

f:id:naoxgames:20211021234231p:plain
という、シーケンサーで「ObjectBinding」に対してタグを設定し、そのタグに対して一括置き換え設定をするパターンもあります。
こんな感じで設定して
f:id:naoxgames:20211021235939p:plain

こんな感じで表示される
f:id:naoxgames:20211022000006p:plain


今回こっちを使わなかったのは、タグは基本的に文字列を指定をする必要があり、打ち間違いなどのリスクがあるにはあります。
実際の業務で文字列はあんまり打たせたくないな~という個人的な気持ちから、今回は使いませんでした。
「MovieSceneObjectBindingID」は「LevelSequence」に設定されているものがプルダウンで表示されてその中から選ぶ形式のため、間違えにくいと考えています。

「AddBindingByTag」を使う場合は、私としては
・タグは特定の種類から選択式にする改造
・特定のアクターを設定すると自動で適切なタグが付く改造
・特定の種類以外のタグが指定されていないか検出、置き換えなどをするツールを作る
などでケアが必要かなと思います。
逆にそういうケアさえすれば「AddBindingByTag」のほうがとても便利かと思います!!
・・・・もしかしたら後々そういうのを記事で書くかも・・・・?わかりませんが。


思ったこと

という感じでやってみたわけですが。
エンジン改造なしだとこんな感じかなぁという印象です。
やはり使い勝手の悪さは残る気がしているので、近々エンジン改造ありで、どれくらい便利にできるか検討してみます。多分。
改造したところで、ある程度の条件下で自動的に置き換えるって感じになると思うので、逆に置き換えたくない場合にトラブルになったりする可能性もありそう・・・・?

ここはPlus Ultraの精神でさらに向こうへ行こうと思います。





正しい対応方法やより良い方法があればご指摘お願いいたします!!!