太郎Work

Unityとかで困ったこと等を残しておきます

UnityTimelineのTrackをスクリプトから入れ替える

現在Timeline関連の実装をしているのですが、Trackの順序をスクリプトから入れ替えたくなったのでメモ
多分こんなことしたい人いないと思うので需要ゼロです

入れ替えたい理由はPlayableOutputの接続順をプログラム的に変えたかったためです

本来はWindow内でTrackを掴むことで順番を入れ替えられます

f:id:tarowork:20200219134548p:plain
Timeline Window

このTrackを保持しているのはPlayableDirectorに指定しているTimelineAssetですが、このTimelineAssetにはMoveが用意されていません

docs.unity3d.com

悲しいことにCreateTrackとDeleteTrackしかなく、TimelineWindowの実装を追っていくと内部でMove的なメソッドは存在していますが呼び出しやすい感じにはなっていません…

そこでTrackを入れ替えたときのTimelineAssetの差分を見てみると `List m_Tracks` 中身を変更しているだけでした
f:id:tarowork:20200219144759p:plain

なので無理やりSerializedPropertyから書き換えることで入れ替えがうまくいきました。

var so = new SerializedObject(timelineAsset);
var tracks = so.FindProperty("m_Tracks");
tracks.MoveArrayElement(src, dst);
so.ApplyModifiedProperties();
so.Dispose();

これだけだとTimelineWindowの描画が更新されないのでReflectionでTimelineAssetを再読み込みするメソッドを呼び出します
第4引数のboolをtrueにしておくことで実質Reload状態にすることが出来ました

var timelineWindowType =
    typeof(UnityEditor.Timeline.TimelineEditor).Assembly.GetType("UnityEditor.Timeline.TimelineWindow");
var window = EditorWindow.GetWindow(timelineWindowType);
var method = timelineWindowType.GetMethod("SetCurrentTimeline",
    BindingFlags.Instance | BindingFlags.NonPublic,
    null,
    new[] {typeof(TimelineAsset), typeof(PlayableDirector), typeof(TimelineClip), typeof(bool)},
    null);

method?.Invoke(window, new[] {timelineAsset, _director, (object) null, true});
<||