[Three.js] ゲームっぽい1人称視点やってみた。

色々と調べていたら、
FirstPersonControls を使うと、ゲームっぽい1人称視点で操作ができると言う噂を聞きつけてやってみました。

https://gorogoronyan.web.fc2.com/htmlsample/src/TestJS_threejs_FirstPersonControls1.html

# OrbitControls
まず、
FirstPersonControlsを使わずに、
OrbitControlsで、カメラを動かす場合、

### コード
```html:html
<html>
  <head>
    <meta charset="utf-8" />
    <script type="importmap">
      {
        "imports": {
          "three": "https://cdn.jsdelivr.net/npm/three@0.164.1/build/three.module.js",
          "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.164.1/examples/jsm/"
        }
      }
    </script>
    <script type="module">
      import * as THREE from "three";
      import { OrbitControls } from "three/addons/controls/OrbitControls.js";
      import { FirstPersonControls } from "three/addons/controls/FirstPersonControls.js";

      var clock = new THREE.Clock();

      // サイズを指定
      const width = 960;
      const height = 540;

      // レンダラーを作成
      const canvasElement = document.querySelector("#myCanvas");
      const renderer = new THREE.WebGLRenderer({
        canvas: canvasElement,
      });
      renderer.setSize(width, height);

      // シーンを作成
      const scene = new THREE.Scene();

      // カメラを作成
      const camera = new THREE.PerspectiveCamera(45, width / height);
      // カメラの初期座標を設定
      camera.position.set(0, 0, 1000);

      // カメラコントローラーを作成
      const controls = new OrbitControls(camera, canvasElement);

      /*
      const controls = new FirstPersonControls(camera, renderer.domElement); //
      controls.movementSpeed = 100;  //移動速度
      //controls.lookVertical = true; //default true
    
      // true でマウスによる方向転換の操作を許可する
      //controls.activeLook = false;  //default true
      // マウスによる方向転換の速度
      controls.lookSpeed = 0.02;
      */

      // 形状とマテリアルからメッシュを作成します
      const geometry = new THREE.BoxGeometry(100, 300, 50);
      const material = new THREE.MeshNormalMaterial();
      const mesh = new THREE.Mesh(geometry, material);
      scene.add(mesh);

      const geometry2 = new THREE.BoxGeometry(200, 50, 100);
      const box2 = new THREE.Mesh(geometry2, material);
      //場所を変える
      box2.position.x = 400;
      box2.position.z = 400;
      scene.add(box2);

      const geometry3 = new THREE.BoxGeometry(50, 100, 200);
      const box3 = new THREE.Mesh(geometry3, material);      
      box3.position.z = -1000;
      scene.add(box3);

      const geometry4 = new THREE.BoxGeometry(100, 200, 50);
      const box4 = new THREE.Mesh(geometry4, material);      
      box4.position.z = 400;
      scene.add(box4);

      const box5 = new THREE.Mesh(geometry3, material);      
      box5.position.x = -400;
      scene.add(box5);
      
      const box6 = new THREE.Mesh(geometry, material);      
      box6.position.x = -400;      
      box6.position.z = 400;
      scene.add(box6);

      const box7 = new THREE.Mesh(geometry, material);      
      box7.position.x = 400;      
      box7.position.z = -1000;
      scene.add(box7);

      const box8 = new THREE.Mesh(geometry, material);      
      box8.position.x = 400;      
      scene.add(box8);

      const box9 = new THREE.Mesh(geometry, material);      
      box9.position.x = -400;      
      box9.position.z = -500;
      scene.add(box9);

      const box10 = new THREE.Mesh(geometry2, material);      
      //box9.position.x = -400;      
      box10.position.z = -500;
      scene.add(box10);

      const box11 = new THREE.Mesh(geometry3, material);      
      box11.position.x = 550;      
      box11.position.z = -500;
      scene.add(box11);

      //軸を追加する。
      const axesHelper = new THREE.AxesHelper( 1000 );
      scene.add( axesHelper );

      //グリッドを追加する。
      const gridHelper = new THREE.GridHelper(2000, 50,0xffff00) 
      scene.add(gridHelper);

      tick();

      // 毎フレーム時に実行されるループイベントです
      function tick() {
        // レンダリング
        renderer.render(scene, camera);
        requestAnimationFrame(tick);
        //controls.update(clock.getDelta());
      }
    </script>
  </head>
  <body>
    <canvas id="myCanvas"></canvas>
  </body>
</html>
```
### 画面表示
こんな感じで、箱を配置。

