Search Unity

Unity 2017.1 と同時に Timeline がリリースされて以降、数多くのフィードバックをお寄せいただきました。Unity では多くのデベロッパーの方々との話し合いやフォーラムでのユーザーの皆様とのやり取りを通して分かったのは、多くの皆様が、Timeline を単なるシーケンシングツール以上のものとして使用したいと考えているということです。私はこれに関して今までにいくつかの講演(Unite Austin 2017 など)を行い、一部のユーザーの方々に向けて Timeline の使い方を説明した記事もブログにて発表しました。本記事では、Timeline を使用してダイアログを制御したり、分岐に対応したり、さらに Timeline をゲームの AI システムと接続する方法をご紹介します。

Timeline は元々、拡張性を持たせることを大きな目標として開発されました。この機能を設計したチームは、ユーザーの皆様が「組み込みのものだけでなく独自のクリップやトラックをビルドしたい」というニーズを持つことを常に念頭に置いていました。このため当然ながら、Timeline のスクリプティングに関して多くの質問が寄せられています。Timeline の基盤となっているシステムは強力なものですが、同機能に慣れていない方にとっては、扱いが難しくなることもあります。

しかし、まず Timeline とは何でしょうか?Timeline は、アニメーションクリップ、音楽、サウンドエフェクト、カメラショット、パーティクルエフェクト、さらに他の Timeline などの、異なる要素をシーケンスするためのリニア編集ツールです。基本的には Premiere®、After Effects®、Final Cut® などのツールと非常に似ていますが、リアルタイム再生用に開発されている点が異なっています。

Timeline についての基本的な解説は、Unity マニュアルの Timeline に関するドキュメンテーションをご覧ください。本記事では、この機能の基本的な概念をさらに一歩踏み込んでお話して行きます。

Playable API

Timeline は Playables API をベースに実装されています。

これは、アニメーションやオーディオなどの複数のデータソースを読み出し、ミックスしてひとつの出力を通して再生できる強力な API です。このシステムは、精密なプログラムによる制御が可能で、オーバーヘッドが低く、パフォーマンスに焦点を当てて設計されています。ちなみに骨組みは、Animator コンポーネントを動かすステートマシンの背後にあるものと同じですので、Animator 用にプログラミングされたことがあるなら、聞き慣れた概念が登場するでしょう。

Timeline の再生が始まると、Playables と呼ばれるノードで構成されたグラフがビルドされます。これは PlayableGraph と呼ばれる、ツリーのような構造内に整理されます。

(注)シーン内の PlayableGraph(Animator、Timelines など)のツリーを視覚化したい場合は PlayableGraph Visualizer というツールがダウンロード可能です。本記事ではこのツールを利用して各種カスタムクリップのグラフを視覚化して行きます。Playable API とグラフについての(Animator との関連における)詳細は、Pierre-Paul のブログ記事をご参照ください。

ここからは、3 つの単純な例を使って Timeline の拡張方法をご紹介して行きます。まず基礎的な事項として、Timeline にスクリプトを追加する最も簡単な方法からご説明します。その後で他の概念にも順次触れて行き、最終的には大部分の機能をご活用いただけるようにしたいと思います。

アセット

本記事で使用する例をすべて含む小さなデモプロジェクトをパッケージにしました。ぜひダウンロードして参照しながら本記事をお読みください。もちろん、本記事だけでもお楽しみいただけますが。

(注)アセットは、異なる接頭語を付けることで例毎に区別しています(「Simple_」「Track_」「Mixer_」 など)。下記のコード内では読み易くするためにこれらの接頭語は省略しています。

例 1 ― カスタムクリップ

最初の例は非常に単純です。目的は Light コンポーネントの色と強度をカスタムクリップで変更することです。カスタムクリップを作成するには、以下の 2 つのスクリプトが必要です。

  • データ用のスクリプト(PlayableAsset から継承したもの­)
  • ロジック用のスクリプト(PlayableBehaviour から継承したもの)

Playable API の基本的な原則のひとつはロジックとデータの分離です。このためまず PlayableBehaviour を作成し、その中に以下のように行いたい事を記述する必要があります。

何が行われているのでしょうか?まず、Light のどのプロパティを変更したいかに関する情報があります。また、PlayableBehaviour はオーバーライド可能な ProcessFrame というメソッドを持っています。

