[Unity][Timeline] Timelineを再生と編集で環境を分ける設計について

この記事は [Unity #2 Advent Calendar 2018](https://qiita.com/advent-calendar/2018/unity2) の12/12(水)の記事です。

## はじめに

Unity Timelineを扱う上での実装方針・設計について話をしようと思います
実際のサービスの実装にTimelineの機能を使おうとしている人向けの内容です
Timelineの基本的な内容を説明しているとそれだけでとてつもなく長くなってしまうため色々と端折って説明していきます
予め御了承ください

## Unity Timelineとは

とは言いつつ概要は軽く触れておきます
UnityのTimeline機能は、時間に合わせたシーケンシャルな制御を行うことができるアセットの作成・編集・再生を可能にするUnityの機能です
[Unity 2017で新しく登場した](https://blogs.unity3d.com/jp/2017/07/11/introducing-unity-2017/)ばかりで、ムービーシーンやカットシーンなどを制作する際に活用できる機能です
詳しくは[公式マニュアル](https://docs.unity3d.com/Manual/TimelineSection.html)にも解説がありますのでそちらも参照してください

## `TimelineAsset`を再生する上での制約

ということでガリガリと本題に入っていきます
実際にTimelineの機能を使うには大雑把に説明すると以下のような作業を行います

- `TimelineAsset`を作成する
- **シーン上に配置された`PlayableDirector`**に`TimelineAsset`の参照を渡す
- Timeline編集ウィンドウを開いて上記の`TimelineAsset`を選択する
- TrackやClipを作成・編集する(**シーン上のオブジェクト**をTrackやClipにバインドする)
- `PlayableDirector` を再生して動作を確認する

かなりざっくりですが、これらのTimeline機能の内容を大きく分類すると**アセットとしてそのまま保存されるもの**と**シーン上のオブジェクトとの関係性(いわゆる参照関係)を示すもの**の2種類が混在していることが分かると思います

Unityの仕様上、デフォルトの挙動ではシーンとそれ以外のアセットの参照を繋ぐ手段はなく、ストレージに保存されたアセット側からシーンの中のオブジェクトの参照を保持しておくようなことはできません

Prefabなどを扱う場合はPrefabの中にはシーンの情報を含めず、Prefabを扱う側が用途に合わせてそのPrefabを配置したシーンとの参照関係を動的に解決させるような仕組みを作っているというケースが殆どだと思います

しかし、`TimelineAsset`はデフォルトの挙動として**シーン上のオブジェクトとの参照関係**の情報も必要になってくるのです

これを解決するために、Timeline(というかPlayables)の機能では[`ExposedReference<T>`](https://docs.unity3d.com/ScriptReference/ExposedReference_1.html)と呼ばれる仕組みを使っています

`PlayableDirector`がこの機能を扱うために[`IExposedPropertyTable`](https://docs.unity3d.com/ScriptReference/IExposedPropertyTable.html)のインターフェイスを継承しているため、標準の機能を使ってシーンと`TimelineAsset`を1対1のセットで作るだけであればこのあたりの仕組みについて深く理解していなくても使えるようにはなっています

1対1の関係で使うだけであれば...

## 動的に操作対象のオブジェクトを差し替えて再生したい

この**シーン上のオブジェクトと`TimelineAsset`の参照関係**の制約があるとややこしくなってくるのが、**Timelineで操作する対象を状況に合わせて動的に入れ替えたい**という要件です

例えば、ガチャ機能の演出部分をTimelineで実装するといった場合、演出としてはレアリティごとに`TimelineAsset`のデータを用意したいのですが、引いたキャラクターは同レアリティの中でも違うキャラクターはたくさんありますし、運用していくとそのラインナップが入れ替わっていくことも容易に想像できます

|登場人物|理想的な使い方|
|:--|:--|
|シーン|ガチャの機能を表現するために1つだけあれば十分|
|`TimelineAsset`|レアリティごとに演出を分けたいのでレアリティの数だけ用意したい<br>内部で扱うPrefabなどもできれば共通化して使い回したい|
|キャラクター|引いたキャラクターを自由に設定できるようにしたい<br/>他の機能でも使い回すので単体で`AssetBundle`にしておきたい|

Timelineの制約に合わせて1キャラクターごとにシーンを作るのは無駄が多く現実的ではないので、出来れば各要素を必要な数だけ作って実行時に動的に依存(参照)関係を解決できるような仕組みにしたいです

再生環境だけを考慮するのであれば、上記の要件に合わせて**参照を動的に解決する仕組みを実装**すれば問題なさそうに見えます

## TimelineAssetを編集するときは動的な参照解決などは無視したい

ただし、次に問題になってくるのがそのデータを実際に制作(編集)する際の環境です

実際に完成されたものを再生する場合は、納品された各種データ(AssetBundleだったり、サーバーからのレスポンスだったり)をロードしてきてそれらを1つずつ初期化して依存関係を解決していけば問題ないかもしれません

しかし、**それらのデータを制作している最中はまだ各種データが不完全**(AssetBundleになっていない、サーバーと繋ぎ込みしていないetc)な状態です

また、データを制作している最中はトライ&エラーを繰り返しながらデータのパラメータを調整して完成へと近づけていくので、動作を確認するために毎回完成されたデータとして納品フローを辿る(AssetBundleをビルドする、マスターデータを更新してデプロイするetc)のはとても時間がかかって非効率です

`TimelineAsset`を編集する環境では、そもそものTimelineの制約に則って、シーン上に直接オブジェクトを置いていき、シーンと`TimelineAsset`との参照関係を手軽に差し替えながらローカルの環境だけでトライ&エラーを繰り返せるようになっていたほうが効率が良いでしょう

## 再生環境と編集環境を分ける

以上のことから、実際にTimelineをなにかの機能(今回の例だとガチャ)に組み込む場合は、**完成データを再生する環境と編集データを扱う環境を明確に分けたほうがよいでしょう**

|環境|内容|
|:--:|:--|
|再生環境|サーバーに納品されたデータを取得して正しく再生できる|
|編集環境|ローカルにある編集中のデータを使って加工したり動作確認したりできる|

環境を分けるような実装をしようとすると、それぞれの環境に対しての保守が大変になるのでは？という懸念があるかもしれませんが、そこはエンジニアの腕の見せどころです

何が共通に扱えて、何が固有のものなのかをしっかりと整理して設計を進めましょう

最後に例として出したガチャ機能に合わせたTimelineの再生・編集環境の共通処理を整理したものを載せます

### 共通の機能
- TimelineAssetの再生機能

### 再生環境固有の機能
- 各種データの取得処理
 - ガチャの抽選結果(サーバーのレスポンス？)
 - レアリティに合わせた`TimelineAsset`(AssetBundle?)
 - キャラクターモデル(AssetBundle?)
- 各種データの参照関係の動的解決
 - `TimelineAsset`を`PlayableDirector`に設定
 - TrackやClipの参照解決(`PlayableDirector.SetGenericBinding`/`SetReferenceValue`など)
 - キャラクターモデルの配置
- 次のシーンへの遷移処理

### 編集環境固有の機能
- 好きなレアリティ演出・キャラクターを配置できる機能
 - AssetBundle化される前のデータを自由に入れ替えられる
- シーンに直置きされたデータをそのまま再生できる機能
- 繰り返し再生できる機能
 - Timeline編集Windowのデフォルトの機能を使って好きな場所で再生・一時停止ができるようになっていると良い
- 編集し終わった(完成した)データを納品できる機能
 - AssetBundle化やマスターデータの更新など

このようにして実装する機能の仕様に合わせた分類をしっかりと行うことで大きな問題は起きにくいと思います

## さいごに

文字ベースでガーッと説明してしまったためイメージが付きにくいところも多々あると思います
Advent Calendarには間に合いませんでしたが、後日サンプルプロジェクト的なものを用意して補足の説明を追加するつもりですので更に細かいところが気になる方はそちらを待っていただければと思います

**また「ウチではTimelineをこんな感じで使ってるよ！」という意見がありましたらぜひとも教えてください**
Timelineは登場して1年ちょっとたって少しずつ日本語の情報も増えてきていますが、まだ実践を伴った奥深い話というのはあまり表に出てきていない印象です
私の記事をきっかけに少しでも情報の交換のきっかけになってくれれば幸いです