【Unity】Timelineの再生位置をスライダーで指定する


こんにちはっ🌟八ツ橋まろんですっ!

ARアプリを作っているときに、Timelineをスライダーで操作したくなったのでスクリプトを書きました。本記事ではそのコードと解説をします。

↓↓↓↓このコードによってこんなことができます↓↓↓↓(GIF)
(このGIFではアニメーションだけが入ったTimelineを使用しています)

・再生ボタン/一時停止ボタンの実装
・Timelineの進行度合いがスライダーに反映される
・スライダーの位置をクリックして変えるとTimelineの再生位置も連動する

以下、完成コード

TimelineUiController.cs
using System.Collections;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Playables;
using System; // Actionに必要

public class TimelineUiController : MonoBehaviour
{
    [SerializeField]
    PlayableDirector timeline;

    [SerializeField]
    Slider slider;

    [SerializeField]
    GameObject playButton;

    [SerializeField]
    GameObject pauseButton;

    double length;

    float sliderValue;
    float sliderValueStored;
    float time;

    private void Start()
    {
        length = timeline.duration;
        Play();
    }

    void Update()
    {
        sliderValue = slider.value;
        time = ConvertDtoF(timeline.time);

        if(time >= 0.995 && timeline.extrapolationMode != DirectorWrapMode.Loop)
        {
            playButton.SetActive(true);
            pauseButton.SetActive(false);
        }

        if (sliderValue != sliderValueStored)
        {
            sliderValueStored = sliderValue;
            time = sliderValue;
            SetTimelineTime(time);
        }
        else if(time != sliderValueStored)
        {
            slider.value = time;
            sliderValueStored = time;
        }
    }

    public void Play()
    {
        timeline.Play();
        playButton.SetActive(false);
        pauseButton.SetActive(true);
    }

    public void Stop()
    {
        timeline.Stop();
        PlayOneFrame();
    }

    public void Pause()
    {
        timeline.Pause();

        time = ConvertDtoF(timeline.time);
        sliderValue = time;
        sliderValueStored = time;
        playButton.SetActive(true);
        pauseButton.SetActive(false);
    }
    public void LengthReset()
    {
        length = timeline.duration;
    }
    void PlayOneFrame()
    {
        Play();

        //1フレーム後にPauseする
        StartCoroutine(DelayMethod(1, () => {Pause(); }));
    }

    private IEnumerator DelayMethod(int delayFrameCount, Action action)
    {
        for (var i = 0; i < delayFrameCount; i++)
        {
            yield return null;
        }
        action();
    }

    void SetTimelineTime(float f)
    {
        double d = ConvertFtoD(f);
        timeline.time = d;
        PlayOneFrame();
    }

    double ConvertFtoD(float f)
    {
        double d = (double)(f * length);
        return d;
    }

    float ConvertDtoF(double d)
    {
        float f = (float)d / (float)length;
        return f;
    }
}


使い方

[SerializeField]
PlayableDirector timeline;

[SerializeField]
Slider slider;

[SerializeField]
GameObject playButton;

[SerializeField]
GameObject pauseButton;

上記にPlayableDirectorとスライダー、再生ボタン、停止ボタンをあてがってください。

解説

Timelineの総時間は

length = timeline.duration;

で取得できます。ただし、これはdouble型で得られるため、スライダーと連動したいならfloat型に変換しないといけないので注意。

float ConvertDtoF(double d)
{
    float f = (float)d / (float)length;
    return f;
}

timeline.timeで現在の再生位置をdouble型で取得し、総時間で割ったあとにfloat型に変換して、0~1にしています。

time = ConvertDtoF(timeline.time);

スライダーを触ってtimelineの再生位置を変更する場合、timeline.Pause();してtimeline.time = xxx;だけではスライダー位置が動かないため、その後に1フレームだけ再生することでスライダー位置を動かすようにしています。

おわりに

Timelineの再生位置制御の需要は、なかなかないと思いますが、Animation制御を越えてカメラワークやエフェクトの巻き戻しとかを任意でやりたい場合に使えます✨よかったら使ってみてください。

八ツ橋まろん