【Three.js】スマホ用に移動用タッチコントローラーを実装する(ソースコード付き)

スポンサーリンク
Three.js

タッチで操作するコントローラーを作りたいけど、

どうしたらいいの?

こういった疑問を解決いたします。

Three.jsが提供しているカメラの制御機能 (移動や回転) は基本的にPC用になっています。

そのため、スマホにも対応できるレスポンシブなデザインを目指しているのであれば、マウスやキーボードに代わる何らかのインターフェースが必要です。

当記事では、以下のような簡単なコントローラーの画像をUIとして設置し、「ユーザーがこれをタップしている間、移動を行う」方法をソースコードつきで解説いたします。(回転は少しややこしいので別記事でご紹介します)

コントローラーの画像は以下をダウンロードしていただいてかまいません。

制御はPointerLockControlsを用いています。

通常はマウス・キーボードで操作しますが、その代わりとしてコントローラーを設置するというわけです。

PointerLockControlsについて詳しく知りたい方は以下の記事を参考にしてください。

当記事を読むと、

  • タッチコントローラーをすぐ、簡単に実装できる
スポンサーリンク

処理の流れ

ユーザーのアクションに対する処理の流れを簡単に説明します。

  1. ユーザーがコントローラーをタッチする
  2. イベントリスナのコールバック関数で、タップされたターゲットのIDを取得
  3. IDに応じて処理を分岐(移動方向を決める)

この流れを理解してソースコードを見た方が分かりやすいかと思います。

スポンサーリンク

実際にコントローラーで制御してみる。ソースコードで解説!

ソースコードはこちらです。デモもありますので、お試しいただければと思います。

<!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に設定すると消えます-->
    <img id="controller-forward" src="controller_forward.png" style="position: absolute; left: 0%;" />
    <img id="controller-back" src="controller_back.png" style="position: absolute; left: 20%;"/>
    <img id="controller-left" src="controller_left.png" style="position: absolute; left: 40%"/>
    <img id="controller-right" src="controller_right.png" style="position: absolute; left: 60%;"/>
    <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);  //カメラにOrbitControls機能を付与
      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();
        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 onTouchStart = function ( event ) {  //キーボード押下時の処理
        switch ( event.target.id ) {
          case "controller-forward": 
            moveForward = true;
            break;
          case "controller-left":
            moveLeft = true;
            break;
          case "controller-back":
            moveBackward = true;
            break;
          case "controller-right":
            moveRight = true;
            break;
        }
      };

      let onTouchEnd = function ( event ) {  //キーボードから離れたとき
        switch ( event.target.id ) {
          case "controller-forward":
            moveForward = false;
            break;
          case "controller-left":
            moveLeft = false;
            break;
          case "controller-back":
            moveBackward = false;
            break;
          case "controller-right":
            moveRight = false;
            break;
        }
      };

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

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

↑のデモをPCで試したい方は、通常のモードではタップできないので、デベロッパツールでスマホ・タブレットモードにしてください。

以下の記事でその方法について解説しております。

コントローラーの位置はかなり雑ですが、コントローラーをタップ(クリック)すると移動するのが分かるかと思います。

ソースコードは前節の流れに沿って考えるとそんなに難しくないと思います。

まとめ

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

今回はタッチコントローラーでカメラを移動させる方法をご紹介しました。

別記事にて、回転機能もご紹介しようと思います。

また、今回のケースですとユーザーがPCの場合はコントローラーを表示する必要はないので、ユーザーのデバイスの種類に応じてUIを変える処理もご紹介しようと思います。

以上です。

コメント

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