UnityのタイムラインでPlayableBehaviour内でクリップタイミングを取得する方法

Unityのタイムラインのカスタムトラックを実装する際に、PlayableBehaviour内(特にTrackMixer内)でクリップの開始時間や終了時間を取得したくなったが、 簡単に取得できなかったので、メモがてらにブログに残すことにします。

PlayableBehaviour内でクリップタイミングを取得する

こちらのフォーラムの情報を参考にしました。

https://forum.unity.com/threads/how-to-access-the-clip-timing-start-end-time-in-playablebehaviour-functions.494344/

まず、お目当てのクリップのタイミングは、TimelineClipのプロパティより取得できます。

https://docs.unity3d.com/Packages/[email protected]/api/UnityEngine.Timeline.TimelineClip.html

具体的には、あるクリップの開始フレームが取得したければ、startプロパティから取得できます。

今回実現したいことへの課題は、TrackMixerからTimelineClipを参照できない点です。

ですが、TrackMixerを作成する際に通るTrackAsset.CreateTrackMixer内でGetClipsメソッドを利用することで、 各クリップのTimelineClipインスタンスを取得できます。

// https://forum.unity.com/threads/how-to-access-the-clip-timing-start-end-time-in-playablebehaviour-functions.494344/
public class LoopTrack : TrackAsset
{
    public override Playable CreateTrackMixer(PlayableGraph graph, GameObject go, int inputCount)
    {
        var clips = GetClips();
        foreach(var clip in clips)
        {
            // clipがお目当てのインスタンス
        }
 
        return ScriptPlayable<LoopMixer>.Create(graph, inputCount);
    }
}

TrackMixer内(この例ではLoopMixer)で触ることができるのは各クリップのPlayableBehaviourなので、 下記のLoopDataに示すように、Clipを定義します。

// https://forum.unity.com/threads/how-to-access-the-clip-timing-start-end-time-in-playablebehaviour-functions.494344/
public class LoopData : PlayableBehaviour
{
    // PlayableBehaviour内にクリップ情報を保持する変数を定義しておく
    [NonSerialized]public TimelineClip Clip;
}

では、CreateTrackMixerメソッド内で取得したTimelineClipをどうやって渡すかというと、 下記に示すように、クリップアセットであるPlayableAsset(この例ではLoopClip)に一度TimelineClipを渡してやり、 その後、クリップアセットからPlayableBehaviourを生成するPlayableAsset.CreatePlayableメソッド内で、 保持していたTimelineClipを渡すことで実現できます。

// https://forum.unity.com/threads/how-to-access-the-clip-timing-start-end-time-in-playablebehaviour-functions.494344/
[Serializable]
public class LoopClip : PlayableAsset, ITimelineClipAsset
{
    [NonSerialized]public TimelineClip clipPassthrough = null;
    public LoopData template = new LoopData ();
 
    public ClipCaps clipCaps
    {
        get { return ClipCaps.None; }
    }
 
    public override Playable CreatePlayable(PlayableGraph graph, GameObject owner)
    {
        // 保持しているTimelineClipをPlayableBehaviourに渡す
        template.Clip = clipPassthrough;
        var playable = ScriptPlayable<LoopData>.Create (graph, template);
   
        return playable;
    }
}
public class LoopTrack : TrackAsset
{
    public override Playable CreateTrackMixer(PlayableGraph graph, GameObject go, int inputCount)
    {
        var clips = GetClips();
        foreach(var clip in clips)
        {
            // CreateTrackMixer内で、各クリップの自身のTimelineClipをPlayableAssetに渡す
            var loopClip = clip.asset as LoopClip;
            loopClip.clipPassthrough = clip;
        }
 
        return ScriptPlayable<LoopMixer>.Create(graph, inputCount);
    }
}

こうすることで各々のクリップのTimelineClipに、トラックのPlayableBehaviourからアクセスすることができます。

// https://forum.unity.com/threads/how-to-access-the-clip-timing-start-end-time-in-playablebehaviour-functions.494344/

public class LoopMixer
{
    public override void ProcessFrame(Playable playable, FrameData info, object playerData)
    {
        var selectedPlayable = (ScriptPlayable<LoopData>)playable.GetInput(0);
        LoopData selectedPlayer = selectedPlayable.GetBehaviour();
        // indexが0のクリップのクリップタイミングを取得している
        // 先程定義したClipプロパティにTimelineClipが渡っている
        Debug.Log("Clip Start: " + selectedPlayer.Clip.start);
    }
}

まとめ

  • PlayableBehaviour内ではTimelineClipインスタンスを取得できないため、クリップの開始や終了タイミングを取得することができない
  • TrackAsset内でGetClipsメソッドによりTimelineClipが取得できるので、取得したクリップをPlayableBehaviourにわたすことで実現ができる