やりたいこと
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 );