CrossRoad

AR、MR、VR、Babylon.jsを中心とした技術ブログ。 If you're non-Japanese native guys, check "English" category.

Babylon.jsでOculus Questのコントローラを使ってオブジェクトを移動させる方法

Babylon.jsはWebVRコンテンツを作る仕組みが色々と揃っていますが、コントローラでオブジェクトを移動させる機能は入っていません。

そこで、Babylon.jsのメソッドを利用して作りました。今回は作り方を紹介します。

1. Babylon.jsにはコントローラのrayが当たったことを検出するメソッドがある

Babylon.jsでVRのコントローラを表示させ、インタラクションを有効にすると、このように白い線が出ます。

Example of raycast from vr controller in babylon.js

 var VRHelper = scene.createDefaultVRExperience(); // Babylon.jsでVR機能を使うときの宣言
 VRHelper.enableInteractions();//インタラクション有効

この白い線はUnityでいうraycastです。rayが当たったことを検出するメソッドonNewMeshSelectedを使うと、rayが当たったオブジェクトの情報を取得できます。

そこで、たとえばこのように書いておくと、rayが当たったとき、rayが離れたときでそれぞれselectedMeshに値を入れることができます。

var selectedMesh;
VRHelper.onNewMeshSelected.add(function(mesh) {
     selectedMesh = mesh;
});

 VRHelper.onSelectedMeshUnselected.add(function() {
     selectedMesh=null;
 });

2. コントローラの動きに同期させるには、オブジェクトを親子関係にする

Unityでは、以下のようなコードで親子関係を作ることができます。

childObject.transform.parent = parentObject.transform;

Unityの場合、childとparentは同じワールド座標系で配置されたままです。したがって今回やりたいことが実現できます。

Babylon.jsでもparentがあり、以下のように書くことで親子関係を実現できます。

childObject.parent = parentObject

しかし、仕様上childObjectがparentObjectのローカル座標系で表示されてしまいます。そのため、親子関係にはなるのですが、childObjectが予想しないところにワープしてしまいます。

Making mesh P a parent of mesh C changes the frame of reference for mesh C to the local axes of mesh P.

引用元:Use a Parent - Babylon.js Documentation

これを避けるため、コントローラ側から子としたいオブジェクトを指定して、座標変換が起きないようにします。

webVRController.mesh.addChild(selectedMesh); 

3. ボタンの押しっぱなしはonTriggerStateChangedObservableメソッドを利用する

前回の下記記事の2.3に書いた内容を元にします。

Babylon.jsでOculus Questコントローラの入力を取る方法 - CrossRoad

ただし、このときは単押しを取得する方法でした。今回は押しっぱなしを使いたいので、以下のstateObject.valueの値で比較します。

 webVRController.onTriggerStateChangedObservable.add((stateObject)=>{
           //途中省略
           if(stateObject.value > 0.01){
           //途中省略

4. 動作確認する

Oculus Questのブラウザから以下のURLにアクセスし、HMDアイコンを選択すると確認できます。ソースコードもみられます。

Babylon.js Playground

補足:Playgroundという仕組みを使っています。Playgroundについては別の記事で解説予定です。

以下は実行例の動画です。
www.youtube.com

5. 残課題

5.1 両手で使用することもできるが、しばらくすると固まってしまう

以下のif文をコメントアウトすると、両手のコントローラでオブジェクトを移動させることができます。

 if(webVRController.hand=="left"){  
}

しかし、何度か実行していると、以下のエラーが出て固まります。

maximum call stack size exceeded

どうもこの辺りが参考になりそうな可能性が高いですが、時間なくて追えていません。

【今日のバグ取り】 JavaScript でコールスタックが溢れていたのをどうにかした話 - 無駄と文化

わかったら追記します。

6. おわりに

過去の記事と合わせて、キー入力、テレポート移動、今回のオブジェクト移動など、WebVRでの基本的な動きがだいぶわかってきました。そろそろBabylon.jsを使ったゲームなど、もう少しアプリのようなものを試してみる予定です。

また、この機能をtwitterで紹介したところ、Babylon.jsのコミッターの方に声をかけていただきました。

近いうちにBabylon.jsのドキュメントにプルリクエストを投げる予定ですので、反映されたらそちらもご確認いただければと思います。