ProcessFrame は更新の度に呼び出されます。このメソッドの中に Light のプロパティを設定できます。PlayableBehaviour 内でオーバーライド可能なメソッドのリストはこちらです次に、カスタムクリップ用の PlayableAsset を作成します(以下)。

1 つの PlayableAsset には 2 つの目的があります。1 つ目には、これは Timeline アセット自体の中でシリアライズされるため、クリップデータを含んでいます。2 つ目には、Playable グラフ内に入る PlayableBehaviour をビルドします。

最初のラインをご覧ください。

これが新しい Playable を作成し、それにカスタム挙動である LightControlBehaviour をアタッチします。その後 PlayableBehaviour のライトのプロパティを設定できます。

ExposedReference はどうでしょう?PlayableAsset はアセットなので、シーン内のオブジェクトを直接参照することは不可能です。ExposedReference は「CreatePlayable が呼び出された時にはオブジェクトが 1 つリゾルブされる」ことを保証する機能を果たします。

これで、Timeline に Playable Track を追加し、その新しいトラック上で右クリックしてカスタムクリップを追加することができます。クリップに Light コンポーネントをアサインすると結果を確認できます。

この例では、組み込みの Playable Track は、今作成したような単純な Playable トラックを受け取れる汎用トラックです。より複雑な状況になるとクリップを専用トラックにホスティングする必要があります。

例 2 ― カスタムトラック

最初の例に関して注意すべき点は、カスタムクリップを追加する度に各クリップに Light コンポーネントをアサインする必要があり、これが沢山あると骨の折れる作業になることです。これは、トラックのバウンドオブジェクトを使うことで解決できます。

あるトラックの構造

トラックは、それに紐付けられた(「バウンド」)オブジェクトまたはコンポーネントを 1 つ持つことができます。つまり、トラック上の各クリップが、そのバウンドオブジェクト上で直接実行可能になります。これは非常に一般的な挙動であり、アニメーショントラックやアクティベーショントラック、Cinemachine トラックはこの仕組みで機能しています。

あるライトのプロパティを複数のクリップで修正したい場合は、Light コンポーネントをバウンドオブジェクトとして求めるカスタムトラックを作成します。カスタムトラックを作成するには TrackAsset を拡張するスクリプトが別に 1 つ必要です(以下)。

ここには 2 つの属性があります。

  • TrackClipType は、トラックが受け取る PlayableAsset のタイプを指定します。ここでは、カスタムの LightControlAsset を指定します。
  • TrackBindingType は、トラックが求めるバインディング(紐付け)のタイプを指定します(ゲームオブジェクトかコンポーネントかアセットになります)。ここでは Light コンポーネントを指定します。

PlayableAssetPlayableBehaviour も、トラックと連動させるために若干修正する必要があります。ご参考のために、もう必要なくなっているラインをコメントアウトしました。

PlayableBehaviour には、もう Light 変数は必要ありません。ここでは、ProcessFrame メソッドがトラックのバウンドオブジェクトを直接提供しています。あとは、適切なタイプにオブジェクトを投げるだけで済みます。これはいいですね!

PlayableAsset は、もう Light コンポーネント用の ExposedReference をホールドする必要はありません。この参照はトラックによって操作され、直接 PlayableBehaviour に与えられます。

タイムライン内に LightControl トラックを追加し、それに特定のライトを紐付け(バインド)することができます。そのトラックに追加した各トラックは、同トラックに紐付けされた Light コンポーネントを操作します。

Graph Visualizer を使ってこのグラフを表示すると、こんな感じになります。

予想通り、右側の 5 つのボックス(クリップ)が 1 つのボックスに収束しています。この 1 つのボックスがトラックです。その後、すべてが Timeline(紫のボックス)に入ります。

(注) ピンク色の「Playable」というボックスは、実は Unity が自動で作成するミキサー Playable です。このため、クリップと同じ色になっています。ミキサーとは何でしょうか?次の例でミキサーについてお話します。

例 3 ― ミキサーでクリップをブレンドする

Timeline では、重なり合うクリップ同士をブレンドあるいはクロスフェードさせることができます。カスタムクリップもブレンドが可能です。ただし、これを行うにはブレンドされるすべてのクリップのデータにアクセスできるミキサーを 1 つ作成する必要があります。

ミキサーは、上記で使用した LightControlBehaviour 同様、PlayableBehaviour から派生します。そしてここでも ProcessFrame 関数を使用します。主な違いは、この Playable は、トラックスクリプトによって(関数 CreateTrackMixer をオーバーライドすることで)ミキサーとして明示的に宣言されていることです。 LightControlTrack スクリプトは、以下のようになりました。

