7080 + 1

ゲームプログラミングの記事を書いてます。

【Unity】Timelineのヘッドを手動で動かしたときだけ処理を仕込む【Timeline】

やりたいこと

Timeline の現在時間は PlayableDirector.time で取得することができます。
が、この値が普通に Timeline を再生して動いたのか、それとも手動で Timeline のヘッドを動かしたから動いたのかを区別することはできません。
(あるかもしれないけど見つけられなかった

そこで、Timeline を少しいじって手動で動かしたときのイベントを仕込んで、
イベントハンドラを設定できるようにしてみました。

普通に再生された分にはこのイベントは発火しませんが、ヘッドを Timeline ウィンドウから動かしたときに発火します。

前提

使用したバージョンはそれぞれ以下です。
Unity: 2020.3.19f1
Timeline: 1.5.5

前準備

まずは、PackageManagerから落としてきているであろう Timeline をいじれるようにします。
これは本題ではないので割愛しますが、こちら参考にしていただければと思います。
tsubakit1.hateblo.jp

Timeline側にイベントを仕込む

肝心になるのは TimelineWindow_TimeCursor.cs というファイルです。
差分をはっつけておきます

実際の差分はこちらから見れます。
Timelineのヘッドが動いたイベントを仕込む · atori708/Sandbox2020@889761e · GitHub

大事なのは OnChangedPlayHead ですが、クリックしたときとドラッグしたときで2箇所に仕込む必要があります。

また、イベントハンドラを削除する処理も一応仕込んでおきます。
Sandbox2020/TimelineWindow.cs at master · atori708/Sandbox2020 · GitHub

仕込んだイベントをフックする

リフレクション使って、追加したイベントにイベントハンドラを登録します。
ChangePlayHeadHandlerというイベントハンドラを使います。
これはただ単にログ出力をするだけのメソッドです。

・今回の場合、 OnChangedPlayHead を持ってる TimelineWindowのインスタンスを持ってきます。
TimelineWindow はシングルトンになっているので、CreateInstance() はせず、instance という名前を取得してきます。

Assembly assem = typeof(TimelineEditor).Assembly;

// TimelineWindowクラスのインスタンスを生成
Type timeilneWindowType = assem.GetType("UnityEditor.Timeline.TimelineWindow");
timelineWindowObj = timeilneWindowType.GetProperty("instance", BindingFlags.Public | BindingFlags.Static).GetValue(null);

・TimelineWindow のインスタンスから、 OnChangedPlayHead のイベントを取得します。

// OnChangedPlayHeadのイベントを取得
onChangedPlayHead = timeilneWindowType.GetEvent("OnChangedPlayHead");
Type tDelegate = onChangedPlayHead.EventHandlerType;

・OnChangedPlayHead にイベントハンドラを登録するには、イベントハンドラのデリゲートを作る必要があります。

// ハンドラを取得
MethodInfo miHandler = typeof(TimelinePlayHeadTest).GetMethod("ChangePlayHeadHandler", BindingFlags.NonPublic | BindingFlags.Instance);

// ハンドラーのデリゲートを作成
changedPlayHeadHandlerDelegate = Delegate.CreateDelegate(tDelegate, this, miHandler);

・作成後、イベント登録用のメソッドを取得し、イベントハンドラを引数として実行します。

// OnChangedPlayHeadのAddメソッドを取得
MethodInfo addHandler = onChangedPlayHead.GetAddMethod();

// ハンドラーを引数にAddメソッドを実行
System.Object[] addHandlerArgs = { changedPlayHeadHandlerDelegate };
addHandler.Invoke(timelineWindowObj, addHandlerArgs);

イベントハンドラを削除するための処理も、イベントハンドラ登録と同じような流れになります。

// OnChangedPlayHeadのRemoveメソッドを取得
MethodInfo removeHandler = onChangedPlayHead.GetRemoveMethod();

// ハンドラーを引数にRemoveメソッドを実行
System.Object[] removeHandlerArgs = { changedPlayHeadHandlerDelegate };
removeHandler .Invoke(timelineWindowObj, removeHandlerArgs );