【PlayableAPI】カスタム(User)Playableの作り方

2023年現在、Unity側が用意しているPlayable APIには
アニメーションとAudioを再生するAPI(クラス群)が用意されており、これに加えて、
自分でPlayable APIの機構を使って何かを再生したい場合にカスタムするためのAPIが用意されている。 *1

この記事では、このカスタム(User)Playableの作り方をまとめました。

環境:Unity2021.3.3f1(win10)

PlayableBehaviourを継承したクラスを作成する

まずカスタムPlayableを作成するためにはPlayableBehaviourを継承したクラスを作ります。
このクラスには実際にPlayableで行いたい処理を書きます。

必要に応じて各メソッドをoverrideして行いたい処理を実装しよう。
よく使われれるのは以下の3つのメソッドです。

public class TestPlayableBehaviour : PlayableBehaviour
{
    public override void OnBehaviourPlay(Playable playable, FrameData info)
    {
        //開始時に一度だけ呼ばれる
    }

    public override void ProcessFrame(Playable playable, FrameData info, object playerData)
    {
        //このPlayableを実行している間、毎フレーム呼ばれる
    }

    public override void OnBehaviourPause(Playable playable, FrameData info)
    {
        //このPlayableが終了した時に1度だけ呼ばれる
    }
}

Playableには処理を実行している時間*2を設定でき(やり方は後術)、
各メソッドのパラメータで渡ってくるPlayableクラスのGetDurationでこの値を取得することができます。

またこのPlayableを開始してからの時間*3Playable#GetTimeで取得できます。


これを基本にここではImageの透過を調整してフェードインするPlayableを作ってみます。

public class FadePlayableBehaviour : PlayableBehaviour
{
    public Image fadeLayer;

    public override void OnBehaviourPlay(Playable playable, FrameData info)
    {
        this.fadeLayer.color = new Color(0, 0, 0, 1); //初期化:はじめは真っ暗に
    }

    public override void ProcessFrame(Playable playable, FrameData info, object playerData)
    {
        //全体時間÷実行時間で徐々に透過
        double alpha = playable.GetTime() / playable.GetDuration();
        this.fadeLayer.color = new Color(0, 0, 0, (float)1f - alpha);
    }

    public override void OnBehaviourPause(Playable playable, FrameData info)
    {
        //ProcessFrameで必ずアルファ値が0になることは保証されないので最後に必ず完全に透過
        this.fadeLayer.color = new Color(0, 0, 0, 0); 
    }
}

注意したいのは、OnBehaviourPauseで必ず終了処理を実装するということです。
ProcessFrameは毎フレーム呼ばれると書きましたが、仮に処理落ちした場合は
アルファ値が半端な状態でPlayableが終わってしまいますし、1度も呼ばれない可能性もあります。

作成したPlayableを実行する

作成したPlayableを実行するには
ScriptPlayableScriptPlayableOutputを使って、PlayableGraphに登録します。

   protected override void DoExecute()
    {
        //PlayableGraph を生成
        PlayableGraph playableGraph = PlayableGraph.Create("Example Playable");

        //ScriptPlayableを使って、自作したカスタムPlayableを生成
        ScriptPlayable<FadePlayableBehaviour > fadePlayable = 
                ScriptPlayable<FadePlayableBehaviour >.Create(playableGraph , 0);

        //ScriptPlayableOutputへ登録
        ScriptPlayableOutput output = ScriptPlayableOutput.Create(_playableGraph, "fader");
        output.SetSourcePlayable(fadePlayable);

        //カスタムPlayableに渡したい情報がある場合はこんな風に書けるよ
        fadePlayable.GetBehaviour().fadeLayer = fadeLayer; //この例ではフェード用のImage

        //実行時間を設定
        fadePlayable.SetDuration(2f); //この例では2秒かけてフェードする

        //実行!
        playableGraph.Play();
    }

ポイントは、ScriptPlayable<カスタムPlayable>#Createで作成すること
ScriptPlayable.GetBehaviourでカスタムPlayableが取得できるので外部から渡す情報があれば渡せることです。

またカスタムPlayableに限った話ではありませんが、
ScriptPlayable.SetDurationで実行時間を設定することができます。

*1:docs.unity3d.com

*2:アニメーションならアニメーションしている時間

*3:deltaTimeでもゲームを開始してからの時間でもないので注意