このトラック用の Playable Graph が作成されると、新しい挙動(ミキサー)も作成されてトラック上のすべてのクリップに接続されます。

またロジックも PlayableBehaviour からミキサーへ動かします。結果、PlayableBehaviour は以下のようにシンプルになりました。

含まれているのは、ランタイムでPlayableAsset から来るデータのみです。一方ミキサーは、以下のように、その ProcessFrame 関数内にすべてのロジックを含むことになります。

ミキサーは、トラック上に存在するすべてのクリップにアクセスすることができます。この場合、現在ブレンドに寄与するすべてのクリップの Intensity と Color の値を読み込まなければならないので、for ループでそれらを順次処理する必要があります。1 回のサイクルごとに入力(GetInput(i))にアクセスし、その入力に各クリップのウェイト(GetInputWeight(i))を掛け合わせた値を足し合わせていくことで、そのクリップがブレンドに寄与する度合いを取得します。

2 つのクリップがブレンドされているとしましょう。1 つは赤に、もう 1 つは白に寄与しています。ブレンドが 4 分の 1 進んだ時点では、色は 0.25 * Color.red + 0.75 * Color.white で、若干フェードした赤になっています。

ループが終了すると、紐付けされた Light コンポーネントに合計値を適用します。これは以下のような結果になります。


赤いボックスは、プログラムした通りのミキサー Playable になっており、フル制御が可能です。これは上述の(ミキサーが Unity デフォルトのものであった)例 2 とは対照的です。

また、ブレンド途中のグラフなので、緑のボックス 2 と 3 の両方にミキサーに接続する鮮明な線が付いていることにもご注目ください。これは、これらそれぞれのウェイトが 0.5 前後であることを示しています。

ミキサー内にブレンドを実装する場合、どんなロジックにするかはあなた次第です。2 つの色のブレンドは単純ですが、例えば極端な例で、AI システム内の異なる AI ステートを表す 2 つのクリップのブレンドはどうでしょう?UI 内の 2 行のダイアログは?2 つの静的ポーズを 1 つのストップモーション・アニメーションにブレンドする場合は?あるいは、連続的なブレンドではなく、飛び飛びの値を取るブレンド(ポーズ同士がモーフィングされるが、それが 0・0.25・0.5・0.75・1 など離隔した値の増加で行われる)を行う場合もあるでしょう。

この強力なシステムがあれば、できることは無限大です!

例 4 ― カスタムクリップにアニメーションを付ける

本ガイドの最後のステップです。上述の例に戻り、「テンプレート」と呼ばれるものを使って、データを動かすための異なる方法を実装してみましょう。この方法の大きな利点のひとつは、テンプレートのプロパティをキーフレームできることです。このおかげで、カスタムクリップ用のアニメーションを直接 Timeline 上で作成することが可能となります。

上述の例の中では、Light コンポーネントへの参照があり、PlayableAssetPlayableBehaviour の両方の Color と Intensity がありました。データはインスペクター内の PlayableAsset で設定され、その上でグラフ作成時にそれがランタイムで PlayableBehaviour 内にコピーされていました。

これも有効な方法ですが、データが重複され、常にそれらの同期を維持しなければならなくなります。これでは間違いが起こり易くなります。代わりに、PlayableBehaviour の「テンプレート」という考え方を用いることも可能です。これを行うには PlayableAsset 内でそれへの参照を作成します。したがって、まず以下のように LightControlAsset をリライトしてください。

LightControlAsset は、値自体ではなく LightControlBehaviour だけを持つ状態になりました。以前よりさらにコードの量が少なくなりました!

以下のように、LightControlBehaviour は変更しないままにしてください。

Timeline 内でクリップを選択するとテンプレートへの参照によって以下のインスペクターが自動的に生成されるようになりました。

このスクリプトが整備されたら、アニメーションが付けられます。新しいクリップを作成すると、トラックのヘッダに赤い円形ボタンが表示されます。これは、Animator を追加しなくてもクリップにキーフレームを追加できるようになったことを意味します。これは、赤いボタンをクリックし、クリップを選択し、キーを作成したい場所に再生位置を動かして、そのプロパティの値を変えるだけで行えます。

