kugi's notebook

やったこと、思ったことなどつらつらと書きます

UnityのTimelineを使ってMV製作した話

はじめに

こちらの記事はUnityゲーム開発者ギルド Advent Calendar 2022の11日目の記事です。

adventar.org

今回はUnityのTimelineを用いてMV制作を行った話について書こうと思います。

NaTsu Beats

今年の1月ごろから妻が趣味で楽曲制作を始めました。

www.youtube.com

是非チャンネル登録をお願いします!!

制作を始めたきっかけについては妻のnoteをご覧ください。

ビートメイキングを始めた話|NaTsu Beats|note

妻はイラストを描くことも好きなので、イメージイラストやちょっとしたアニメーションも作成してYouTubeにアップロードしています。

私はMV制作のお手伝いをしていて、 先日公開された "A Brand New Day!" という楽曲ではUnityのTimelineを用いて作成しました。


www.youtube.com

TimelineでのMV制作

MV制作をTimelineでしよう!となった理由は単純にTimelineの勉強がしたかったからです笑

バージョンは以下のものを使用しました。

基本的に絵素材は妻が作成し、実装を私が担当しました。

動画の出力にはUnity Recorderを使用しました。

ソースコードについては以下のリポジトリに公開しています。

github.com

全体の流れ

MVの流れとしては、NA-CHANとアザ...セイウチのCHILL-BALLが1日かけて街や公園、森を巡るものになっています。

NA-CHANとCHILL-BALL

曲は既に出来上がった状態でのMV制作だったため、 どのタイミングで何を出すかといった指示メモを書いてもらいました。

指示メモ

場面の転換

今回場面としては大きく分けて4つありました。

4つの場面

NA-CHANとCHILL-BALLが歩くアニメーションを再生しながら、 背景スクロールで場面転換を表現しました。

背景スクロールについて

背景用のSpriteRendererを持ったGameObjectを2つ用意して、 スクロールさせつつ、前後を入れ替えることで実装しています。

スプライトのスクロール

スクロール処理はBackgroundMoverが処理しています。 LoopTimeで指定した時間でTransferDistだけ進み、後方の位置へ戻ります。

ロケット鉛筆の要領ですね!

FrontとBackを交互に入れ替える

背景スクロール用のTimeline拡張

Timelineウィンドウでシークバーをスクラブした場合にも再生中と同様に背景スクロールさせたかったため、拡張機能を実装しました。

今回のMV制作では曲の進行に合わせて場面を切り替えたり、スクロールを止める場面があったため、各クリップでの細かいタイミング調整が必要でした。 スクラブでの確認ができないと編集を行うたびに先頭から再生することになるので地獄を見るところでした…

Timelineウィンドウ上でスクラブしてもスクロールが反映されるように拡張

tips.hecomi.com

こちらの記事を参考に以下のTimeline拡張を行いました。

ドキュメントによると

PlayableBehaviourのProcessFrame()はPlayableGraphのProcessFrameフェーズで実行されるメソッド

のようです。

参照:Unity - Scripting API: Playables.PlayableBehaviour.ProcessFrame

これは再生時だけでなく、Timelineを再生していないEditorモードでも実行されるとのことでした。つまり、シークバーをスクラブする際にも実行されるようです。

参照:How to get Playable.GetTime() when not in play mode ? - Unity Forum

BackgroundTimelineBehaviour.ProcessFrame

BackgroundTimelineBehaviourではProcessFrame()内で背景スクロールの処理であるBackgroundMover.ScrollOnFixedFrameRate()を実行しています。

背景スクロール処理
BackgroundMover.ScrollOnFixedFrameRate()

信号の場面などで背景スクロールを停止している部分ではIsStopフラグがtrueになったクリップを割り当てています。

BackgroundMoverの処理を呼ぶBackgroundTimelineBehaviour

Timeline再生時のスクロール

Timeline再生中はMonoBehaviourUpdate()で呼び出すときのように、FrameData.deltaTimeを元にスクロール位置を計算しています。

Timeline停止時のスクロール

Editor上でスクラブをするようなTimelineが再生されていない状態ではFrameData.deltaTime0.0fになるため、そのままFrameData.deltaTimeは使用できませんでした。

Timeline停止中はProcessFrame()内で取得したクリップにおける再生位置(フレーム)を元にスクロール位置を計算しています。 また、BackgroundTimelineClipに現在のクリップまでにスクロールさせた分の経過時間(フレーム)を持たせることで、 スクロール停止クリップを挟んだ場合にも対応させています。

クリップが直前までの経過スクロール時間を保持している

背景スプライトの切り替え

スクラブで背景がスクロールできるようになったので、 次は各場面でスプライトを切り替えるための拡張を作成しました。

  • BackgroundSceneTrackSpriteRenderをバインドするスプライト切り替え用の拡張トラック
  • BackgroundSceneClip:切り替えるスプライトを指定する拡張クリップ
  • BackgroundSceneBehaviour:バインドしたSpriteRenderにクリップで指定したスプライトを割り当てるPlayableBehaviour派生クラス

スプライトの切り替え用の拡張

FrontとBack用のBackgroundSceneTrackを用意し、BackgroundSceneClipで指定したスプライトを各SpriteRendererに割り当てています。

FrontとBackの境目が画面に映らないようにフレーム送りでの細かい調整を行なったため、スクロール拡張がかなり役立ちました。

フレーム送りで境目が気にならないように調整

また制作序盤ではMarkerを使って通知を送ってシーンの切り替えを行おうとしていました。 しかし、スクラブなどを考慮するとトラックにしてしまった方が良いと思い、最終的には拡張トラックを作成しました。

