【Unity】PlayerLoopのタイミングをシリアライズしてInspectorから指定

PlayerLoop便利です。昔はイベント拾うためだけのMonoBehaviourを生成したりしてましたが、もう要りません。 tsubakit1.hateblo.jp

UniTaskでもめちゃ使ってるようです。多分YieldやDelayの終了通知だと思う
neue cc - C#のasync/await再考, タイムアウト処理のベストプラクティス, UniTask v2.2.0

これ、コードからだけじゃなくInspectorからも指定したくないですか…?

できたもの

コード

PlayerLoop挿入くん。シリアライズしてEditorからタイミング指定できる。

使い方

SerializablePlayerLoopをどっかにシリアライズして持っておくと…

[SerializeField]
SerializablePlayerLoop updateMethod = new SerializablePlayerLoop();

こんな感じでドロップダウンから選べるようになります。

スクリプトでは下記のようにイベントをaddしておけば指定したタイミングで呼んでくれます。

updateMethod.OnPlayerLoop += UpdateMethod;

やってること

上記で貼ったコードですが、3つのクラスがあります

PlayerLoopInjector

PlayerLoopを差し込んだり、差し込んだのを外したりします。 タイミングの指定にはUnityEngine.PlayerLoop以下の型を指定します。 例えば↓とか
PlayerLoop.Update - Unity スクリプトリファレンス

PlayerLoopは一度挿入するとドメインリロードまで刺さりっぱなしになるのでPlayMode終了時に初期状態に戻す処理を入れてます。

SerializablePlayerLoop

シリアライズ対象とする型です。 この中のtimingフィールドがSerializeReferenceになってますが、この属性を持ってるとUnityが型情報もシリアライズしてくれます。 その型情報を使って上記のPlayerLoopInjectorに処理を差し込むようになってます。 型情報は見てますが中の値は使ってないです。

SerializablePlayerLoopDrawer

SerializablePlayerLoopをInspectorで表示する際の処理を書いてます。 具体的には、タイミング指定に使える型を収集してきてドロップダウンとして表示します。 UnityEditor.EditorGUI.Popupはオプション文字列に/を入れとくとグルーピングしてくれて便利です。

「タイミング指定に使える型」の候補は PlayerLoop.GetDefaultPlayerLoop() で取ってきてます。

おしまい

そういうわけで処理の実行タイミングをInspectorから指定する仕組みができました。 もともとは次のリポジトリ弾幕の更新タイミングをプロジェクト設定として指定するために用意したものです。 UpdateにしたかったりFixedUpdateにしたかったりプロジェクトごとに違うので。

github.com

自作ゲーム内でずっと修正を重ねながら使ってるものですが、この弾幕ももうちょっと整備したいですね…