【Unity】【Timeline】ブレンドも可能なカスタムクリップを持つカスタムトラックの作り方

UnityのTimelineでブレンド可能なクリップを持つカスタムトラックを実装する方法です。

Unity2018.3.9

はじめに

Timelineを拡張する方法はいくつか手法があり、そのせいで最初は理解しづらい部分があります。
この記事では、その中でも最もスタンダードで出来ることの幅が広い拡張方法を紹介します。

また、TimelineはUnityのPlayableという仕組みをベースとしていますが、
Playableについては本記事では説明しませんので次の記事を参照してください。

light11.hatenadiary.com

もっとお手軽な方法も

この記事で紹介する方法は上述の通り最もできることの幅が広い拡張方法です。
ただその分実装方法も複雑なので、ブレンドなどできなくてもいい場合にはもっと簡単な方法も使えます。
これら簡単な方法については以下の記事にまとめていますので必要に応じて参照してください。

light11.hatenadiary.com

Clipを作る

まずClipを作ります。Clipとは下図の青枠の部分です。

f:id:halya_11:20190327004335p:plain

Clipを作るにはPlayableBehaviourを継承したクラスとPlayableAssetを継承したクラスを作ります。
まずPlayableBehaviourです。

using UnityEngine;
using UnityEngine.Playables;

// Serializable属性を付ける必要あり
[System.Serializable]
public class ExampleBehaviour : PlayableBehaviour
{
    //クリップごとの設定値のみを定義する
    public Color startColor;
    public Color endColor;
}

PlayableBehaviourはPlayableの機能であり、ScriptPlayableのふるまいを定義できます。
ただ、Timelineのカスタムトラックで使う際には、

  • Seializableアトリビュートを付ける
  • クリップごとに設定したいフィールドを定義しておく

ということだけ覚えておけばOKです。

次にPlayableAssetを継承したクラスを作ります。

using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Timeline;

public class ExampleClip : PlayableAsset, ITimelineClipAsset
{
    // 必ずpublic(レコードボタンが表示されない)でBehaviourを持たせる
    public ExampleBehaviour behaviour = new ExampleBehaviour();

    // このクリップの特徴を定義
    public ClipCaps clipCaps {
        get {
            // ブレンドに対応、タイムスケール変更に対応
            return ClipCaps.Blending | ClipCaps.SpeedMultiplier;
        }
    }

    public override Playable CreatePlayable(PlayableGraph graph, GameObject owner)
    {
        // BehaviourのPlayableを作って返すだけ
        return ScriptPlayable<ExampleBehaviour>.Create(graph);
    }
}

PlayableAssetはTimeline上の1つのクリップ情報を表し、先ほどのExampleBehaviourからPlayableを作って返すメソッドを持ちます。
また、後で使うので、ExampleBehaviourをpublicなフィールドとして保持しておきます。

Mixerを作る

次にMixerを作ります。
MixerもPlayableの概念で、PlayableとPlayableをブレンドする役割を持ちます。

using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Timeline;
using UnityEngine.UI;

public class ExampleMixerBehaviour : PlayableBehaviour
{
    public TimelineClip[] Clips { get; set; }
    public PlayableDirector Director { get; set; }

    public override void ProcessFrame(Playable playable, FrameData info, object playerData)
    {
        var graphic = playerData as Graphic;
        if (graphic == null) {
            return;
        }

        var time = Director.time; // Timeline全体の現在の時間
        var color = Color.black;
        for (int i = 0; i < Clips.Length; i++) {
            var clip = Clips[i];
            var clipAsset = clip.asset as ExampleClip; // クリップのアセット
            var behaviour = clipAsset.behaviour; // クリップが持つBehaviour
            var clipWeight = playable.GetInputWeight(i); // クリップのブレンドウェイト
            var clipProgress = (float)((time - clip.start) / clip.duration); // クリップの進行率

            if (clipProgress >= 0.0f && clipProgress <= 1.0f) {
                color += Color.Lerp(clipAsset.behaviour.startColor, clipAsset.behaviour.endColor, clipProgress) * clipWeight;
            }
        }

        graphic.color = color;
    }
}

