LoginSignup
0
0

More than 5 years have passed since last update.

Gear VR Frameworkで視線選択を実装する

Last updated at Posted at 2017-02-27

Gear VR Frameworkで視線選択を実装するにあたって、ユーザーがオブジェクトを見ているかどうかを判定する必要があります。これはGVRPickerIPickEventsを使って簡単に実装できます。やることは次の4つです。

  1. IPickEventsを実装したクラスを作成する
  2. GVRScene.getEventReceiver().addListener()で1.のオブジェクトを登録する
  3. GVRSceneObject.attachCollider()で当たり判定を設定する
  4. GVRPickerのインスタンスを作成する

以下、順番に見ていきます。

IPickEventsを実装したクラスを作成する

IPickEventsインターフェースはユーザーがオブジェクトを見始めた時、見ている間、オブジェクトから目を離した時、といったタイミングでのコールバック処理を定義します。

class PickHandler implements IPickEvents {

    /**
     * オブジェクトを見始めた時に呼ばれる。
     *
     * @param sceneObj 見ているオブジェクト
     * @param pickInfo 見ている位置等の情報
     */
    public void onEnter(GVRSceneObject sceneObj, GVRPicker.GVRPickedObject pickInfo) {
    }

    /**
     * 何かを見ている間、毎フレーム呼ばれる。
     *
     * @param sceneObj 見ているオブジェクト。
     * @param pickInfo 見ている位置等の情報
     */
    public void onInside(GVRSceneObject sceneObj, GVRPicker.GVRPickedObject pickInfo) {
    }

    /**
     * オブジェクトから視線を外した時に呼ばれる。
     *
     * @param sceneObj 見ていたオブジェクト
     */
    public void onExit(GVRSceneObject sceneObj) {
    }

    /**
     * 何かを見始めた時に呼ばれる。
     *
     * @param picker
     */
    public void onPick(GVRPicker picker) {
    }

    /**
     * 何も見ているものがなくなった時に呼ばれる。
     *
     * @param picker
     */
    public void onNoPick(GVRPicker picker) {
    }
}

GVRScene.getEventReceiver().addListener()で1.のオブジェクトを登録する

先程作成したクラスをシーン中の視線選択に使用するために、シーン内で発生したイベントを受け取るEventReceiverにインスタンスを登録します。

scene.getEventReceiver().addListener(new PickHandler());

GVRSceneObject.attachCollider()で当たり判定を設定する

シーン中のオブジェクトを視線選択するためには、オブジェクトに当たり判定が必要です。一番簡単なのはオブジェクトに設定されている頂点情報をそのまま当たり判定に使用する方法です。そのためにはGVRMeshColliderを使用します。

sceneObject.attachCollider(new GVRMeshCollider(gvrContext, false));

GVRPickerのインスタンスを作成する

毎フレーム視線選択判定を行うようにするため、GVRPickerのインスタンスを作成します。

GVRScene scene = gvrContext.getMainScene();
new GVRPicker(gvrContext, scene);

インスタンスを作成するだけというのがとても違和感あるのですが、ソースを覗くとGVRPickerのコンストラクタの内部で毎フレーム視線選択判定を行うように自身をフレームワークに登録するGVRPicker.startListening()を呼び出していることがわかります。なので、インスタンスを作成するだけで動作するのです。

GVRPickerには視線選択を開始・停止するメソッドが用意されておらず、GVRPickerをコンポーネントとしてGVRSceneObjectattachComponentしたりdetachComponentしたりすることで間接的に視線選択の開始・停止を行えます。しかしインスタンスを作成した直後に自動的にも視線選択を開始するようになっていたりと、明らかに実装がおかしいです。

おそらく本来の使い方の想定に近いのは、このようにカメラやシーンのルートオブジェクトのコンポーネントとしてアタッチさせる方法だと思うのですが……このようにするとうまく動作しません。

動作しない例
scene.getMainCameraRig().getOwnerObject().attachComponent(new GVRPicker(ctx, scene));
scene.getRoot().attachComponent(new GVRPicker(ctx, scene));

これで一通りの実装ができました。最後にまとめて書いた例を紹介します。

実装例
import android.util.Log;

