【Three.js】マウスとキーボードでカメラを制御するPointerLockControls【ソースコード付き】

スポンサーリンク
Three.js

マウスとキーボードでカメラを制御したいけど、どうしたらいいの?

こういったお悩みを解決いたします。

開発するコンテンツに合わせて、カメラの操作方法はその都度変えていかなくてはなりません。

ぜひ、今回紹介する制御方法を試してもらえたらと思います。

当記事は、一人称視点でマウスとキーボードを用いて制御する方法です。

マウスは視点の回転、キーボードは移動を担っています。

Three.jsのPointerLockControlsを少し改良したものを使用しています。

後半には、ソースコードとこのページで試せるデモがあります。

当記事を読むと、

  • PointerLockControlsの使い方が分かる
  • 一人称視点でマウス&キーボードでカメラを制御することができるようになる
スポンサーリンク

PointerLockControlsの導入と基本的な使い方

PointerLockControls.jsは公式サイトからダウンロードできるファイル一式に含まれています。

パスは以下のようになっています。

three.js-master/examples/js/controls/PointerLockControls.js

読み込み方法は普段と同じです。

<script src="./three.js-master/examples/js/controls/PointerLockControls.js"></script>

操作方法について、以下の流れになります。

  1. クリックしてマウスポインタをロックする
  2. マウスポインタを動かすと、それに合わせて視点が動く

後ほどデモでお試しいただけますので、操作感はそちらで味わってみてください。

上記を見て分かる通り、デフォルトのPointerLockControlsでは移動機能は備わっておりません

以下では、キーボードによる移動機能を付け加えたサンプルをソースコード付きで紹介しております。

スポンサーリンク

PointerLockControls + 移動機能でカメラを制御してみる

ソースコードは以下になります。

各行で何をしているかはコメントに記してありますので参考にしてみてください。

<!DOCTYPE html>
<html>
  <head>
    <title>three.js Sample</title>
    <meta charset="utf-8">
    <script src="./three.js-master/build/three.min.js"></script><!--three.jsライブラリの読み込み-->
    <script src="./three.js-master/examples/js/controls/PointerLockControls.js"></script><!--PointerLockControls.jsの読み込み-->
  </head>
  <body style="overflow: hidden;"><!--スクロールバーが出る方はoverflowをhiddenに設定すると消えます-->
    <script>
      const scene = new THREE.Scene();  //シーンを作成
      const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 5000);  //カメラを作成
      const renderer = new THREE.WebGLRenderer(); //レンダラーを作成
      const controls = new THREE.PointerLockControls(camera, renderer.domElement);  //カメラにPointerLockControls機能を付与
      const boxGeometry = new THREE.BoxGeometry(100, 100, 100); //boxの形状データ
      const boxMaterial = new THREE.MeshNormalMaterial(); //boxの材質(マテリアル)情報
      const box1 = new THREE.Mesh(boxGeometry, boxMaterial); //形状と材質から実際の3Dオブジェクトを生成
      const box2 = new THREE.Mesh(boxGeometry, boxMaterial);
      const box3 = new THREE.Mesh(boxGeometry, boxMaterial);
      let moveForward = false;
			let moveBackward = false;
			let moveLeft = false;
      let moveRight = false;
      let prevTime = performance.now(); //1フレーム前の時刻を記憶
      let velocity = new THREE.Vector3();
			let direction = new THREE.Vector3();
      init();
      animate();

      function init(){    //初期化用関数
        renderer.setSize(window.innerWidth, window.innerHeight);
        renderer.setPixelRatio(window.devicePixelRatio);
        renderer.shadowMap.enabled = true;
        renderer.shadowMap.type = THREE.BasicShadowMap;
        document.body.appendChild(renderer.domElement); //canvas要素をbodyタグに追加
        camera.position.set(0, 0, 500);
        box1.position.set(0, 0, 0);
        box2.position.set(200, 0, 500);
        box3.position.set(-200, 0, 500);
        scene.add(box1); scene.add(box2); scene.add(box3);
        scene.add(controls.getObject());

        renderer.domElement.addEventListener('click', function() {
          controls.lock(); 
        });
      }

      function animate(){
        move();
        renderer.render(scene, camera);
        requestAnimationFrame(animate); //毎フレーム呼び出すことで描画
      }

      function move(){  //カメラの移動を制御する関数.毎フレーム呼ばれる
        let time = performance.now();
        if ( controls.isLocked === true ) { //マウスのポインタがロックされているときのみ有効
          let delta = ( time - prevTime ) / 1000;

          //速度を減衰させる
          velocity.x -= velocity.x * 10.0 * delta;
          velocity.z -= velocity.z * 10.0 * delta;

          //進行方向のベクトルを設定
          direction.z = Number( moveForward ) - Number( moveBackward );
          direction.x = Number( moveRight ) - Number( moveLeft );
          direction.normalize();

          if ( moveForward || moveBackward ) velocity.z -= direction.z * 1000.0 * delta;
          if ( moveLeft || moveRight ) velocity.x -= direction.x * 1000.0 * delta;

          controls.moveRight( - velocity.x * delta );
          controls.moveForward( - velocity.z * delta );

        }
        prevTime = time;
      }

      let onKeyDown = function ( event ) {  //キーボード押下時の処理
        switch ( event.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;
        }
      };

      let onKeyUp = function ( event ) {  //キーボードから離れたとき
        switch ( event.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;
        }
      };

      document.body.addEventListener( 'keydown', onKeyDown, false );  //キーボードに関するイベントリスナ登録
      document.body.addEventListener( 'keyup', onKeyUp, false );
    </script>
  </body>