また、白いボックスをクリックすると、カーブビューが展開され、キーフレームによって作られた曲線を確認できます(下図参照)。

もう一つ便利な機能があります。Timeline クリップ上をダブルクリックすると Animation パネルが開いてそれが Timeline にリンクされます。リンクされると以下のボタンが表示されます。

リンクされれば、再生位置マーカ―をTimeline ウィンドウと Animation ウィンドウの両方で同時に動かせます。同期が維持されますので、キーフレームのフル制御が可能です。Animation ウィンドウ内でアニメーションが修正できるので、キーフレームの操作がより快適になります。

このビューでは、アニメーションカーブとドープシートをフルに活用し、カスタムクリップのアニメーションの精密な調整が行えます。

(注)この方法でアニメーションを付ける場合、アニメーションクリップが作成されます。これはTimeline アセットの配下にあります。

まとめ

Timeline をスクリプティングでレベルアップさせることで開ける無限の可能性を、本記事でご紹介できたなら嬉しく思います。

ぜひ、ご質問やフィードバックを Twitter にお寄せください。また、Timeline を使った皆様の制作物も是非ご紹介ください!

10 コメント

コメントの配信登録

コメント受付を終了しました。

  1. Benjamin Sims

    10月 12, 2018 5:38 am

    This tutorial was quite helpful as it’s difficult to find a good written guide for getting started with custom scripting in Timeline.

    However, using this guide leaves the targeted objects/properties in the scene in a modified state. It would be great if you could cover how to revert objects to their original state after the timeline clip finishes, or when the timeline is no longer in preview mode.

    For example, the example scripts change the color and intensity of a light. There should be a way to revert the light’s color and intensity to it’s non-animated values after the clip has finished, or when preview mode is toggled off.

  2. Time and Event

    9月 23, 2018 6:09 pm

    Cool article. I would like to do something a bit complex using timelines (and/or animations, I hope you can help me!
    I need to make a counter tha goes form 0 to a Max in T seconds adapting the speed of the counter based on T and Max, eg. Min=0,Max=100,T=2 in 2 seconds the counter reaches Max, in case Max is 20 the counter speed is adapted to go from 0 to 20 in 2 seconds. Would it be possible?
    Also it is possible to have a timeline that animate something based on a condition?
    Thanks for any suggestions.

    1. Ciro Continisio

      9月 24, 2018 11:23 am

      Thanks!
      For the counter thing, do you want it to be on Timeline? It sounds like something that would be better as a regular script.
      On Timeline, that’s quite trivial because each frame you can take the currentTime/duration, and that would give you the progress on the clip (from 0 to 1). At that point you just need to multiply that value for your Max, and you get the current value on your counter (maybe you need to round the result too). More info on the API specific to Timeline here: https://docs.unity3d.com/ScriptReference/Playables.PlayableExtensions.html (see GetTime, GetDuration, etc.)

      For the second thing you mention, I have an example based on conditions right in this post: https://blogs.unity3d.com/2018/04/05/creative-scripting-for-timeline/

  3. CuriousOrange

    9月 6, 2018 2:01 pm

    Can this thing make function calls/send events from its time line yet?

    1. Ciro Continisio

      9月 6, 2018 3:06 pm

      With custom tracks you can do “pretty much anything”, including implementing your own system of callbacks (“onClipStarted”, “onClipUpdate”, “onBlend”, etc.).

      If you are referring to just adding events to the Timeline (like the ones in Animation Clips, represented by little markers) then that’s something that is not available right now, but will come in the future.

      1. George cook

        9月 8, 2018 5:58 am

        I solved the timeline events issue. Road tested live deployed solution in prod for almost a year. GitHub open source. Asset store link here. Enjoy. https://assetstore.unity.com/packages/3d/characters/timeline-events-115300

  4. “The reference to the template now automatically produces this Inspector when you select the clip in the Timeline”

    If it’s not showing up, remember to add [System.Serializable] to LightControlBehaviour

    1. Ciro Continisio

      9月 6, 2018 3:51 pm

      Thanks Bruno, good catch! We’ve updated the blog post, I will now update the assets too.

  5. “The reference to the template now automatically produces this Inspector when you select the clip in the Timeline”

    If it’s not showing up, remember to add [System.Serializable] to LightControlBehaviour

  6. Back in a day Valve introduced Source Filmmaker and the world met a huge stream of quality video content produced by the community. Seeing how Timeline gets polished brings the thought that the same would happen here.