この記事はUnreal Engine 4 (UE4) その1 Advent Calendar 2022の4日目の記事です。
過去の記事で、レベルシーケンサーに、ブループリントからオブジェクトを配置する記事を投稿したので、
今回はその続きとして、ブループリントからアニメーションのキーフレームの入力するまでの内容になります。
記事の内容
- レベルシーケンスの構造について
- Bind / Spawnables / Possessableの違いについて
- キーフレームの追加の仕方
- SequenceBindingProxy → Actor / Actor → SequenceBindingProxy
- Runtimeと非Runtimeの挙動の違い
確認環境
- Windows 11 Home
- UnrealEngine 4.27.2
レベルシーケンスの構造について
最初にレベルシーケンスの構造について説明します。
レベルシーケンスには、トラックが存在し、その中にセクションがあり、セクションの中にチャンネルが存在しており、チャンネルの中にキーフレームが打たれています。
キーフレームを打つことで、アニメーション(変化する値)させています。
- トラックに複数のセクションを追加する事は可能ですが、シーケンスエディタで表示されるのは最初に追加したセクションだけ
- セクションとチャンネルは、1対1の関係で、セクションが追加されるとチャンネルも追加される
具体的に、レベルシーケンスの内容を見ながら説明します。
上の画像を元に説明します。
レベルシーケンスの中にマスタートラックのショットトラック(紫の枠)とスポーンされるアクター(赤い枠)が追加されています。
アクターは、トラック(青い枠)を持っており、トラックの中にセクション(黄色い枠)を持ち、
セクションの中にチャンネル(黄色い枠)があり、チャンネルの中に打たれているキーフレーム(緑の丸)です。
マスタートラックには、ショットトラック、カメラカットトラックなど、トラックを追加する際に出るダイアログ(下の画像)にあるトラックがマスタートラックになります。フォルダなど、一部はマスタートラックではないものもあります。
レベルシーケンスにスポーンされているたアクターには、直下にトラックを持った場合とコンポーネントがトラックを持っている場合が存在します。
レベルシーケンスの構造については、negimochiさんがブログで詳細に説明をされている為、ここでは簡単な説明に留めます。
トラック / セクション / チャンネルへのアクセスについて
上の画像は、新規に作成したレベルシーケンサーにカメラとショットが一つ追加されている状態です。
このLevelSequenceに対して、GetMasterTracksを行うと、カメラカットトラックのみが入った配列が返ってきます。
CineCameraActorやCube(StaticMeshActor)のオブジェクトは取れません。
GetMasterTracksノード
GetMasterTracksノードは、LevelSequenceオブジェクトからマスタートラックを取得できるノードです。
アクターを取得する
CineCameraActorやCubeにアクセスするには、どうすればいいのでしょうか?
この場合は、GetBindingsノードまたはGetSpawnablesノードを使用します。
実際に、ノードを実行し返ってきた結果をダンプしてみました。
GetBindingsノードでは、CameraComponentが含まれているのに対し、GetSpawnablesノードでは含まれていない点に注意してください。
コンポーネントは、Spawnableなオブジェクトではないのです。その為、Bind情報から取得します。
GetBindingsノードやGetSpawnablesノードでは、Actor型のオブジェクトではなく、SequenceBindingProxyというオブジェクトで返って来ます。
レベルシーケンスでアクターやコンポーネントをアクセスする際に、このSequenceBindingProxyオブジェクトを操作して、アクセスを行います。
SequenceBindingProxyオブジェクトの詳細は、後述します。
トラックを取得する
Cine Camera ActorやCubeが持っているSpanedやTransformのトラックへのアクセスはどう行えばいいのでしょうか?
GetSpawnablesでアクターを取得し、そのアクターに対してGetTracksを行う事で、Spaned(MovieSceneSpawnTrack) や Transform(MovieScene3DTransformTrack) へのアクセスが可能です。
次に気になるのが、CameraComponentの下にあるシネマティクスで公開されているPropertyへのアクセスについてです。
その為には、まずCameraComponentへアクセスする必要があります。
CameraComponentへアクセスは、GetSpawnablesでアクターを取得し、そのアクターに対してGetChildPossablesノードを行う事で、CameraComponentへのアクセスが可能です。
ここで少し疑問に思うのが、GetSpawnablesノートでは取得できなかったCameraComponentをGetChildPossablesノードで取得できる点です。
違和感を感じるとこかも知れませんが、GetChildPossables内の実装では、最初にGetBindingsの中身からアクセスしている点が挙げられます。
その為、コンポーネントはGetChildPossablesノードでアクセスしていく事がベターだと思われます。
チャンネルを取得する
チャンネルを取得するまでの流れです。
長いですが、レベルシーケンサー内の一つのチャンネルを見つけるまで、
Spawnableオブジェクト ➡ コンポーネント ➡ Track ➡ Section ➡ Channel の順で探していく必要があます。
FindTracksbyTypeノードやFindChannelsbyTypeノードというものも実装されている為、必要によって使って行きましょう。
GetDisplayNameで名前を見る際の注意点
ここで一つ、注意が必要な事があります。
上の画像のコメントにも記載していますが、トラックを見分ける判断材料が、GetDisplayNameしかありません。
具体的な説明をすると、CameraComponentには、Current Focal Lengthとマニュアル焦点距離 (フォーカス設定)とCurrent Apertureの三つのプロパティが追加されています。
このプロパティがどのトラックであるか判断材料が、コメントに記載した、MoviwSceneTrackが持っているGetDisplayNameだけだという事です。
トラックやセクションやチャンネルで、Pure関数としてGetObjectNameとGetDisplayNameが使用できるのですが、
MovieSceneFloatTrack_0やMovieSceneFloatSection_0またはMovieSceneScriptingFloatChannel_0っという名前を返してくる為、判断材料とはなりえません。
トラックの名前を記録し、トラックの中を解析していく事が大切かと思います。
Bind / Spawnables / Possessableの違いについて
ここで、シーケンスにアクターが管理された状態について説明します。
状態としては、Bind(Binding) / Spawnables / Possessableの三つの状態があります。
Bind
Bind状態とは、レベルシーケンサーに配置されている状態であると言えます。
GetBindingsノードで取得してくるオブジェクトもこの状態のオブジェクトであると考えていいでしょう。
SpawnablesとPossessable
SpawnablesとPossessableの違いについてです。
Spawnablesは、レベルシーケンサーに配置されている状態でスポーンされるオブジェクトの事、Possessableはレベルにスポーンされているオブジェクトが、レベルシーケンサーに配置されている状態です。
詳細は、公式のドキュメントに記載があります、
ドラック&ドロップで、コンテンツブラウザからアクターをBindしたアクターは、同時にSpawnablesされている為、Bind状態とSpawnables状態を意識する機会は少ないでしょう。
上で説明したとおり、GetBindingノードとGetSpawnablesノードの結果が違う点に注意してください。
キーフレームの追加の仕方
この記事の本題のブループリントからのキーフレームを打ち方について、説明していきます。
上の画像は、レベルシーケンサーに、ドラック&ドロップでスタティックメッシュに配置し、LocationのY座標の0フレーム目に-420を打ち、20フレーム目に420のキーフレームを打って、Y軸の移動を行っています。
説明サンプル用ブループリント
ブループリントから同じ動作をするブループリントが下のブループリントです。こちらは、ユーティリティウィジェットで作成しています。
表示されない方は、下のリンクから確認できます。
ブループリントからアクターをスポーンしてキーフレームを追加するサンプル
ノードの説明
FindChannelsbyTypeノード
アニメーションのキーフレーム打つトラックを見つける際に使用します。
結果が、配列となっている為、ループを行い目的のトラックを見つける必要があります。
CastToMovieSceneScriptingFloatChannelノード
キャストノードです。次に紹介するAddKey系のノードが、CastToMovieSceneScripting****Channelノードで実装されている為、操作したい型にキャストする必要があります。
以下の型が存在します。
型 | キャスト用ノード |
---|---|
Boolean | CastToMovieSceneScriptingBoolChannel |
Int | CastToMovieSceneScriptingIntgerChannel |
Float | CastToMovieSceneScriptingFloatChannel |
String | CastToMovieSceneScriptingStringChannel |
Byte | CastToMovieSceneScriptingByteChannel |
String | CastToMovieSceneScriptingStringChannel |
Event | CastToMovieSceneScriptingEventChannel |
ActorReference | CastToMovieSceneScriptingActorReferenceChannel |
ObjectPath | CastToMovieSceneScriptingObjectPathChannel |
以降に紹介するAddKeyノード、GetKeysノード、GetNumKeysノード、RemoveKeyノード、SetDefault / GetDefault / RemoveDefault / HasDefaultノードは、型ごとに専用のノードが用意されています
AddKeyノード
実際にキーフレームを入力するノードです。
下の画像は、Boolean型のトラックのキーフレーム追加する際に使用します。
その他のノード
その他、Keyで検索すると、下の画像のようにいくつかのノードが引っ掛かります。
検証出来た物だけ載せておきます。
SetDefault / GetDefault / RemoveDefault / HasDefaultノード
MovieSceneScriptingFloatChannelにキャストしたオブジェクトから引けます。MovieSceneScriptingChannelオブジェクトのままだと、ノードが出てきません。
下の画像の赤枠で示した箇所が、何もキーフレームが打たれていない場合の値が、デフォルトの値となります。
このノードは、そのデフォルトの値の設定操作できるノードとなります。
それぞれ、SetDefault(設定) / GetDefault(取得) / RemoveDefault(削除) / HasDefault(設定の可否をチェック) できるノードとなります。
MovieSceneScriptingFloatChannelにキャストしたオブジェクトから引けます。
名前の通り、チャンネルが持っているキーのリストを取得できます。
MovieSceneScriptingFloatChannelにキャストしたオブジェクトから引けます。
名前の通り、チャンネルが持っているキー数を返します。
MovieSceneScriptingFloatChannelにキャストしたオブジェクトから引けます。
MovieSceneScriptingKeyに対して実行するノードで、引数で渡したキーフレームを削除します。
削除ノードは、RemoveSectionやRemoveChannelというノードは用意されていませんが、RemoveTrackまたは、RemoveMasterTrackノードは用意されており、トラックやマスタートラックの削除に使用します。
SequenceBindingProxy → Actor / Actor → SequenceBindingProxy
レベルシーケンスに管理されているアクターは、レベルで管理されているアクターと同一オブジェクトであっても、アクセス方法が違います。
GetAllActorsノードを使用すれば、レベルシーケンスに管理されているアクターにアクターとしてはアクセスする事が出来ますが、レベルシーケンスに管理されているアクターとしては変更は反映されません。
『トラック / セクション / チャンネルへのアクセスについて』の項目で触れましたが レベルシーケンスでは、
SequenceBindingProxyというオブジェクトを使って操作を行って行きます。
SequenceBindingProxyとActorの変換したいケースは、しばしば必要になってきます。
以降は、その方法についての記載になります。
SequenceBindingProxy → Actorの変換
SequenceBindingProxyには、GetObjectTemplateノードが実装されています。
これは、Actor型を返す為、目的のアクター型にキャストする事で、Actor関数のアクセスする事が可能です。
Actor → SequenceBindingProxyの変換
変換用の便利関数が用意されていない為、GetBindings ➡ ForLoop ➡ GetDisplayName で名前のチェックして一致するものを探す必要があります。
Runtimeと非Runtimeの挙動の違い
最後に、Runtimeと非Runtimeの挙動の違いついて少し説明を入れたいと思います。
この辺りは、エディタの挙動を知っていなければ、ハマるポイントだと思われます。
Runtime状態と非Runtime状態
この記事では、Runtime状態とは、下の画像のプレイボタンが押されている状態を指しており、非Runtime状態とはプレイボタンが押されていない状態となります。
ムービーレンダーキューやムービーシーンレンダーキューを実行時は、Runtime状態となって実行されています。
Runtime状態から非Runtime状態ではオブジェクト名が変わってしまう
非Runtime状態がRuntime状態になった際にレベルシーケンサーで管理されているアクターは、スポーンされ直されてしまいます。
再スポーンが行われている為、一見同じ型の同じアクターで合っても、新規にインスタンスとしては違ったオブジェクトになっています。
その為、以下の問題が発生してきます。
- オブジェクト名前(GetObjectName や GetDisplayName)が違う
- Guidが違う
オブジェクト名前が違う
非Runtime状態からRuntime状態でオブジェクト名前が変わってしまうのは、かなり困りものです。
何故なら、GetDisplayNameやGetObjectNameを見て、名前からそのアクターの正体を探らなければならない場面も多いからです。
その為、オブジェクトの名前がころころ変わってしまうのは、嫌な挙動となっています。
取れそうな対応策として、非Runtime状態でのみ操作する前提でブループリントを作成するか、スポーンされ直しても変化しない情報を持たせておくなどの対策をする必要がありそうです。
Guidについて
Guidは、レベルシーケンサーに管理されているアクターやコンポーネント、トラックの等に振られているユニークな番号となります。
このGuidも再生ボタンを押すと再取得され変わってしまいます。
このGuidは、レベルシーケンサーに管理されているオブジェクトが使用可能になっているかチェックする際に役に立ちます。
IsValidでのチェックノードが用意されていますが、このチェックではまだ初期化前のオブジェクトでもtrueで返って来る事がある為、
『00000000000000000000000000000000(34文字)』との比較を行方が確実です。
終わりに
作業効率を考える際には、レベルシーケンスの自動的な構築は、視野に入ってくる作業だと思います。
しかし、ブループリントからレベルシーケンサへのアクセスのワークフローは、あまり見かけません。
これから、レベルシーケンスをブループリントからさわる必要がある人の助けになれば幸いです。
また。Actorクラスから継承したブループリントについては、罠が結構ある為、気を付ける点などの記事を、
今後、いくつか投稿していこうと思います。
明日は、ねむさんになります。
よろしくお願いいたします。