</html>

デモを見たい方はこちらをクリック(ページは遷移しません) ⇒

※デモはPCのみとなります。画面をクリックすることでPointerLockControlsの機能が有効になります。中止したい場合は、ESCキーを押下してください。

scriptタグの4行目にあたりますが、PointerLockControlsのインスタンスを生成する際にカメラを渡してあげることで、カメラにPointerLockControlsの機能が付与されるというわけです。

注意ですが、インスタンス生成時に、第2引数としてHTML要素(↑のコードではrenderer.domElement)を指定するのを忘れないようにしてください。おそらくエラーが出るはずです。

PointerLockControls.jsの中でいくつかのイベントリスナをその引数で与えたHTML要素に登録しているためです。

renderer.domElement は、シーンがレンダリングされるcanvas要素を表していますので、これを渡しておけば問題ないです。

移動機能については、以下の表のように対応しております。

キー挙動
W前に移動
S後に移動
A左に移動
D右に移動

もしキーを変更したければ、onKeyDown, onKeyUpの中のキーを変更します。

また、速度の減衰率や移動の速さを変更したいときは、move関数内の定数を変更します。

まとめ

いかがでしたでしょうか。

カメラの制御の方法には、PointerLockControlsだけでなく様々な種類があります。

・OrbitControls

・FirstPersonControls

いろいろ試して、開発コンテンツに合うような制御方法を見つけましょう。

以上です。

Three.jsの学習におすすめの書籍はコレ!

・はじめてのThree.js

この本には、Three.jsを用いてコンテンツを開発する際に、必要な情報がほぼすべて盛り込まれています。

Three.jsをここまで詳しく解説している本はおそらくこれしかないです。

私は大学4年生からの研究活動でThree.jsを用いていますが、初めての学習でこの本を使用しました。

自分が必要な情報をかいつまんでその都度学習することもできますが、私個人としては、一通り目を通してThree.jsでできることをざっと把握することをおすすめします。

なぜなら、できることの全貌を知っている方が、アイデアの幅が広がり設計・コーディングもしやすくなるからです。

ぜひ一度お調べになってみてはいかがでしょうか。

コメント

タイトルとURLをコピーしました