ノートの端の書き残し

UnityやらC#やら。設計が得意かもしれない。

【Unity】Timelineエディタ拡張【TimelineAction】

作るもの

UnityのTimelineで、クリップやトラックを右クリックでできる操作を追加します。

環境

Unity2022.3.4f1
Timeline1.7.4

Timelineバージョン1.4.0以降なら問題無いです。

自作クリップ

Colorfloatを設定するようなクリップがあったとします。

ここで、あるクリップに設定されてるfloatの値だけを他の沢山あるSampleClipにコピペしたいな~と思ったとします。

シナリオ

実際にコピペする際の操作は、

  1. 1つだけSampleClipを選択し、右クリックでメニューを表示。コピーコマンドを実行すると、floatだけコピーしてどこかに保存する。
  2. 1つ以上のSampleClipを選択し、右クリックでメニューを表示。貼り付けコマンドを実行すると、コピーされたfloatがあれば選択されている全てのSampleClipfloatに貼り付ける。

というものになります。

TimelineAction

こういった、クリップやトラックに対する動作はTimelineActionというクラスを継承して作成できます。 さきほどのコピーのクラスは以下のようになります。

using System.Linq;
using UnityEditor.Timeline.Actions;

[MenuEntry("SampleClip/ValueCopy")]
public sealed class SampleClipOnlyValueCopy : TimelineAction
{
    public static float? ClipBoard { get; private set; }

    public override bool Execute(ActionContext context)
    {
        // バリデーションで、1つSampleClipが選択されていることは保証されてる
        var sampleClip = context.clips.First().asset as SampleClip;
        ClipBoard = sampleClip.Value;
        return true;
    }
    
    public override ActionValidity Validate(ActionContext context)
    {
        // トラックは選択されてなくて、
        // クリップは1つだけ SampleClip が選択されてたら表示
        // それ以外は表示しない
        var hasTracks = context.tracks.Any();
        var isOnlyOneSampleClip = context.clips.Count() == 1 && context.clips.All(clip => clip.asset is SampleClip);
        
        return !hasTracks && isOnlyOneSampleClip ? ActionValidity.Valid : ActionValidity.NotApplicable;
    }
}

Executeで選択されているトラックやクリップに対しての操作を定義し、
Validateで、コマンドを表示するか、非表示にするか切り替えます。
今はSampleClipにだけ行う操作なので、それ以外のクリップを右クリックした際には表示すらさせないようにNotApplicableとします。

MenuEntryAttributeでどんな名前で表示するかを決めます。

次に、貼り付ける方のコマンドは以下のようになります。

using System.Linq;
using UnityEditor.Timeline.Actions;

[MenuEntry("SampleClip/ValuePaste")]
[ApplyDefaultUndo("SampleClipValuePaste")]
public sealed class SampleClipValuePaste : TimelineAction
{
    public override bool Execute(ActionContext context)
    {
        var copyValue = SampleClipOnlyValueCopy.ClipBoard.Value;
        foreach (var clip in context.clips)
        {
            var sampleClip = clip.asset as SampleClip;
            sampleClip.SetValue(copyValue);
        }

        return false;
    }

    public override ActionValidity Validate(ActionContext context)
    {
        // トラックは選択されてなくて、
        // クリップはSampleClipだけ選択されてたら表示
        // クリップボードに値が無い場合は表示はするけど選べない
        var hasTracks = context.tracks.Any();
        var isOnlySampleClip = context.clips.All(clip => clip.asset is SampleClip);
        var hasValue = SampleClipOnlyValueCopy.ClipBoard.HasValue;

        if (hasTracks || !isOnlySampleClip)
        {
            return ActionValidity.NotApplicable;
        }
        return hasValue ? ActionValidity.Valid : ActionValidity.Invalid;
    }
}

コピーと同じくValidateExecuteMenuEntryがありますが、少し増えているものがあります。

まず、Validateの結果にInvalidが増えました。このとき、表示はするけど実行できない状態になります。
今ですと、SampleClipを選択してるけどコピーされてる値が無い場合は貼り付けられませんから、実行だけ禁止します。

また、クラスの属性にApplyDefaultUndoAttributeが追加されました。これは、Executeした場合に勝手にその操作をCtrl + ZUndoできるよう登録してくれるものです。 コピーの際にはどうでもよかったですが、貼り付けはUndoできてほしいので付けました。

終わり

Timelineは一般的に非プログラマが触ることが多いと思いますので、手軽に扱えるように環境を整えてあげたいですね。

それにしても、割と活用頻度が多そうなAPIなのですが、調べても全然ユーザーの資料が出てこず、不思議です。
(ApplyDefaultUndoなんか便利なのに、後述Qiita記事のInvokerの中身を追わないと存在に気付かなかった)

参考資料

公式のAPIリファレンス
docs.unity3d.com

TimelineActionについてまとめてくださっているQiita記事 qiita.com