0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ゲームコントローラーでCesiumを操作

Posted at

ホリパッド TURBO for Nintendo Switch™ / PC を買いました。PCにも使えるということで、PCにつないでみました。マウスの代わりには使えないんですね。Xboxのコントローラーとして認識されました。
ブラウザでもJavascriptを書けば使えるということなので3D地図のプラットフォームのCesiumで試しました。結果は、試したCesiumで使えました。ボタンにどの機能を割り当てるかはちゃんと考えないといけないというのが実感できました。

環境

ゲームパッド API の使用によると Gamepad API はブラウザによって対応が若干ことなるようなので、他のブラウザでは動かないかもしれません。Edgeでは動きましたが、そのうち動かなくなるかもしれません。

コードはエラーチェックとかいろいろ手を抜いていることをご了承ください。

参考にしたページ

Cesium JS

Gamepad API

ホリパッド TURBO

ホリパッド TURBO for Nintendo Switch™ / PC 取扱説明書

Gamepad API と ホリパッド TURBO の対応

ホリパッド TURBO for Nintendo Switch™ / PC の Gamepad API のボタン(buttons)と軸(axes)の対応。(個人調べ)

ボタンは16個認識されました。でもキャプチャーボタンは、PC 使用不可と取扱説明書に書いてあったとおり認識されませんでした。

ボタン# ホリパッドTURBOボタン
0 A
1 B
2 X
3 Y
4 L
5 R
6 ZL
7 ZR
8
9
10 Lスティック
11 Rスティック
12 十字ボタン 上
13 十字ボタン 下
14 十字ボタン 左
15 十字ボタン 右
16 HOME

LとRのスティックも認識されましたが、上下と左右で別々に認識されるんですね。

軸# ホリパッドTURBOスティック
0 Lスティック 左右 -1.0: 左、+1.0: 右
1 Lスティック 上下 -1.0: 上、+1.0: 下
2 Rスティック 左右 -1.0: 左、+1.0: 右
3 Rスティック 上下 -1.0: 上、+1.0: 下

Gamepad API では、この「ボタン#」と「軸#」を使って識別しています。

Javascriptのコード

そんなに長くならなかったのでindex.htmlに全部入れたのを載せます。

地図は、国土地理院の地理院タイル 電子国土基本図(オルソ画像) を使いましたが特に深い意味はないです。素直に、Cesium Ion のを使っても見れると思います。

index.html
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8">
  <title>cesium</title>
  <link rel="icon" href="data:,">
  <meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=no">
  <script src="https://cesium.com/downloads/cesiumjs/releases/1.124/Build/Cesium/Cesium.js"></script>
  <link href="https://cesium.com/downloads/cesiumjs/releases/1.124/Build/Cesium/Widgets/widgets.css" rel="stylesheet">
  <style>
    html,
    body,
    #cesiumContainer {
      width: 100%;
      height: 100%;
      margin: 0;
      padding: 0;
      overflow: hidden;
    }
  </style>
</head>

