[Unity] Timelineでスキルアニメーションの演出を扱う

#概要
Timelineはシーンオブジェクトを制御して映像作品を作ることにフォーカスを当てていると思われる。
そのTimelineをゲーム用途に、さらにシーンに存在しない動的に読み込むプレファブに対して利用できるかを探る。

#環境
Unity 2017.1.2p1

#要件

- スキルの演出にはパーティクル、オーディオがあり、これらをFbxからインポートしたアニメーションと連動させる。
- スキルは複数あり、その状態によってスキルアニメーションのデータとなるTimelineを動的に読み込む。(切り替えるときもある)
- アニメーションの状態管理にAnimatorControllerを利用していてTimelineと併用したい。
- パーティクルは3Dオブジェクトの特定のボーンの下に生成したい。
- オーディオは3D音響ではない。
- パーティクルはプレファブ化(シーンオブジェクトではない)、インポートしたFBXのプレファブからプレファブを作ってそこにPlayableDirectorを追加する。

#結果
できる。(上記の条件なら)

ただPlayableDirectorの隠しパラメータであるExposedReferenceの仕様が詳細がなく不明な点が多い。
Timeline編集後にPlayableDirectorのあるプレファブを適用(Apply)しないと隠しパラメータが保存されないので、ただしく再生されない。(ヒューマンエラーのリスクあり)

##こういうのは難しいかも
ボディのオブジェクトと武器のオブジェクトがあり、ボディ側にPlayableDirectorがあり、武器側のパーティクルを制御したいときは、スクリプトによる実装で連携する等の作業が必要になりそう。


#手順
##Playable Directorの準備
インポートしたFbxにアニメーションがついているとき自動でAnimatorが付加される。
このAnimatorと同じゲームオブジェクトにPlayableDirectorを追加する。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/36154/f5d1c3c8-75c4-c66f-782b-a389dc53ef5f.png)

Timelineアセットは関連付けしていない。


##Timelineを作成する。
Timelineアセットを作成して、PlayableDirectorにセットする。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/36154/e4ce1044-07f5-58bb-4c74-e7a10b84976c.png)

「Animation Track」を追加して、そのトラックに先ほどのAnimatorを関連付ける。
さらに演出を追加したいアニメーションを追加する。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/36154/306442c4-193f-8619-7ecb-e66ae3dedebb.png)

メニュー「Add From Animation Clip」から追加できる。
D&Dでもできると思われる。

音声用のAudio Track, パーティクル用のControl Trackを追加する。

Control Trackのメニューから「Control Playable Asset Clip」を選択してControlPlayableAssetを追加する。
追加されたControlPlayableAssetを選択するとインスペクター画面に詳細が表示される。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/36154/c2283f3e-1d49-794a-76c1-4542817bfc2f.png)

ここでParent ObjectにボーンのGameObjectを、Prefabにパーティクルのプレファブを設定する。

音声はトラックにAudioClipをD&D。

すべての設定が終わったらPlayableDirectorからTimelineの設定を外す。
**そしてプレファブのApplyボタンを押す。**

![image.png](https://qiita-image-store.s3.amazonaws.com/0/36154/d1066178-73aa-d374-1d7f-206741e95ffd.png)

これはPlayable DirectorをDebugモードにするとみることができるExposed Referencesの値を保存するため。
この値をもとにシーンオブジェクトとTimelineアセットとを連携する仕組みになっている。

異なるAudioClipを再生する2つのTimelineの片方のみを実行したとき
Profilerでメモリ調査したが再生したもののみしかAudioClipが読み込まれていなかった。
とはいえ、ほかの条件ではScene BindingsやExposed Referencesに設定されたアセットに関連するアセットがメモリにプリロードされるかもしれない。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/36154/f79d0852-2507-bc0d-778a-a3cebfbd56d8.png)

外した状態でTimelineを選択すると設定したAnimatorの状態がみえなくなる。
またControlTrackに設定したボーンの状態も「None」になる。
(AssetBundleでMissingになったものをランタイム上で復元しようとする仕組みがUnityがあるけど、それと同じだと思いたい:sweat_smile:)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/36154/7cc533d3-c8d0-2285-420f-1c487892752e.png)


##Timelineを動的に読み込む

```csharp
        // PlayableDirectorとAnimatorは事前に関連づけといてください。
        PlayableDirector _playableDirector;
        Animator _animator;

        IEnumerator LoadSkillData(string skillPath)
        {
            ResourceRequest request = Resources.LoadAsync<PlayableAsset>(skillPath);
            yield return request;

            PlayableAsset timeline = request.asset as PlayableAsset;
            Assert.IsNotNull(timeline, "Failed to load PlayableAsset.");

            _playableDirector.playableAsset = timeline;
            _playableDirector.Play();

            // MEMO) IsPlaying()だとエラーが出た
            while (!_playableDirector.playableGraph.IsDone())
            //while (_playableDirector.playableGraph.IsPlaying())
            {
                yield return null;
            }

            _animator.Play("idle", -1, 0f);
        }
```

Timelineを動的に読み込んでPlayableDirectorに設定。
その後、再生して、再生後にAnimatorの特定の状態に戻す。
(サンプルでは"idle"の状態に戻している)

注:
ITimeControl.OnControlTimeStop(https://docs.unity3d.com/ScriptReference/Timeline.ITimeControl.OnControlTimeStop.html)
のタイミングでAnimator.Playを実行しようとすると「Animator does not have an AnimatorController」でて再生されなかった。



#わかったこと

- Timelineにすることで関連アセットの動的ロードが簡単に実装できる。
- ただし動的ロードのときのExposedReferenceの解決方法の仕様がまだ詳細がなく不安点があり。
- AudioClipはエディタを再生しないと確認できないので、いままでと変わらない。
- パーティクルはプレビューで確認できるので作業効率はあがる。