第8話 次郎！ダンジョン見つけたyo！

**知らない人の家に勝手に入ってはいかん！**

今日も、太郎と次郎は街を徘徊していたんですが、そのとき、太郎がダンジョンを発見した様子をコード化しました。

![canvas.png](https://qiita-image-store.s3.amazonaws.com/0/105644/d936f57b-b40a-891a-68fe-d20b86ec79d9.png)

今回は、Three.jsを使って発見したダンジョンを表示したいと思います。

## 構成

マインクラフトみたいにCubeで構成してみます。Cubeは壁、扉、天井、床の4種類を用意します。そのCubeを3層（天井、自分のいる階層、床）で並べます。

```js:hoge.js
var mapData = {
  ceiling : [
    "■■■■■■■■■■",
    "■■■■■■■■■■",
    "■■■■■■■■■■",
    "■■■■■■■■■■",
    "■■■■■■■■■■",
    "■■■■■■■■■■",
    "■■■■■■■■■■",
    "■■■■■■■■■■"
  ],
  wall : [
    "■■■■■■■■■■",
    "■   ■    ■",
    "■   ■□■■ ■",
    "■■□■■  ■ ■",
    "■■ ■■  ■ ■",
    "■■ ■■■□■ ■",
    "■      □ ■",
    "■■■■■■■■■■"
  ],
  floor : [
    "■■■■■■■■■■",
    "■■■■■■■■■■",
    "■■■■■■■■■■",
    "■■■■■■■■■■",
    "■■■■■■■■■■",
    "■■■■■■■■■■",
    "■ ■■■■■■■■",
    "■■■■■■■■■■"
  ]
};
```
■ と □ の部分にCubeを配置します。それぞれの層に合わせて ■ の部分にそれぞれの層に合わせたCubeを配置します。

## Cubeの作成準備

形状(Geometry)を決めて、その表面素材(Material)を用意し、これを使って物体(Mesh)を作成するという手順になります。

```js:hoge.js
var blockSize = 10;
var geometry = new THREE.CubeGeometry(blockSize, blockSize, blockSize);
```
これで、1辺の長さが10の立方体の形状が出来上がります。今回はすべてCubeなので、形状は1つだけ作成しておきます。

今度は、それぞれのCubeが壁、扉、天井、床に見えるように素材を用意します。これについて、今回はpng画像を使います。
まず、png画像を読み込みます。そのあと、素材の作成になります。

```js:hoge.js
var textureFile = {
  wall    : "img/wall.png",
  door    : "img/door.png",
  ceiling : "img/ceiling.png",
  floor   : "img/floor.png",
};
var loader = new THREE.TextureLoader();
var texture = {
  wall   :loader.load(textureFile.wall),
  door   :loader.load(textureFile.door),
  ceiling:loader.load(textureFile.ceiling),
  floor  :loader.load(textureFile.floor)
};
var material = {
  wall   : new THREE.MeshPhongMaterial({map:texture.wall, bumpMap:texture.wall, bumpScale: 0.05}),
  door   : new THREE.MeshPhongMaterial({map:texture.door, bumpMap:texture.door, bumpScale: 0.08}),
  ceiling: new THREE.MeshPhongMaterial({map:texture.ceiling, bumpMap:texture.ceiling, bumpScale: 0.09}),
  floor  : new THREE.MeshPhongMaterial({map:texture.floor, bumpMap:texture.floor, bumpScale: 0.09})
};
```
今回の画像が岩肌っぽいので、バンプマッピングで凸凹感を出します。

さて、形状の定義と素材の準備ができました。構成で示したようにCubeを並べていきます。

## シーン
作成したCubeをどこに並べるかというと、空間(Scene)に並べます。なので、空間を作っておく必要があります。

```js:hoge.js
var scene = new THREE.Scene();
```
## Cubeの配置

配置データを参照しつつ、空間に配置します。
壁の配置はこんな感じになります。

```js:hoge.js
for (y = 0; y < mapData.wall.length; y++) {
  for (x = 0; x < mapData.wall[y].length; x++) {
    if (mapData.wall[y].charAt(x) == "■") {
      var cube = new THREE.Mesh(geometry, material.wall);
      cube.position.set(blockSize * x, 0, blockSize * y);
      scene.add(cube);
    }
  }
}
```
あとは、こんな感じでしょうか...

```js:hoge.js
// 扉の配置
if (mapData.wall[y].charAt(x) == "□") {
  var cube = new THREE.Mesh(geometry, material.door);
  cube.position.set(blockSize * x, 0, blockSize * y);
  scene.add(cube);
}
// 天井の配置
if (mapData.ceiling[y].charAt(x) == "■") {
  var cube = new THREE.Mesh(geometry, material.ceiling);
  cube.position.set(blockSize * x, blockSize, blockSize * y);
  scene.add(cube);
}
// 床の配置
if (mapData.floor[y].charAt(x) == "■") {
  var cube = new THREE.Mesh(geometry, material.floor);
  cube.position.set(blockSize * x, -blockSize, blockSize * y);
  scene.add(cube);
}
```

## 太郎と次郎
配置データの上を北向きとして、下図の位置に二人がいるとします。
![map.png](https://qiita-image-store.s3.amazonaws.com/0/105644/30d12f07-280f-8235-ea87-c8a3c3a84ec0.png)

```js:hoge.js
// 方角
var direction = {
  north : 0,
  west  : 1,
  south : 2,
  east  : 3
};
// プレイヤー情報
var Player = {
  x : 6,
  y : 1,
  direction : direction.west,
};
```
場所とどっち向いてるかを記憶しておきます。
さて、Cubeを空間に配置して、二人の位置が決まったので、実際にどう見えるのか？が問題です。
これが、カメラです。カメラから見たCubeの配置が投影されるわけで、このカメラは二人の目です。

```js:hoge.js
// カメラ用パラメータ
var cameraParams = {
  fov : 95,
  aspect : 1.8,
  near : 1,
  far : 50
};
var camera = new THREE.PerspectiveCamera(
  cameraParams.fov, cameraParams.aspect, cameraParams.near, cameraParams.far
);
camera.position.set( Player.x * blockSize, 0, Player.y * blockSize );
camera.rotation.y = rotationAngle[Player.direction];
```
3Dっぽく見えるように透視投影のカメラを作ります。カメラには、画角(fov)とアスペクト比(aspect)、見える範囲(near～far)を指定します。場所と向きをさっき記憶した情報から取得して、カメラの位置と向きを設定します。

## 描画

表示サイズを決めて、rendererを作成し、描画します。

```js:hoge.js
// 表示サイズ
var mazeScreen = {
  width : 480,
  height : 270
};
var canvas = document.getElementById("game");
var renderer = new THREE.WebGLRenderer();
renderer.setSize( mazeScreen.width, mazeScreen.height );
canvas.appendChild( renderer.domElement );
var render = function(){
  renderer.render(scene, camera);
};
var animate = function(){
  render();
  requestAnimationFrame( animate );
};
animate();
```

いや、真っ暗だし...

## 光源
はい、人もそうですけど、光ないと何も見えませんｗ
空間に光を追加します。太陽の光は入ってこない設定なので、懐中電灯とかが頼りです。今回はスポットライト的な懐中電灯ではなく、ろうそく的な感じで光を追加します。

```js:hoge.js
var lightParams = {
  point   : 0xffffff
};
var light = new THREE.PointLight( lightParams.point, 3, 10, 1 );
light.position.copy( camera.position );
light.position.z -= 1;
scene.add( light );
```
ろうそく（点光源）を空間に追加します。位置はカメラのちょっと前ぐらいで。光の色、明るさ、光の届く範囲、減衰率を指定します。光の届く範囲を0にすると、無限に光が届きますｗ

これで見えますよね。ぅん。
しかし、ブラウザのレンダリング能力ってすごかったんですね...

いよいよ、次回最終回は、このダンジョンを徘徊してみますｗ

[こんな感じ(デモ)](http://tkymgr.o0o0.jp/)で...