こんにちは、ZeniZeniです。
最近Unityのタイムラインをよくいじっています。
#やりたいこと
Unityのタイムラインをいじっていると、トラック上にクリップが一つだけしか存在してはいけないような制限を設けたいときがあります。(私はありました。)
さぁ作ろうと思って色々調べてみると、カスタムPlayable Trackでタイムラインのトラックを自作しないといけないことがわかりました。
こちらの動画が参考になります。
https://youtu.be/6SPpjSKy9LI
上の動画を参考にしながら、トラックにクリップが二つ以上置けない制限を設けた、シーン遷移を行うカスタムトラックを作成しました。
プロジェクトはこちらにあります。
https://github.com/Zeni-Y/TimelineCustomTrackTest
#実装方法
まず、作製したMixerとTrackのコードはこちらです。(プラスClipとBehaviourのコードもありますが、長くなるので割愛します)
using System.Collections.Generic;
using UnityEngine.Playables;
using UnityEngine.Timeline;
using System.Linq;
using UnityEngine.SceneManagement;
namespace ZeniZeni.CustomTrack
{
public class SceneManagementMixer : PlayableBehaviour
{
internal PlayableDirector m_playableDirector;
internal string m_scene;
internal LoadSceneMode m_mode;
internal IEnumerable<TimelineClip> m_clips;
private bool oneShot = true;
// NOTE: This function is called at runtime and edit time. Keep that in mind when setting the values of properties.
public override void ProcessFrame(Playable playable, FrameData info, object playerData)
{
if (m_clips.Count() >= 2) return;
int inputCount = playable.GetInputCount<Playable>();
if (inputCount == 0) return;
var time = m_playableDirector.time;
var enumulator = m_clips.GetEnumerator();
enumulator.MoveNext();
for (int i = 0; i < inputCount; i++, enumulator.MoveNext())
{
var clip = enumulator.Current;
var asset = clip.asset as SceneManagementClip;
m_scene = asset.m_scene;
m_mode = asset.m_mode;
if (clip.start <= time && time <= clip.end && oneShot)
{
SceneManager.LoadSceneAsync(m_scene, m_mode);
oneShot = false;
}
else if (time < clip.start || time > clip.end)
{
if (!oneShot) oneShot = true;
}
}
}
}
}
using System.Linq;
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Timeline;
namespace ZeniZeni.CustomTrack
{
[TrackColor(0.3523021f, 1f, 0f)]
[TrackClipType(typeof(SceneManagementClip))]
public class SceneManagementTrack : TrackAsset
{
public override Playable CreateTrackMixer(PlayableGraph graph, GameObject go, int inputCount)
{
var mixer = ScriptPlayable<SceneManagementMixerBehaviour>.Create(graph, inputCount);
var director = go.GetComponent<PlayableDirector>();
if (director != null)
{
SceneManagementMixerBehaviour bh = mixer.GetBehaviour();
//Track上のクリップを取得
bh.m_clips = GetClips();
//Clipが二つ以上あれば一つだけになるようにClipを新しい順に消す
if (bh.m_clips.Count() >= 2)
{
for (int i = 0; i < bh.m_clips.Count()-1; i++)
{
timelineAsset.DeleteClip(bh.m_clips.Last());
}
Debug.Log("You can put only one SceneManagementClip in this Track.");
}
bh.m_playableDirector = director;
}
return mixer;
}
}
}
クリップを二つ以上置けないようにする機能の実装方法ですが、クリップを作成したときに、そのトラック上にクリップが二つ以上存在すれば、一つだけになるようにClipを新しい順に消すような形で実装しています。
クリップを二つ以上置こうとすると以下のGIFのようになります。
ただ問題がありまして、インスペクターを表示した状態でこの動作を行うと、下の画像のようなエラーが発生します。
一応Clearで簡単に消せますが、気になっちゃいますよね。
理想としては、クリップが一つ存在すれば追加でクリップを作成できないような形にしたかったんですが、実装方法がわからず断念しました。
方法がわかり次第、追記していきます。