今回の拡張方法では、前節のExampleBehaviourで何も処理を書かなかった代わりにMixerで前クリップ分まとめて処理を行います。
各クリップの情報はこのソースコードのようにして取得できるので、それらを使って好きなように処理できます。
今回の例ではクリップの進行率に応じて求めた色を各クリップのウェイトに応じてブレンドしています。

Trackを作る

最後にTrackを作ります。Trackとは下図の青枠の部分です。

f:id:halya_11:20190327010208p:plain

ソースコードは次のように書きます。

using System.Linq;
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Timeline;
using UnityEngine.UI;

[TrackBindingType(typeof(Graphic))] // コントロールする対象の型
[TrackColor(1, 0, 0)] // トラックの色
[TrackClipType(typeof(ExampleClip))] // 設定できるクリップの型(複数指定可能)
public class ExampleTrack : TrackAsset // TrackAssetを継承する
{
    public override Playable CreateTrackMixer(PlayableGraph graph, GameObject go, int inputCount)
    {
        // Mixerを作って返す
        var mixer = ScriptPlayable<ExampleMixerBehaviour>.Create(graph, inputCount);
        mixer.GetBehaviour().Clips = GetClips().ToArray();
        mixer.GetBehaviour().Director = go.GetComponent<PlayableDirector>();
        return mixer;
    }

    public override void GatherProperties(PlayableDirector director, IPropertyCollector driver)
    {
        // Timelineから外したときに値を戻したい場合はこのように書く
#if UNITY_EDITOR
        Graphic trackBinding = director.GetGenericBinding(this) as Graphic;
        if (trackBinding == null)
            return;
        driver.AddFromName<Graphic>(trackBinding.gameObject, "m_Color");
#endif
    }
}

TrackはCreateTrackMixer()で前節のMixerBehaviourからScriptPlayableを作って返します。
さらにこの時にMixerにClipの情報とPlayableDirectorを渡します。

また、GatherProperties()をオーバーライドすると対象のオブジェクトをTimelineから外した時に元の状態に戻すことができます。
エディタで開発しているときには地味に重要な機能なのできちんと書いておきたいところです。

結果

ここまででカスタムトラックが作成できました。
TimelineのAddメニューからExampleTrackが選択できるようになっています。

f:id:halya_11:20190327010929p:plain

またExampleClipも追加されています。

f:id:halya_11:20190327011005p:plain

クリップの設定値を調整し、次のようにGraphicの色を変えるカスタムトラックができました。

f:id:halya_11:20190327010836g:plain

ClipのDrawerを作る

クリップのインスペクタの設定値の部分は自由に拡張できます。

f:id:halya_11:20190327011223p:plain

方法としてはCustomPropertyDrawerを定義するだけです。
こちらは一般的なエディタ拡張なので詳細は割愛します。

using UnityEditor;
using UnityEngine;

[CustomPropertyDrawer(typeof(ExampleBehaviour))]
public class ExampleDrawer : PropertyDrawer
{
    public override float GetPropertyHeight (SerializedProperty property, GUIContent label)
    {
        return EditorGUIUtility.singleLineHeight * 2;
    }

    public override void OnGUI (Rect position, SerializedProperty property, GUIContent label)
    {
        var startColorProp = property.FindPropertyRelative("startColor");
        var endColorProp = property.FindPropertyRelative("endColor");

        var fieldRect = position;
        fieldRect.height = EditorGUIUtility.singleLineHeight;

        EditorGUI.PropertyField(fieldRect, startColorProp);

        fieldRect.y += EditorGUIUtility.singleLineHeight;
        EditorGUI.PropertyField(fieldRect, endColorProp);
    }
}

Default Playables

Asset StoreにDefault Playablesというアセットが公開されています。

assetstore.unity.com

これはUnity公式によるアセットで、Timelineの組み込みの機能だけは操作できないものを操作できるように
カスタムトラックやカスタムクリップが多数定義されています。

これらはTimelineを拡張するときにとても参考になりそうです。

またDefault Playablesには本記事で紹介したスクリプト一式ののテンプレートを簡単に生成できるウィザードもついているので、
たくさんカスタムトラックを作る場合には使うと便利かもしれません。

参考サイト

www.youtube.com

blogs.unity3d.com

関連

light11.hatenadiary.com

light11.hatenadiary.com

light11.hatenadiary.com