<body>
  <div id="cesiumContainer"></div>
  <script type="module">
    // Cesium
    // https://cesium.com/learn/cesiumjs-learn/cesiumjs-quickstart/

    // Cesium Ion のアクセストークンを設定する
    // Cesium.Ion.defaultAccessToken = 'your_access_token';

    // ビューのホームとして富士山を設定する
    Cesium.Camera.DEFAULT_VIEW_RECTANGLE = Cesium.Rectangle.fromDegrees(138.63, 35.47, 138.83, 35.27);
    Cesium.Camera.DEFAULT_VIEW_FACTOR = 0;

    const viewer = new Cesium.Viewer('cesiumContainer', {
      baseLayer: Cesium.ImageryLayer.fromProviderAsync(
        // 国土地理院提供の地理院タイル(オルソ画像)を利用する設定
        new Cesium.OpenStreetMapImageryProvider({
          url: 'https://cyberjapandata.gsi.go.jp/xyz/ort/',
          fileExtension: 'jpg',
          credit: new Cesium.Credit('<a href="https://maps.gsi.go.jp/development/ichiran.html">地理院タイル 電子国土基本図(オルソ画像)</a>', true)
        }),
      ),
      geocoder: false,  // 検索ボタン
      homeButton: true,  // ホームボタン
      baseLayerPicker: false,  // レイヤボタン
      requestRenderMode: true,  // https://cesium.com/blog/2018/01/24/cesium-scene-rendering-performance/#enabling-request-render-mode
      terrain: Cesium.Terrain.fromWorldTerrain(),  // 地形
    });

    function flyToMountFuji() {
      viewer.camera.flyTo({
        destination: Cesium.Cartesian3.fromDegrees(138.73, 34.87, 20000.0),
        orientation: {
          heading: 0.0,
          pitch: Cesium.Math.toRadians(-20.0),
          roll: 0.0,
        }
      });
    }
    flyToMountFuji();

    // Gamepad
    var loopRequired = false;

    function addgamepad(e) {
      // console.log('addgamepad', e.gamepad);
      if (!loopRequired) {
        requestAnimationFrame(updateStatus);
        loopRequired = true;
      }
    }

    function axisValue(val) {
      return Math.round(val * 1000);
    }

    function updateStatus() {
      loopRequired = false;
      for (const gamepad of navigator.getGamepads()) {
        if (!gamepad) continue;
        loopRequired = true;
        // for (const [i, button] of gamepad.buttons.entries()) {
        //   if (button.pressed) {
        //     console.log('button pressed', i, button);
        //   }
        // }
        // for (const [i, axis] of gamepad.axes.entries()) {
        //   var val = axisValue(axis)
        //   if (val != 0) {
        //     console.log('axis x 1000', i, val);
        //   }
        // }
        if (gamepad.buttons[2].pressed) {  // Xボタン
          var amount = 10.0;
          if (gamepad.buttons[7].pressed) {  // ZRボタン
            amount *= 20.0;
          }
          viewer.camera.zoomIn(amount);
        }
        if (gamepad.buttons[3].pressed) {  // Yボタン
          var amount = 10.0;
          if (gamepad.buttons[7].pressed) {  // ZRボタン
            amount *= 20.0;
          }
          viewer.camera.zoomOut(amount);
        }
        val = axisValue(gamepad.axes[0])  // Lスティック左右
        if (val != 0) {
          var angle = 1e-11 * Math.abs(val)*val;
          if (gamepad.buttons[6].pressed) {  // LRボタン
            angle *= 20.0;
          }
          viewer.camera.rotateRight(angle);
        }
        var val = axisValue(gamepad.axes[1])  // Lスティック上下
        if (val != 0) {
          var angle = 1e-11 * Math.abs(val)*val;
          if (gamepad.buttons[6].pressed) {  // LRボタン
            angle *= 20.0;
          }
          viewer.camera.rotateUp(angle);
        }
        var val = axisValue(gamepad.axes[2])  // Rスティック左右
        if (val != 0) {
          var angle = 1e-6 * val;
          viewer.camera.lookRight(angle);
        }
        var val = axisValue(gamepad.axes[3])  // Rスティック上下
        if (val != 0) {
          var angle = 1e-6 * val;
          if (gamepad.buttons[7].pressed) {  // ZRボタン
            angle *= 20.0;
          }
          viewer.camera.lookUp(angle);
        }
      }
      if (loopRequired) {
        requestAnimationFrame(updateStatus);
      }
    }

    window.addEventListener("gamepadconnected", addgamepad);
  </script>
  </div>
</body>

</html>

前半はCesium部分です。ブラウザでアクセスすると富士山が見えるように設定しました。
後半が Gamepad API部分です。ゲームパッド API の使用 にあったように定期的にゲームコントローラの gamepad オブジェクトを取得し、ボタンやステックの値を見ているだけになります。ゲームコントローラが接続された時のイベントgamepadconnectedをみて updateStatus を定期的に呼び出すかどうかを判断しています。ゲームパッド API の使用 では ゲームコントローラが切断された時のイベントgamepaddisconnected もあったのですが使わなかったので入っていません。
ボタンやステックが操作されたときに、Cesium のカメラの位置を変更しています。動かす値とかは動かしてなんとなく決めた値です。この編はカメラの位置やZoomとかで変化させたほうがよいと思ったのですがそのあたりがわからず、動かして決めました。Lステックでは viewer.camera.rotateXXX を使っています。これは地球に沿って移動するというものです。

おわり

Lステックで左右に倒すと横にスライドするように動きます。飛行機のように旋回して動くような感じにはならなかった。 rotateXXX がよくなかったのかも…

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?