import org.gearvrf.GVRAndroidResource;
import org.gearvrf.GVRContext;
import org.gearvrf.GVRMain;
import org.gearvrf.GVRMeshCollider;
import org.gearvrf.GVRPicker;
import org.gearvrf.GVRRenderData;
import org.gearvrf.GVRScene;
import org.gearvrf.GVRSceneObject;
import org.gearvrf.IPickEvents;

public class Main extends GVRMain {
    private static final String TAG = "Main";

    /**
     * オブジェクトに視線を合わせたり外したりしたときに呼ばれる処理。
     */
    private class PickHandler implements IPickEvents {

        /**
         * オブジェクトを見始めた時に呼ばれる。
         *
         * @param sceneObj 見ているオブジェクト
         * @param pickInfo 見ている位置等の情報
         */
        public void onEnter(GVRSceneObject sceneObj, GVRPicker.GVRPickedObject pickInfo) {
            Log.d(TAG, "onEnter: " + sceneObj);
            sceneObj.getTransform().setScale(1.1f, 1.1f, 1.1f); // ちょっと大きくする
        }

        /**
         * 何かを見ている間、毎フレーム呼ばれる。
         *
         * @param sceneObj 見ているオブジェクト。
         * @param pickInfo 見ている位置等の情報
         */
        public void onInside(GVRSceneObject sceneObj, GVRPicker.GVRPickedObject pickInfo) {
//            Log.d(TAG, "onInside: ");
        }

        /**
         * オブジェクトから視線を外した時に呼ばれる。
         *
         * @param sceneObj 見ていたオブジェクト
         */
        public void onExit(GVRSceneObject sceneObj) {
            Log.d(TAG, "onExit: ");
            sceneObj.getTransform().setScale(1.0f, 1.0f, 1.0f); // 元の大きさに戻す
        }

        /**
         * 何も見ているものがなくなった時に呼ばれる。
         *
         * @param picker
         */
        public void onNoPick(GVRPicker picker) {
            Log.d(TAG, "onNoPick: ");
        }

        /**
         * 何かを見始めた時に呼ばれる。
         *
         * @param picker
         */
        public void onPick(GVRPicker picker) {
            Log.d(TAG, "onPick: ");
        }
    }

    @Override
    public void onInit(GVRContext ctx) throws Throwable {
        super.onInit(ctx);

        GVRScene scene = ctx.getMainScene();
        scene.getEventReceiver().addListener(new PickHandler());
        new GVRPicker(ctx, scene);

        // 視線選択カーソル
        GVRSceneObject cursor = new GVRSceneObject(ctx, 0.5f, 0.5f, ctx.getAssetLoader().loadTexture(new GVRAndroidResource(ctx, R.raw.cursor)));
        cursor.getTransform().setPosition(0, 0, -10);
        cursor.getRenderData().setRenderingOrder(GVRRenderData.GVRRenderingOrder.TRANSPARENT);
        scene.getMainCameraRig().addChildObject(cursor);

        // 選択ボタン
        GVRSceneObject btn1 = new GVRSceneObject(ctx, 1, 1, ctx.getAssetLoader().loadTexture(new GVRAndroidResource(ctx, R.raw.btn1)));
        btn1.setName("btn1");
        btn1.getTransform().setPosition(-3, 0, -10);
        btn1.attachComponent(new GVRMeshCollider(ctx, false));
        ctx.getMainScene().addSceneObject(btn1);

        GVRSceneObject btn2 = new GVRSceneObject(ctx, 1, 1, ctx.getAssetLoader().loadTexture(new GVRAndroidResource(ctx, R.raw.btn2)));
        btn2.setName("btn2");
        btn2.getTransform().setPosition(0, 0, -10);
        btn2.attachComponent(new GVRMeshCollider(ctx, false));
        ctx.getMainScene().addSceneObject(btn2);

        GVRSceneObject btn3 = new GVRSceneObject(ctx, 1, 1, ctx.getAssetLoader().loadTexture(new GVRAndroidResource(ctx, R.raw.btn3)));
        btn3.setName("btn3");
        btn3.getTransform().setPosition(3, 0, -10);
        btn3.attachComponent(new GVRMeshCollider(ctx, false));
        ctx.getMainScene().addSceneObject(btn3);
    }
}

IPickEventsの呼ばれる順番

IPickEventsのメソッドは、以下の順番で呼ばれます。

  1. onEnter
  2. onPick
  3. onInside (複数回)
  4. onExit
  5. onNoPick
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0