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

# WEB GLで作る迷路アプリ

最近は、JavaScriptで利用できる物理エンジンが増えてきたこともあり、

「**3D脱出迷路アプリ**」

を、「Three.js」と「CANNON.js」を使用して開発する方法について書いていきたいと思います。

当初は「Three.js」だけで開発をしようと思っていたのですが、以前に「iPhone・Androidアプリ」を開発していて、「衝突判定の実装」が思いのほか重たかったため、、今回は物理エンジンを使うことにしました。

「oimo.js」や「Phisi.js」「ammo.js」など、さまざまな物理エンジンを試して、比較的新しくて物理計算速度が高いことと、実装がしやすい仕様だったため「CANNON.js」を使用することにしました。



◆[Three.js](http://threejs.org/)
◆[CANNON.js](http://www.CANNONjs.org/)


# ■「Three.js」と「CANNON.js」の関係
「Three.js」は、一から実装をするとかなり労力のかかる「WEB GL」を上手くラッピングしてくれる「WEB GLラッパーライブラリ」です。

以前に「WEB GL」を書いてみたことがありますが、「OPEN GL」の実装コードを書いていくので、「OPEN GL」について知識が無いと、そもそも使えないという前提があります。

「Three.js」ももちろん学習コストがかかりますが、「OPEN GL」を一から書くよりは格段に扱いやすいので、「迷路脱出アプリ」を開発するには欠かせない存在です。

そして、「CANNON.js」は物理計算をしてくれるJavaScriptで作成された物理計算プログラムです。

もともとCAEをやっていましたので、3Dの物理計算を自分で行う大変さを考えると、大幅に開発期間の短縮を行えるのが「CANNON.js」の魅力です。


# ■初めの目標は「迷路の中をウォークスルーできる」こと
今回ご説明をしていくのは「ウォークススルー」を行う部分。

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

迷路の中を、「左右旋回・前進後退・停止・ジャンプ」することができる機能を実装できるところまでをご説明していきたいと思います。

※ジャンプ機能は迷路全景を確認するための試実装機能です。



# ■迷路アプリに必要な要素
迷路アプリに必要な最低限の機能として、

・マップデータ
　- 壁の位置と大きさ
　- 地面の位置と大きさ
　- テクスチャ素材
・自位置データ
・視点方向データ
・前進・後退・停止機能
・左右旋回機能

などのデータや機能を作る必要があります。

今回は、地面を作成し地面に砂地の画像を表示する部分までをご説明していきたいと思います。

下図は今回開発した部分の画像です。

![画像1.jpg](https://qiita-image-store.s3.amazonaws.com/0/96997/7ae95b85-250a-d733-c11f-75ce4f153027.jpeg)

真ん中にある黒い横線は地平線。

下側の砂地部分は地面です。

##CANNON.jsの設定

CANNON.jsの設定部分は、

```
world = new CANNON.World();       //物理世界を作成
world.gravity.set(0, -9.82, 0);   //物理世界に重力を設定
world.broadphase = new CANNON.NaiveBroadphase(); //衝突している剛体の判定
world.solver.iterations = 10;     //反復計算回数
world.solver.tolerance = 0.1;     //許容値

//CANNON Ground--------------------------------------------------------------
var groundMat = new CANNON.Material('groundMat');    //マテリアルを作成
groundMat.friction = 0.3;       //摩擦係数
groundMat.restitution = 0.5;    //反発係数

phyPlane = new CANNON.Body({mass: 0});      //ボディを作成
phyPlane.material = groundMat;              //ボディにマテリアルを設定
phyPlane.addShape(new CANNON.Plane());      //地面を作成
phyPlane.quaternion.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), -Math.PI / 2 );   //地面を回転
world.add(phyPlane); //物理世界に追加
```

となりますが、

１．「CANNON.js」のオブジェクトを作る
２．オブジェクトのプロパティに値を設定
３．オブジェクトを物理世界に追加

という流れで処理を実装していきます。

「反復計算回数・許容値・摩擦係数・反発係数」のように物理計算方法・物理特性を設定するための設定項目が「CANNON.js」には数多く出てきます。

そのため、これらの設定項目の言葉の意味を理解していないと、自分が一体何の値を設定しているのかわからなくなってきます。

今回設定する項目は、主に下記のような項目です。


剛体　　　　　・・・　物体自体が変形しないものを「剛体」と言います。
反復計算回数　・・・　衝突している剛体を判定する時の計算の精度を決める値
許容値　　　　・・・　衝突している剛体を判定する時の計算の精度を決める値
摩擦係数　　　・・・　剛体同士が接した時の摩擦力に関する係数
反発係数　　　・・・　剛体同士が接した時の反発する度合いを表した係数


剛体は物体自体が変形しないので、物理計算を非常に早く行うことができます。

一方、物体自体の変形を計算する方法もあり、CAEの世界では「有限要素法」と呼ばれる手法を用います。

ビル・マンションや橋梁など身近な構造物の安全性は、物体の外部から受ける力や加速度から、物体の内部に発生する力の大きさを計算しているのですが、CANNON.jsでは、物体の内部に発生する力は計算しないため計算量が少なくなりますので、リアルタイムに物体の挙動を表現することができるようになっています。

理論は複雑ですが、「有限要素法」にご興味がある方は下記サイトが参考になるかと思います。

◆[Finite Element Method](http://fem.gr.jp/fem/index.html)


## Three.jsの設定
```
scene = new THREE.Scene();    //Three.jsの世界（シーン）を作成
scene.fog = new THREE.Fog(0x000000, 1, 100);  //フォグ（黒色）を作成

//Three Camera--------------------------------------------------------------
camera = new THREE.PerspectiveCamera(90, 800 / 600, 0.1, 10000);    //カメラを作成
camera.position.set(Math.cos(Math.PI / 5) * 30, 5, Math.sin(Math.PI / 5) * 80);  //カメラの位置を設定
changeLookAt();  //視点を変えるための自作関数

scene.add(camera); //シーンにカメラを追加


//Three Light--------------------------------------------------------------
var light = new THREE.DirectionalLight(0xffffff, 0.5);  //照らす方向を指定する光源
light.position.set(10, 10, -10);  //光源の位置を指定
light.castShadow = true;          //影を作る物体かどうかを設定
light.shadowMapWidth = 2024;      //影の精細さ（解像度）を設定
light.shadowMapHeight = 2024;     //影の精細さ（解像度）を設定
light.shadowCameraLeft = -50;     //ライトの視点方向の影の表示度合い
light.shadowCameraRight = 50;     //ライトの視点方向の影の表示度合い
light.shadowCameraTop = 50;       //ライトの視点方向の影の表示度合い
light.shadowCameraBottom = -50;   //ライトの視点方向の影の表示度合い
light.shadowCameraFar = 100;      //影を表示する範囲の設定
light.shadowCameraNear = 0;       //影を表示する範囲の設定
light.shadowDarkness = 0.5;       //影の透明度
scene.add(light);
              
var amb   = new THREE.AmbientLight(0xffffff);  //全体に光を当てる光源
scene.add(amb);   //光源をシーンに追加


//Three Ground--------------------------------------------------------------
var graMeshGeometry = new THREE.PlaneGeometry(300, 300);  //地面の形状を作成
var graMaterial = new THREE.MeshBasicMaterial({           //マテリアルを作成
        map: ground_texture                               //地面にテクスチャ（砂地）を設定
});                                                       //「ground_texture」は別部分で作成
    
viewPlane = new THREE.Mesh(graMeshGeometry, graMaterial); //メッシュを作成
viewPlane.rotation.x = -Math.PI / 2;  //地面を回転
viewPlane.position.y = 1 / 2;         //地面の位置を設定
viewPlane.receiveShadow = true;       //地面に影を表示する
scene.add(viewPlane);                 //シーンに追加
```

こちらも「CANNON.js」と同じく、オブジェクトを作り、そのオブジェクトにプロパティの値を設定していきますが、設定する内容は主に、「描画方法」や「表現方法」になります。

まず、Three.jsの世界（シーン）を作成して、カメラを作成しカメラの位置を設定しています。

```
camera = new THREE.PerspectiveCamera(90, 800 / 600, 0.1, 10000);    //カメラを作成
camera.position.set(Math.cos(Math.PI / 5) * 30, 5, Math.sin(Math.PI / 5) * 80);  //カメラの位置を設定
```

次のコード部分、

```changeLookAt();  //カメラの視点を変えるための自作関数```

は、後にボタンの操作をした時に視点（視線）を変えるための処理をしてくれる関数です。

迷路脱出アプリを作るためには、ウォークスルーのように「その世界を実際に歩いている表現」を行う必要があるため、別関数で視点（視線）を制御しています。

ちなみに、その関数の中は下記のようになっています。

```
function changeLookAt(){
    theta = selfData.angle / 180 * Math.PI;
    var posX = selfData.posX + Math.cos(theta) * 10000;
    var posY = selfData.posY + Math.sin(theta) * 10000;
    camera.lookAt(new THREE.Vector3(posX, 0, posY));     //カメラの視点（視線）を変える
}
```

詳しい内容は後ほどご説明しますので、今は

```camera.lookAt(new THREE.Vector3(posX, 0, posY));     //カメラの視点（視線）を変える```


という部分で自分の見ている方向（カメラの視線・視点）を設定しているということだけ覚えておきましょう。


最後に、作成したカメラをシーンに追加します。

```scene.add(camera); //シーンにカメラを追加```


次に、「Three.js」でレンダラーを作成し3D空間を描画していきます。

```
//Three Render--------------------------------------------------------------
renderer = new THREE.WebGLRenderer({antialias: true});  //レンダラーを作成
renderer.setSize(wSizeWidth, wSizeHeight);              //レンダラーのサイズを設定

renderer.setClearColor(0xffffff, 1);                    //レンダラーの描画内容をクリア
renderer.shadowMapEnabled = true;                       //レンダラーの影の描画を有効化
document.body.appendChild(renderer.domElement);         //DOM要素をBodyに追加

//Start Rendering--------------------------------------------------------------
renderer.render(scene, camera);  //レンダラーで3D空間を描画
```

上記を追加すると、ブラウザ上に「Three.js」の描画内容が表示されるようになります。

描画を行うためには、レンダラーを作成し、レンダラーのプロパティに「レンダリングに関する設定」を行って、DOM要素をブラウザに追加します。

そして、レンダラーの「renderメソッド」を実行すると、「Three.js」の描画が開始されます。

次回以降は、迷路マップデータの作成・迷路マップの描画・ウォークスルー機能の実装について書いていきたいと思います。

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

# Three.jsとCANNON.jsについて参考にさせていただいた記事
[Three.js の基礎](http://qiita.com/o_tyazuke/items/d049c7d550086a85259b)
[Three.jsでWebGL(1)](http://qiita.com/odorumaharaja/items/d92f575b855a3fca0f42)
[three.jsで3Dを作りたい！](http://qiita.com/kurokuma/items/0def9a7092e9ce011653)
[WebGLと3D物理演算ライブラリの組み合わせを試してみる](http://qiita.com/cx20/items/3ebed669fb9c9e797935)
[Three.jsでCannon.jsを使う](http://qiita.com/o_tyazuke/items/3481ef1a31b2a4888f5d)
[CANNON.jsでContactMaterialを使う](http://qiita.com/edo_m18/items/9b5c86ca2a781b066fab)

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