![1.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1284134/f286cb0e-9edf-435c-82e5-3cf826491c7c.png)

### CodePen
<p class="codepen" data-height="700" data-default-tab="html,result" data-slug-hash="bNGqdyM" data-pen-title="three.js 入門 7_1" data-user="vhmbdiog-the-flexboxer" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/vhmbdiog-the-flexboxer/pen/bNGqdyM">
  three.js 入門 7_1</a> by sasuke (<a href="https://codepen.io/vhmbdiog-the-flexboxer">@vhmbdiog-the-flexboxer</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://public.codepenassets.com/embed/index.js"></script>

俯瞰視点で見ると、こんな感じの状況です。

# FirstPersonControls
次に、同じ空間を、
FirstPersonControlsを使って1人称視点で、視点移動する。

### コード
上記コードの、OrbitControls の部分を、FirstPersonControlsに変更する。
```js:js
      // カメラコントローラーを作成
      //const controls = new OrbitControls(camera, canvasElement);

      const controls = new FirstPersonControls(camera, renderer.domElement); //
      controls.movementSpeed = 100;  //移動速度
      //controls.lookVertical = true; //default true
    
      // true でマウスによる方向転換の操作を許可する
      //controls.activeLook = false;  //default true
      // マウスによる方向転換の速度
      controls.lookSpeed = 0.02;
```

レンダリング部分のコードを追加する。
```js:js
      // 毎フレーム時に実行されるループイベントです
      function tick() {
        // レンダリング
        renderer.render(scene, camera);
        requestAnimationFrame(tick);
        controls.update(clock.getDelta());  //この行を追加。FirstPersonControls.js　で使う。
      }
```

あと、
```js:js
var clock = new THREE.Clock();
```
を何処か上のほうで宣言しておく。


#### コード全体
```html:html
<html>
  <head>
    <meta charset="utf-8" />
    <script type="importmap">
      {
        "imports": {
          "three": "https://cdn.jsdelivr.net/npm/three@0.164.1/build/three.module.js",
          "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.164.1/examples/jsm/"
        }
      }
    </script>
    <script type="module">
      import * as THREE from "three";
      import { OrbitControls } from "three/addons/controls/OrbitControls.js";
      import { FirstPersonControls } from "three/addons/controls/FirstPersonControls.js";

      var clock = new THREE.Clock();

      // サイズを指定
      const width = 960;
      const height = 540;

      // レンダラーを作成
      const canvasElement = document.querySelector("#myCanvas");
      const renderer = new THREE.WebGLRenderer({
        canvas: canvasElement,
      });
      renderer.setSize(width, height);

      // シーンを作成
      const scene = new THREE.Scene();

      // カメラを作成
      const camera = new THREE.PerspectiveCamera(45, width / height);
      // カメラの初期座標を設定
      camera.position.set(0, 0, 1000);

      // カメラコントローラーを作成
      //const controls = new OrbitControls(camera, canvasElement);

      
      const controls = new FirstPersonControls(camera, renderer.domElement); //
      controls.movementSpeed = 200;  //移動速度
      //controls.lookVertical = true; //default true
    
      // true でマウスによる方向転換の操作を許可する
      //controls.activeLook = false;  //default true
      // マウスによる方向転換の速度
      controls.lookSpeed = 0.08;
      

      // 形状とマテリアルからメッシュを作成します
      const geometry = new THREE.BoxGeometry(100, 300, 50);
      const material = new THREE.MeshNormalMaterial();
      const mesh = new THREE.Mesh(geometry, material);
      scene.add(mesh);

      const geometry2 = new THREE.BoxGeometry(200, 50, 100);
      const box2 = new THREE.Mesh(geometry2, material);
      //場所を変える
      box2.position.x = 400;
      box2.position.z = 400;
      scene.add(box2);

      const geometry3 = new THREE.BoxGeometry(50, 100, 200);
      const box3 = new THREE.Mesh(geometry3, material);      
      box3.position.z = -1000;
      scene.add(box3);

      const geometry4 = new THREE.BoxGeometry(100, 200, 50);
      const box4 = new THREE.Mesh(geometry4, material);      
      box4.position.z = 400;
      scene.add(box4);

      const box5 = new THREE.Mesh(geometry3, material);      
      box5.position.x = -400;
      scene.add(box5);
      
      const box6 = new THREE.Mesh(geometry, material);      
      box6.position.x = -400;      
      box6.position.z = 400;
      scene.add(box6);

      const box7 = new THREE.Mesh(geometry, material);      
      box7.position.x = 400;      
      box7.position.z = -1000;
      scene.add(box7);

      const box8 = new THREE.Mesh(geometry, material);      
      box8.position.x = 400;      
      scene.add(box8);

      const box9 = new THREE.Mesh(geometry, material);      
      box9.position.x = -400;      
      box9.position.z = -500;
      scene.add(box9);

      const box10 = new THREE.Mesh(geometry2, material);      
      //box9.position.x = -400;      
      box10.position.z = -500;
      scene.add(box10);

      const box11 = new THREE.Mesh(geometry3, material);      
      box11.position.x = 550;      
      box11.position.z = -500;
      scene.add(box11);

      //軸を追加する。
      const axesHelper = new THREE.AxesHelper( 1000 );
      scene.add( axesHelper );

      //グリッドを追加する。
      const gridHelper = new THREE.GridHelper(2000, 50,0xffff00) 
      scene.add(gridHelper);

      tick();

      // 毎フレーム時に実行されるループイベントです
      function tick() {
        // レンダリング
        renderer.render(scene, camera);
        requestAnimationFrame(tick);
        controls.update(clock.getDelta());//この行を追加。FirstPersonControls.js　で使う。
      }
    </script>
  </head>
  <body>
    <canvas id="myCanvas"></canvas>
  </body>
</html>

```

### 画面表示

初期位置

![2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1284134/1cf092f5-fb34-4e2e-a5ac-ecf02008f3e1.png)

正面の壁を、左側に回避して抜けてみる。

![3.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1284134/cfdfc53b-d49f-4bd6-be8e-0f7e323eb426.png)

![4.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1284134/3c665a60-5d3b-419b-8fd3-ca70ddfe9771.png)

![5.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1284134/d1912b56-de9b-4d7a-9778-d968213e8667.png)

![6.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1284134/c0b46178-2af3-4e91-b1dd-54d2cdb810c7.png)

シューティングゲームか、何かできそう。

<b>操作方法）
左マウスクリックで、直進。
カメラの向き（進行方向）はマウスポインタで操作する。</b>

### CodePen
<p class="codepen" data-height="700" data-default-tab="html,result" data-slug-hash="RNwpPXg" data-pen-title="three.js 入門 7_2" data-user="vhmbdiog-the-flexboxer" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/vhmbdiog-the-flexboxer/pen/RNwpPXg">
  three.js 入門 7_2</a> by sasuke (<a href="https://codepen.io/vhmbdiog-the-flexboxer">@vhmbdiog-the-flexboxer</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://public.codepenassets.com/embed/index.js"></script>