概要
便利なブリッジライブラリ作りました。
VRTK(VR Toolkit)のあらゆるイベントをUniRxで処理するためのライブラリです。VR開発をやっていて、かつ C# も好き!Rx好き!な方の心をちょいハッピーにしてくれるはずです。
中身の説明と簡単な使い方
中身は結構シンプルで、ただひたすらにVRTKが公開している(つまりpublicな)eventをObservableに変換する拡張メソッドがたくさんと、「メソッド名でコールバックを登録するイベントシステム」を持つクラスに対して、メソッド名ではなくObservableな形でコールバックを登録できるようにしたものがあるだけです。前者はEvent extensionsと呼んでいて、後者はRxVRTK componentsと呼んでいます。
これ、めんどくさすぎて誰もやってなさそうなので作りました。
もちろん手動で片っ端からイベントを調べて拡張メソッドを書くのはとても辛いので、アセンブリからリフレクション!するコードを書いて8割5分くらい自動化しました。いきあたりばったりの汚いコードですがさっきのリポジトリ漁ればでてきます。
自動化部分を文章にするとこんな感じでややこしい……。
VRTKの任意のクラスのイベントをUniRxのストリームに変換する拡張メソッドを実装するスクリプトを自動で生成するUnityエディタ拡張を書いた(ぐるぐる目)
— G2 (@G2U) November 11, 2018
拡張メソッドの使い方は簡単で、以下みたいに使ってやります。もともと+=
や-=
でコールバックを登録・解除していたものを、Observableにすることでただ購読するだけで良くなりました。
VRTK_ControllerEvents controllerEvents = GetComponent<VRTK_ControllerEvents>(); | |
controllerEvents.TriggerPressedAsObservable() | |
.Subscribe(_ => | |
{ | |
Debug.Log("Trigger Pressed!"); | |
}); | |
メソッド名でコールバックを登録するイベントシステムの話
eventをObservableなものにする拡張メソッドは良いとして、「メソッド名でコールバックを登録するイベントシステム」の方(RxVRTK components)の説明です。まあUnityの開発者の皆様ならご存知の通り、「メソッド名でコールバックを登録するイベントシステム」とはAwake()とかOnTriggerEnter()みたいなやつのことで、VRTKもそんな感じのイベントシステムが一部だけ実装されています。
UniRxはUnityの「メソッド名でコールバックを登録するイベントシステム」をTriggersという良い仕組みでラップしてUniRxのストリームに変換しています。とても便利です。
どういう感じかというと、GameObjectの拡張メソッドでイベントをObservableに変換するメソッドが定義されていて、その拡張メソッドを呼び出すと、イベントごとに対応する「Triggerスクリプト」が購読したい対象のGameObjectに貼り付けられ(もしくは既に貼り付いていたらその貼り付いているやつが参照され)ます。「Triggerスクリプト」の中ではOnTriggerEnterのような対象のイベントをフックしてUniRxのイベント(OnNext)を発火するようになっており、購読者はその発火されるUniRxのイベントを受け取るという形です。
最初RxVRTKでもこの方式でやろうと思い実装したのですが、よくよく考えてみると、VRTKの場合はUnityのイベントシステムとは少し趣が違うのでした。
VRTKのイベントシステムの話
Unityの場合、一つのGameObjectに対して複数のMonoBehaviourを継承するスクリプトが貼り付けられます。そしてそれぞれのスクリプトで「メソッド名でコールバックを登録するイベントシステム」を用いることができます。ですから、先程のUniRx.Triggersのように別でスクリプトを用意してそれ経由でイベントを変換するということができます。
一方でVRTKの場合、「メソッド名でコールバックを登録するイベントシステム」はGameObjectの性質を表現するスクリプトの中で用いられます。VRTK_InteractableObjectというやつなんですが、VRTK_InteractableObjectを継承したスクリプトをアタッチされたGameObjectは「インタラクティブ」な性質を持ち、VRのコントローラーから相互作用を及ぼせるようになります。
例えばVRTK_InteractableObjectのパラメータをいじることで「コントローラーから握れる」「コントローラーから使える」みたいな性質をオン・オフできます。こいつを貼り付けるとVR空間で簡単にものを持ったり使ったりできるようになるんですね。便利!
で、このVRTK_InteractableObjectを継承したスクリプトの中でvoid StartUsing(VRTK_InteractUse currentUsingObject);
というメソッドが定義されていると、StartUsingなタイミング(つまり最初に使う操作をしたタイミング)でこのメソッドが呼ばれます。これぞ「メソッド名でコールバックを登録するイベントシステム」ですね。例えばStartUsingの中で弾を撃つ処理をすればVR世界で使える銃が簡単に実装できるというわけです。
このVRTK_InteractableObjectは、それ自身がGameObjectの性質を表現する都合上、同じGameObjectに複数つけても意味をなしません。例えば「コントローラーから握れる」がTrueなVRTK_InteractableObjectと、「コントローラーから握れる」がFalseなVRTK_InteractableObjectは同じGameObjectに同居できませんよね?
というわけでTriggers方式は断念し、VRTK_InteractableObjectを継承するRxVRTK_InteractableObjectなるクラスを作って、そこで「メソッド名でコールバックを登録するイベントシステム」をフックしてUniRxのストリームに変換するようにしたのでした。ちゃんちゃん。「メソッド名でコールバックを登録するイベントシステム」自体をoverrideしているので、元の「メソッド名でコールバックを登録するイベントシステム」は使えなくなります。なのでこれを使う場合は絶対にObservableなメソッドを通してイベントを購読することになります。そこだけ注意ですね。
使い方はこんな感じです。
using RxVRTK; | |
using UniRx; | |
using UnityEngine; | |
public class SomeObject : RxVRTK_InteractableObject | |
{ | |
protected void Start() | |
{ | |
this.StartUsingAsObservable() | |
.Subscribe(user => | |
{ | |
Debug.Log(user.name + " is using this object!"); | |
}); | |
} | |
} |
まとめ
Reactive Extensionsは学習コストがそこそこありますし、私も全部理解して使っているわけではないのですが、とっても便利なのでぜひ使ってみましょう。
ちゃんと学ぶにはかずきさんの記事がおすすめです。UniRxにも搭載されているReactivePropertyの作者さんなので信頼できますね。
深く知りたいならneueccさんのブログ。Rx学んだ人なら誰でも一度は見たことがあるであろう黒背景に赤字のブログです。とってもためになります。UniRxの作者さんです。