はじめに
こんにちは、情熱開発部 プログラム1課の今井です。
もう1月も後半ですね、やっぱり年始めは時間の流れが早く感じられます。
やりたいことが積まれていって消化しきれず困ってしまいますね。
今回はそんな時間繋がりでUnityのTimelineをスクリプトで動的に管理する方法を紹介していきたいと思います。
Timelineを動的に管理したいシチュエーション
Timelineを動的に管理したいシチュエーションとして、再生中にTimelineで管理されている別のカメラワークに切り替えて、特定の配置したClipが開始したタイミングでエフェクトを表示させたい場合の実装手順を紹介していきます。
四方のカメラワークへ動的に切り替えて、特定のClip開始タイミングで爆発エフェクトを表示させたい
使用バージョン
※Unity 2021.3.16f1
※Timeline 1.6.4
Timelineとは
UnityのTimelineは音楽やアニメーションの再生、オブジェクトのActive切替等を時間で管理するための機能です。なのでムービーシーン等の固定演出で用いられることが多いです。しかし、スクリプトを用いることでTimelineを動的に管理することもできます。今回は演出の幅を広げるために、TimelineTrackのBindingやClip情報を取得する手順を紹介していきます。
実装する機能について
カメラを設定するCinemachineTrackの動的な切替
Cinemachineで作成した複数のカメラワークをTimelineで管理する場合に、同じTimelineの中から別のカメラワークへ変更できると便利なので、カメラが使用するTrackを動的に切り替える手順を説明します。
特定のClip開始タイミングの取得
特定のアニメーションやエフェクト表示時にイベントを起こしたい場合、その開始タイミングを使用するため、TimelineのTrack上にあるClipの開始時間を取得する手順を説明します。
実装手順
カメラを設定するCinemachineTrackの動的な切替
使用するシーンにはプレイヤーの四方にCinemachineのVirtualCameraを配置しています。この4つのカメラワークにTimeline上で切り替えていきます。
Timelineを作成して、CinemachineTrackと切り替え用のSignalTrackを追加します。TrackIndexは上からカウントされます。
Trackの追加手順
SignalEmitterの追加手順
SignalReceiverの追加手順
TrackIndexの順番
次は本命のTimeline管理用のスクリプトです。
SetCameraBinding()で次のIndexを算出してTrackにカメラを設定しています。
using UnityEngine.Playables;
using UnityEngine.Timeline;
public class TimelineController : MonoBehaviour
{
[SerializeField] private GameObject mainCamera;
[SerializeField] private PlayableDirector director;
[SerializeField] private List<int> cameraTrackIndexList; // timelineでcinemachineCameraを設定するIndexリスト
private int currentTrackIndex = 0; // 現在カメラが設定されているtimelineIndex
void Start()
{
TimelineAsset timelineAsset = director.playableAsset as TimelineAsset;
director.SetGenericBinding(timelineAsset.GetOutputTrack(cameraTrackIndexList[currentTrackIndex]), mainCamera);
// CinemachineTrackの状態をResetする
director.Stop();
director.Play();
}
public void SetCameraBinding()
{
int index = 0;
// 1 ~ (Listの数 - 1)の範囲
int i = Random.Range(1, cameraTrackIndexList.Count);
// 現在のindex + i がリストの数以上の場合、0に戻って余剰分を足す
if (cameraTrackIndexList.Count <= currentTrackIndex + i)
{
index = (currentTrackIndex + i) - cameraTrackIndexList.Count;
}
else index = currentTrackIndex + i;
TimelineAsset timelineAsset = director.playableAsset as TimelineAsset;
// 現在カメラが設定されているTrackのBindingをリセット
director.ClearGenericBinding(timelineAsset.GetOutputTrack(cameraTrackIndexList[currentTrackIndex]));
// 次のIndex設定
currentTrackIndex = index;
// 新しいTrackのBindingにカメラを設定
director.SetGenericBinding(timelineAsset.GetOutputTrack(cameraTrackIndexList[index]), mainCamera);
// CinemachineTrackの状態をリセット
director.Stop();
director.Play();
}
}
bindingの再設定後、directorのStop()とPlay()を行わないと反映されないので注意が必要です。
上記のスクリプトをオブジェクトにアタッチして設定を行います。
SignalEmitterの設定も行います。
CameraTrackIndexListで切り替え先のTrackIndexを設定しています。
これで実行する準備ができたので、実際にカメラワークが切り替わるか確認しましょう。
Timeline上でも問題なく切り替わっていることが確認できました!
特定のClip開始タイミングの取得
Timeline上の特定のタイミングでイベントを起こすにはSignalEmitterを活用するのが楽ですが、場合によっては特定のClipに紐づけてイベントタイミングを設定する必要があるかもしれません。その時のためにスクリプト上で取得する方法を紹介していきます。
先ほどのTimelineにActivationTrackを追加する形で実装していきます。上から6つ目なのでTrackIndexは5になります。この追加したTrackのClipが開始するタイミングで爆発が起こる演出を作成していきます。
次がスクリプトです。start()でエフェクトのインスタンス時間を設定しています。ToList()[0]の数値部分は同Track上に複数のClipが存在する場合に、別途変更する必要があります。
using System.Linq;
using UnityEngine.Playables;
using UnityEngine.Timeline;
public class ExplosionCreater : MonoBehaviour
{
[SerializeField] private PlayableDirector director;
[SerializeField] private int explosionIndex;
[SerializeField] private Transform explosionParent; // 爆発用親オブジェクト
[SerializeField] private GameObject explosionPrefab; // 爆発プレハブ
private GameObject explosionObj; // 爆発インスタンス
void Start()
{
TimelineAsset timelineAsset = director.playableAsset as TimelineAsset;
// Clipの開始時間取得
float explosionTime = (float)timelineAsset.GetOutputTrack(explosionIndex).GetClips().ToList()[0].start;
// Clip開始時間で実行
Invoke(nameof(CreateExplosion), explosionTime);
}
private void CreateExplosion()
{
explosionObj = Instantiate(explosionPrefab);
explosionObj.transform.SetParent(explosionParent, false);
}
}
上記のスクリプトをオブジェクトにアタッチして設定を行います。
それでは実行する準備ができたので確認していきましょう。
ActivationTrackのClip開始で正常に爆発が起きました!
終わりに
AnimationControllerと異なり、Timelineは固定のアニメーションを時間で管理する場合によく用いられるので、スクリプトで管理する機会は少ないかもしれませんが、いつか必要になった時に活用いただければ幸いです。
最後までご覧いただきありがとうございました!
参考サイト
【Unity】TimelineをScriptから扱うときのTips
【免責事項】
本サイトでの情報を利用することによる損害等に対し、
株式会社ロジカルビートは一切の責任を負いません。