今回は使用しませんでしたが、SceneSetNotificationはその名残です。

マーカー実装時には以下を参考にしました。

クリップ上にkani-chanが...!!

地面のつながりを表現するシェーダー

地面の表現としては上段・中段・下段用のスプライトを用意し、スクロールさせずにシェーダーで実装しました。

素材がドット絵風なテイストだったので、2色のグラデーションに対してポスタライズ処理を使ったシェーダーをShaderGraphで作成しました。

水平方向の2色グラデーションに対してポスタライズ処理をかけたシェーダー
Gradient.shadergraph

パラメーターは以下の通りです。

  • ColorRight:右端の色
  • ColorLeft:左端の色
  • T:ポスタライズ処理領域の中心 [0→1]
  • Width:ポスタライズ処理領域の幅
  • PosterizePower:ポスタライズ処理の度合い

このシェーダーを各スプライトに割り当て、背景スクロールに合わせてTを変化させるトラックを作成しました。

色情報を割り当てるための拡張

  • GroundColor:上中下段のマテリアルに対して色情報をセットするクラス
  • BackgroundColorTrackGroundColorClipChangeColorClip(後述)用の拡張トラック
  • GroundColorClip:地面用のスプライトにセットする色の指定やTを変化させる拡張クリップ
  • BackgroundColorBehaviour:地面の色の変更処理や空フィルターの色を変更する(後述)ためのPlayableBehaviour派生クラス

GroundColorClipで指定する色情報はGradientとして持たせていますが、内部的にはGradient.Evaluate()で取得した左端(time = 0)と右端(time = 1)の色のみを使用しています。

Tの変化に関してはBackgroundColorBehaviour.ProcessFrame内で以下のように計算してそのまま渡すことができました。

クリップの開始→終了を0→1とする

背景スクロールに合わせて地面シェーダーを変化させる

曲のリズムに合わせたキャラクター、小物の動き

MVなので途中に出てくるキャラクターや小物の動きを曲に合わせる点についてもこだわりました。 是非キャラクターたちの動きにも注目してみてください!

おすすめシーン

時間変化に合わせた空と色調補正について

ここでは1日の時間に合わせて情景の変化をさせるために、 空の色や色調補正をかけるための拡張クリップを作成しました。

  • ChangeColorClipTypeに応じて空の色変化や色調補正を行う拡張クリップ

TypeとしてはSKYCOLOR_ADDITIVESPRITE_ALPHAの3種類作成しました。

3種類のChangeColorClip

空の表現について

空については時間変化に応じて カメラのbackgroundColorプロパティを変更しグラデーションさせています。

ChangeColorClipのType.SKYでグラデーションを指定

地面シェーダーで使用したクリップ長を1としたときのクリップ位置tもここでも使用しました。

クリップの再生に合わせてグラデーションさせるために、 ChangeColorClipで設定されたGradientに対してGradient.Evaluate()Colorを取得する際のtとして渡しています。

色調補正について

ある程度MVの流れができ、最終的に色調補正をかけたくなりました。

はじめはURPのPost-processingに含まれるColorAdjustmentsを使用しようと思い、設定を進めていました。

URPのポストプロセス機能

しかし、ポストプロセス(Post-processing)の名前の通り、背景色が描画された後でしか適用できず、背景色を色調補正から除外することはできませんでした。

背景を含めない補正と含まれる補正
せっかく設定した空の色が変わってしまう

空(背景)の色については先程のグラデーション機能で色を決めているため、

「空(背景色)以外の描画結果に対して、Timeline経由で色調補正をかける」

がここで目指すべきゴールでした。

ただ、全てのスプライトを拡張トラックなどで指定して調整するのは途方もない作業かつ、スプライトが増えた場合への拡張性がありません。

最終的にRenderTextureを使った方法を採用することにしました。

カメラを2つ用意する

スプライトの描画結果をRenderTextureに出力するRenderTextureCameraを用意し、MainCameraで出力されたRenderTextureと背景色を描画します。

空の色の調整と色調補正のフロー

色調補正ではChangeColorClipType.COLOR_ADDITVEを使用して時間変化に合わせて、クリップで指定されたグラデーションを用いてRawImagecolorプロパティにセットしています。

まとめ

MVを作る上でこうしたい!という表現がたくさん出てきてそれに合わせる形でいろんな機能拡張に挑戦しました。

最終的に録画を行ったTimelineウィンドウ

初めてのTimeline拡張で冗長なことをしている部分も多々あったとは思いますが、 最終的に満足のいくMVを公開することができました。

また、かなり参考にさせていただいたTimeline拡張の記事を執筆されている凹みさんも先日Unityを用いてムービー作成をされた際の記事を公開されています。 (Happy Wedding!!🎉)

tips.hecomi.com

"A Brand New Day"のMV作成後、そこまで動きの多くないMV(Good Old Times / relaxing lofi beat - YouTube)をAffter Effectsで作ってみました。 他の方法でのMV作成も試してみて、凹みさんも書かれているように、サクッと簡単な動画を作るのであれば動画編集ソフトがやはり使いやすいなと感じました。

ただ、無い機能を自分で拡張して作ること自体は学びが多く、 いろんな小さな課題に対して解決策を考えていく過程はとても楽しい時間でした! チャレンジした分だけ得られるものはあったと感じています。

今回は"A Brand New Day"のMVのためのTimeline拡張の話だったので、限定的なものも多いかとは思いますが、 この記事がまたTimeline拡張へ挑戦する方の一助となれば幸いです。

それでは!