Posted at

A-FrameでVR迷路を移動する

More than 1 year has passed since last update.

A-Frameの勉強として、VR迷路を作ってみました。

ソースコードはgithubで公開しています。


作るもの

A-Frameを使ったVR迷路



"スマートフォン(iOS,Android)+Cardboard"だけで操作できるようlook-controlsで視点を変えることで、camera(カメラ)の向いている方向に移動させます。


迷路の配置

碁盤目状の9×9マスに壁と通路を作るイメージで、<a-plane>の上に<a-box>を並べているだけです。

class="collidable"は、当たり判定のために付与しているクラスです。詳しくは、後で説明します。


index.html

<a-entity id="maze" position="-4 1 -10">

<a-box class="collidable" position="0 0 0" width="1" height="2" depth="1" color="#4CC3D9"></a-box>
<a-box class="collidable" position="0 0 1" width="1" height="2" depth="1" color="#4CC3D9"></a-box>
<a-box class="collidable" position="0 0 2" width="1" height="2" depth="1" color="#4CC3D9"></a-box>
<a-box class="collidable" position="0 0 3" width="1" height="2" depth="1" color="#4CC3D9"></a-box>
...
<a-box class="collidable" position="8 0 7" width="1" height="2" depth="1" color="#4CC3D9"></a-box>
<a-box class="collidable" position="8 0 8" width="1" height="2" depth="1" color="#4CC3D9"></a-box>
</a-entity>
<a-plane rotation="-90 0 0" width="100" height="100" color="#7BC8A4" static></a-plane>

上から見ると、こんな感じです。

localhost-8383-aframe-sample-index.html(iPad) (2).png


プレイヤーの移動

camera(カメラ)の向きからベクトルを計算し、移動速度(speed)を掛けることで移動量とします。

上下は考慮しないので、y軸の回転角から三角関数(あまり自信はない)で求めます。


index.js

var speed = 0.01;

var isIntersect = false;

function movePlayer() {
var camera = document.getElementById('camera');

if (camera && !isIntersect) {
var position = camera.getAttribute('position');
var rotation = camera.getAttribute('rotation');

position.x += -Math.cos((rotation.y - 90) * Math.PI / 180) * speed;
position.z += Math.sin((rotation.y - 90) * Math.PI / 180) * speed;
camera.setAttribute('position', position);
}
}


A-FrameはHTMLで記述されているため、getAttributesetAttributeで移動・回転ができます。

isIntersectは壁との当たり判定の結果を格納する変数です。こちらも後で説明します。

movePlayer()は、くり返し実行する必要があるので、renderメソッドを作成してループさせます。


index.js

var t = 0;

function render() {
t += 0.01;
requestAnimationFrame(render);
movePlayer();
}
render();



プレイヤーと壁の当たり判定

camera(カメラ)にraycasterを追加して、視線(=進行方向)と交差するオブジェクトの有無を判定します。


index.html

<a-entity id="camera" camera look-controls raycaster="objects: .collidable; far: 0.5;" collider-check rotation="0 0 0" position="0 0.3 0"></a-entity>


まず、raycasterのプロパティについて説明します。



  • objects: .collidable;

    当たり判定の対象を指定するプロパティです。

    class="collidable"が付与されたオブジェクトを対象にしており、迷路の壁にあたる<a-box>は当たり判定の対象、床にあたる<a-plane>は対象外となります。


  • far: 0.5;

    当たり判定を行う距離です。

    デフォルト値が"Infinity"となっていて、変更しないと視線に壁があるだけで衝突と判定されてしまいます。

次に、当たり判定用のコンポーネントとして、collider-checkを設定します。


index.js

AFRAME.registerComponent('collider-check', {

dependencies: ['raycaster'],
init: function () {
this.el.addEventListener('raycaster-intersection', function () {
isIntersect = true;
});
this.el.addEventListener('raycaster-intersection-cleared', function () {
isIntersect = false;
});
}
});

raycasterのプロパティで設定した距離内に交差オブジェクトがある場合、raycaster-intersectionイベントが発生します。この間、isIntersecttrueになるようにします。

同様に、交差オブジェクトがない場合、raycaster-intersection-clearedイベントが発生しますので、isIntersectfalseにします。

先程、プレイヤーの移動を行う条件式に!isIntersectを入れていたので、"壁に近付くと止まる"、"前方に壁がなければ前進する"という挙動になります。

これで、コーディングは完了です。


ビルド&デプロイ

gulpを使ってビルドしました。

記事の内容から逸れるので、詳細は割愛します。

$ gulp default

動作確認のためにWebサーバを用意するのも面倒なので、Amazon S3に必要なファイルだけアップしました。


  • dist/app.min.js

  • index.html


迷路に入ってみる

スマートフォン実機(iPhone SE)で、index.htmlにアクセスします。

IMG_1326 (1).png

向きを変えることで、ちゃんと迷路を抜けられました。

ただ、アドレスバーが邪魔ですね。

気を取り直して、VRモードに切り替えます。

視差効果で左右の表示が異なっているのがわかります。

IMG_1327.png

最後に、以前に自作したCardboard初号機にセットして見てみます。

IMG_1328.JPG

立体的に見えた!

...けど、没入感はあまりないかな。


まとめ

A-Frameを使ってVR体験できる迷路を作りました。

ほとんどJavaScriptを書かずに実装できたので、three.jsと比べて、スッキリと見やすく記述できそうです。

移動や当たり判定は、ゲームやナビゲーション等にも応用できそうですし、何かの役に立てば幸いです。

余力があれば、スタート・ゴール時の処理を追加してタイム計測したり、ステージを切り替えられるようにすると、ミニゲームっぽくなるかなと思います。