Three.jsとCANNON.jsで作る「VR3D脱出迷路アプリ」作成（その3）

# ウォークスルー機能の概要
「ウォークスルー機能」とは、３D空間内を実際に歩いているように３D空間内を移動したり、視点を変えられる機能のことです。

[「VR3D脱出迷路-ウォークスルー編」](https://youtu.be/kzPS4hdjUxk)

上の動画を見ていただくとわかると思いますが、前進・後退・左右旋回など、迷路の中を脱出するための「移動機能」を主に実装していくことになります。

# ウォークスルーの「基本的な仕組み」

これからウォークスルー機能を実装するための方法について説明をしていきますが、ウォークスルーを行うための「基本的な仕組み」は下図のようになります。

![画像1.jpg](https://qiita-image-store.s3.amazonaws.com/0/96997/612b3131-66bd-ac29-df7c-86f4726f77af.jpeg)

図の「球体」は、自分自身を表す「CANNON.js」のオブジェクトです。

立方体でも良かったのですが、球体の方が地面との摩擦が少ないため、動きがスムーズなのと、小さい加速度でゴロゴロと転がってくれて、壁に当たった時の挙動など、動きのバリエーションが多いため、今回は球体を選択しました。

この自分自身を表す球体は、「Three.js」で描画する必要はないため、画面には表示されない「透明なオブジェクト」です。

加速度とは、「単位時間の速度の変化量（率）」です。

今回は物理用語が多く出てきますので、逐次説明をしていきたいと思います。

カメラは球体の真上に地面から1.6mくらいの位置となるように配置しています。

1描画毎に球体の位置座標をカメラの位置座標に反映しているため、「CANNON.js」の球体が動くと「Three.js」のカメラも追従して動きます。

# 移動機能に必要な「ベクトルの概念」について
「前へ歩いているような動き（前進）」を行うためには、まず、「どの方向へ進むのか」を決める必要があります。

３D空間の中では、前へ進んだり、後ろへ後退する場合の方向を「**ベクトル**」を用いて表します。

ベクトルとは、「大きさと向きを持つ量」という意味なのですが、図で表すと下図のようなイメージになります。

![画像2.jpg](https://qiita-image-store.s3.amazonaws.com/0/96997/d076aad7-dec2-afd5-2747-c4f62cdfefdd.jpeg)

移動方向を示す赤い矢印が「ベクトル」を表しているのですが、赤い矢印の向いている方向が「進む方向」で、赤い矢印の長さが「加速度の大きさ（量）」を表しています。

このように向きと大きさをセットにして表せるのが「ベクトル」の特徴です。

# 前進機能の仕組み

それでは、プログラム上で「ベクトルの向きと大きさ」を表す方法について説明をしていきたいと思います。

下記のコードは前進するための自作関数です。

```
function forward(){
    theta = selfData.angle / 180 * Math.PI;
    selfObj.velocity.x = Math.cos(theta) * selfData.speed;
    selfObj.velocity.z = Math.sin(theta) * selfData.speed;
}
```

ここに出てくる変数（プロパティ）の意味は、

● theta(シータ）・・・座標軸に対しての回転角度（ラジアン）
● selfObj.velocity.x・・・前進時にX方向に与える加速度
● selfObj.velocity.z・・・前進時にz方向に与える加速度
● selfData.speed・・・前進加速度係数


となります。

ここで、「ラジアン」という言葉が出てきましたが、ラジアンは回転角度を「円の半径と弧の比」で表しています。

![画像3.jpg](https://qiita-image-store.s3.amazonaws.com/0/96997/b32abb0f-4bad-3906-8d1c-405546de3660.jpeg)

１ラジアンの角度は約57.295度です。

この角度は、360度を2π（2 × 約3.1415926535）で除した値と等しくなります。

なぜ、角度に「ラジアン」という値を使うのかと言うと、Javascriptのサインとコサインの値を計算してくれる関数「Math.sin()」と「Math.cos()」の引数にラジアンの値を指定する必要があるからです。

前進関数の１行目、
```
theta = selfData.angle / 180 * Math.PI;
```
は、自分の向いている方向（度単位）をラジアンに変換しています。

例えば、自分の向いている方向が座標軸に対して180度だとすると、
```
theta = 180 / 180 * Math.PI;
```
となるため、
```
theta = 1 * Math.PI;
```
となり、
```
theta = Math.PI(πラジアン）
```
となります。

# 前進方向の加速度を計算する方法

前進方向の加速度を計算するために、三角関数のサイン（SIN）とコサイン（COS）を使用します。
```
selfObj.velocity.x = Math.cos(theta) * selfData.speed;
selfObj.velocity.z = Math.sin(theta) * selfData.speed;
```
サインとコサインの値はJavascriptが計算してくれますが、それぞれの値が何を表しているのかと言うと、下図のようになります。

![画像4.jpg](https://qiita-image-store.s3.amazonaws.com/0/96997/509a1edb-5be2-7bcb-2033-42c6e99a429c.jpeg)

Θ（シータ）はカメラの向いている方向の回転角度を表していますが、この角度を変更することで、前進する方向を変化させることができます。

前進する方向を決めているのは、「加速度比」なのですが、X方向とZ方向の加速度比を変化させることで、進む方向が変化します。

下図の例のようにX方向とZ方向の加速度が等しい時は４５度方向へ進みます。

![画像5.jpg](https://qiita-image-store.s3.amazonaws.com/0/96997/9e1cd87a-b9b8-a98c-332b-1fe0f532964e.jpeg)

次図は、X方向加速度に対して、Z方向加速度が半分の場合の進む方向を表しています。

![画像6.jpg](https://qiita-image-store.s3.amazonaws.com/0/96997/afc33714-a294-0512-79bf-2d84529cd50b.jpeg)

このようにベクトルを足し合わせる方法を「ベクトルの加法」と言います。
力学の分野では「力の合成」と言われたりもします。

# 後進機能の仕組み

後進を行う自作関数は次のようになります。
```
function back(){
    theta = selfData.angle / 180 * Math.PI;
    selfObj.velocity.x = -1 * Math.cos(theta);
    selfObj.velocity.z = -1 * Math.sin(theta);
}
```
先ほど説明した前進関数のX方向とY方向の加速度に「-1」を掛けて、加速度係数を削除しているだけです。

前進方向と反対の方向に進みたいので、「-1」を掛けることで前進と反対の方向に進むことができます。

![画像7.jpg](https://qiita-image-store.s3.amazonaws.com/0/96997/cdfdd5a1-0719-de05-ae75-4bf2e6f4b065.jpeg)

# 左右旋回機能の仕組み

左右に旋回する自作関数は次のようになります。

```
function changeLookAt(){
    theta = selfData.angle / 180 * Math.PI;
    var posX = selfData.posX + Math.cos(theta);
    var posY = selfData.posY + Math.sin(theta);
    //カメラの視点（視線）を変える
    camera.lookAt(new THREE.Vector3(posX, selfObj.position.y + 0.6, posY));
}
```
回転角度をラジアンで表す「theta（シータ）」の計算方法は前・後進と同じですが、ラジアンの値を利用して計算をするのは、加速度では無くて、「**カメラの注視点の座標**」になります。

サインとコサインは「-1～1」の範囲の値になりますので、その値に自分の位置を加算することで、注視点の座標を計算しています。

![画像8.jpg](https://qiita-image-store.s3.amazonaws.com/0/96997/53406460-0645-1e26-7cb2-af35de7b8cf9.jpeg)

# これから実装する機能

迷路の中をウォークスルーができるようになるまでをザっと書いてきましたが、まだ3D空間を作って、その中を移動できるようにしただけなので、これから実装しないといけないものを考えていると思いのほかたくさんありますね。

・タイトル画面の作成
・タイトル画面のメニュー作成
・ゴール座標の設定
・ゴール判定
・ゴール時のメニュー表示＆処理
・ステージの追加
・テクスチャの追加
・制限時間を設けるためのタイマー機能

などなど、これまでご説明させていただいた内容を活かして、ご自身の「オリジナル迷路アプリ」を作ることもできるのではないかと思います。

実装をメインに進めてきましたので、コードを見ても冗長な部分がありますので、少しずつ直していきたいと思います。

何か新しい「迷路アプリ」を作成されましたら、ぜひご一報いただければと思います。
最後までお読みいただき、ありがとうございました。


[WebSite](http://www.vr3dmaze.com/qitta/article3/index.html)
[GitHub](https://github.com/sugiyama-daigo/vr3dmaze/tree/master/article3)

# INDEX
[その１（地面の作成）](http://qiita.com/DAI788/items/4d7397e7918eb9be6d81)
[その２（迷路マップの作成）](http://qiita.com/DAI788/items/8a2309155b543b76783d)
[その３（ウォークスルー機能の作成）](http://qiita.com/DAI788/items/1b302ed9840c65e0193c)