3DCG素人がthree.jsを使ってFPSゲームを作ってみた Part1

# three.jsってなんだよ？
HTML5のcanvasを使って、3Dコンテンツを描写するJavaScriptのライブラリ。
JavaScriptが使えれば、素人でも作れるということなので、簡単なFPSゲームを作ってみようと思う。
[three.js公式](https://threejs.org/)
[Three.js入門サイト](https://ics.media/tutorial-three/index.html)
# とりあえず下準備
必要なライブラリの読み込み、3D空間の設定、カメラ設定、光源設定を行う。
### three.jsライブラリ読み込み
CDNを利用
https://cdnjs.cloudflare.com/ajax/libs/three.js/99/three.min.js
### 各種設定
```javascript
// 3D空間の設定
const scene = new THREE.Scene();
// 背景色設定
scene.background = new THREE.Color( 0x000000 );
// 光源設定
var light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(0.5, 1, 0.75)
scene.add(light);
// 座標軸を表示
const axes = new THREE.AxisHelper(400);
scene.add(axes)
// カメラ設定
const camera = new THREE.PerspectiveCamera(60, width / height);
camera.position.set(300, 200, 400);
camera.lookAt(new THREE.Vector3(0, 0, 0))
```
まだこの段階では何も描写されていない。
![01.png](https://qiita-image-store.s3.amazonaws.com/0/323720/5092bb79-a921-4cb1-cb12-e3a2179da2a7.png)
# フィールド・オブジェクトの配置
土台となるフィールド、平面では寂しいので簡単なオブジェクトを配置する。
### オブジェクト配置
```javascript
// フィールド作成
const geometry = new THREE.BoxGeometry(512, 8, 512, 10, 10, 1);
const material = new THREE.MeshPhongMaterial({color: 0xe29676});
const field = new THREE.Mesh(geometry, material);
field.position.y = -5;
scene.add(field);
// オブジェクト作成
scene.add(createBox(16, 16, 16, {x: -128, z: -128, y: 8}, 0x6c9bd2));
scene.add(createBox(16, 16, 16, {x: 128, z: -128, y: 8}, 0x9cbb1c));
scene.add(createBox(16, 16, 16, {x: -128, z: 128, y: 8}, 0x9cbb1c));
scene.add(createBox(16, 16, 16, {x: 128, z: 128, y: 8}, 0x6c9bd2));

// ボックスオブジェクトの生成関数
function createBox(x, y, z, position, color) {
  const geometry = new THREE.BoxGeometry(x, y, z);
  const material = new THREE.MeshPhongMaterial({color: color});
  const box = new THREE.Mesh(geometry, material);
  box.position.x = position.x;
  box.position.z = position.z;
  box.position.y = position.y;
  return box;
}
```
おぉ～感無量
![02.png](https://qiita-image-store.s3.amazonaws.com/0/323720/87879875-9ec7-9f6c-b137-eec1f1b6c759.png)
#この上を歩きたい！
移動を実現するには、キーボード・マウスの入力を受け取り、いろいろな計算を行ってカメラを移動させる。
一から実装するのは無理なので、PointerLockControlライブラリを使う。
### PointerLockControlライブラリ読み込み
CDNを利用
https://threejs.org/examples/js/controls/PointerLockControls.js
### 設定
```javascript
/**
キーボード入力イベントの設定
移動距離算出
カメラの設定
毎フレーム毎に移動距離からカメラを移動させる処理を追加
*/

// 移動距離算出 即時関数
let move = (function() {

  const SPEED = 60.0;

  let moveForward  = false;
  let moveBackward = false;
  let moveLeft     = false;
  let moveRight    = false;
  let prevTime     = performance.now();

  // キーダウンイベント設定
  document.addEventListener( 'keydown', function(e) {
    switch ( e.keyCode ) {
      case 87: // w
        moveForward = true;
        break;
      case 65: // a
        moveLeft = true;
        break;
      case 83: // s
        moveBackward = true;
        break;
      case 68: // d
        moveRight = true;
        break;
    }
  }, false );

  // キーアップイベント設定
  document.addEventListener( 'keyup', function(e) {
    switch ( e.keyCode ) {
      case 87: // w
        moveForward = false;
        break;
      case 65: // a
        moveLeft = false;
        break;
      case 83: // s
        moveBackward = false;
        break;
      case 68: // d
        moveRight = false;
        break;
    }
  }, false );

  return {
    getVelocity() {
      let time = performance.now();
      let delta = ( time - prevTime ) / 1000;
      let directionX = Number( moveLeft ) - Number( moveRight );
      let directionZ = Number( moveForward ) - Number( moveBackward );
      let velocity = new THREE.Vector3();
      velocity.x -= directionX * SPEED * delta;
      velocity.z -= directionZ * SPEED * delta;
      prevTime = time;
      return velocity;
    }
  }

})();

// カメラ設定
const camera = new THREE.PerspectiveCamera(60, width / height);
const controls = new THREE.PointerLockControls(camera);
controls.getObject().position.y = 10
// canvasクリックでポインタロック
scene.add(controls.getObject());
document.getElementById('myCanvas').addEventListener('click', function() {
  controls.lock(); 
});

// 毎フレーム時に実行されるループイベントです
function tick() {
  let v = move.getVelocity();
  controls.getObject().translateX(v.x)
  controls.getObject().translateZ(v.z)
  renderer.render(scene, camera); // レンダリング

  requestAnimationFrame(tick);
}
```
### 動かしてみる
キーバインド
前方移動: w
後方移動: s
左移動: a
右移動: d
視野方向の変更: マウスでcanvas内をクリックすると、ポインタがロックされ、マウス移動に応じて視野方向が変更
※ESCキーで元に戻ります。
<p data-height="533" data-theme-id="0" data-slug-hash="gQNdJm" data-default-tab="result" data-user="gen6414" data-pen-title="three.js" class="codepen">See the Pen <a href="https://codepen.io/gen6414/pen/gQNdJm/">three.js</a> by Gen Abe (<a href="https://codepen.io/gen6414">@gen6414</a>) on <a href="https://codepen.io">CodePen</a>.</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>
# 次回予告
とりあえず移動ができるようになったので、オブジェクトへの当たり判定をつけたいな

おわり