🎮

Nintendo Switch Pro コントローラーで、Phaserのブラウザゲームを操作する方法

に公開

JavaScript製ブラウザゲームフレームワークであるPhaserには、Gamepad APIが存在しています。
このAPIを使うことでPCに接続されたコントローラーを使用して、ブラウザゲームを操作できます。

今回は、Nintendo Switch Pro コントローラーをPCに接続し、Phaserのブラウザゲームを操作してみます。

成果物

デモURL スクリーンショット
Nintendo Switch Pro コントローラーを介して東北きりたんを操作できます。

動作確認方法

  1. USBケーブルまたはBluetoothで、Nintendo Switch Pro コントローラーをPCに接続します。
    • 動作保証のため、PCに接続するコントローラーは、Nintendo Switch Pro コントローラー1台のみにしてください。
  2. 上記のデモURLを開きます。
  3. 画面表示後、Nintendo Switch Pro コントローラーの任意のボタンを押下することで、コントローラーの接続イベント(connected)が発火します。
  4. 接続イベント発火後は、Nintendo Switch Pro コントローラーの操作を受け付けるようになります。十字カーソルで東北きりたんを操作できます。
    • 画面右下の「Toggle Control」を押下すると、十字カーソル操作から左スティック操作に切り替わります。
    • 操作中のボタン状態は、画面右上のNintendo Switch Pro コントローラーのマッピングされます。

ソースコード解説

要点を絞って解説していきます。

GamePad APIの有効化

まずはじめに、PhaserのGameConfigにあるinput.gamepadtrueに設定して、GamePad APIを有効にします。
> src/config.ts

src/config.ts
import Phaser from 'phaser';
import { MainScene } from './scenes/mainScene/mainScene';

export const config: Phaser.Types.Core.GameConfig = {
  type: Phaser.AUTO,
  scale: {
    // 省略
  },
  physics: {
    // 省略
  },
  input: {
    gamepad: true // GamePad APIを有効化
  },
  scene: [MainScene]
};

Nintendo Switch Pro コントローラーとの接続

Phaser.Sceneのcreate関数内で、this.input.gamepadを利用して、コントローラーとの接続を試みます。
this.input.gamepad.once('connected', (pad) => {…})の形式で設定します。
> src/scenes/mainScene/mainScene.ts

コントローラーと正しく接続できた後は、各ボタン操作時のイベントをセットしていきます。
コントローラー接続時のコールバック関数の引数pad: Phaser.Input.Gamepad.Gamepadを利用して、ボタン押下時はdownイベント。ボタンを離すときはupイベントを設定します。

あとは、downイベント、upイベントのコールバック関数に、具体的な処理を実装していくだけですね。

src/scenes/mainScene/mainScene.ts
this.input.gamepad.once(
  'connected',
  (pad: Phaser.Input.Gamepad.Gamepad) => {
    this.pad = pad; // 接続したコントローラーを保持

    // ボタン押下時のイベントをセット
    pad.on('down', (buttonCode: number) => {
      this.renderControllerButtonStatusText(buttonCode, true);
      this.renderControllerButtonImage(buttonCode, true);
      if (!this.isLStickControl) {
        this.movePlayerWithCursor(buttonCode, true);
      }
    });
    // ボタンを話した時のイベントをセット
    pad.on('up', (buttonCode: number) => {
      this.renderControllerButtonStatusText(buttonCode, false);
      this.renderControllerButtonImage(buttonCode, false);
      if (!this.isLStickControl) {
        this.movePlayerWithCursor(buttonCode, false);
      }
    });
  }
);

ちなみに、Phaser x Nintendo Switch Pro コントローラーのボタン操作において、1つ罠が存在します。
後述します。

Nintendo Switch Pro コントローラーのスティックの傾きを取得する

左スティックと右スティックの傾きは、Phaser.Input.Gamepad.Gamepadaxesから取得できます。
> src/scenes/mainScene/mainScene.ts

Nintendo Switch Pro コントローラーの場合は…

  • pad.axes[0]:左スティックのx方向
  • pad.axes[1]:左スティックのy方向
  • pad.axes[2]:右スティックのx方向
  • pad.axes[3]:右スティックのy方向

で取得できました。

src/scenes/mainScene/mainScene.ts
update() {
  if (!this.pad) return;
  // left stick
  this.texts[0].text = `left stick axes x: ${this.pad.axes[0].getValue()}`;
  this.texts[1].text = `left stick axes y: ${this.pad.axes[1].getValue()}`;
  this.stickLButton.setPosition(
    this.controllerBg.x +
      this.controllerBg.width * -0.275 +
      14 * this.pad.axes[0].getValue(),
    this.controllerBg.y +
      this.controllerBg.height * -0.195 +
      14 * this.pad.axes[1].getValue()
  );

  // right stick
  this.texts[2].text = `right stick axes x: ${this.pad.axes[2].getValue()}`;
  this.texts[3].text = `right stick axes y: ${this.pad.axes[3].getValue()}`;
  this.stickRButton.setPosition(
    this.controllerBg.x +
      this.controllerBg.width * 0.13 +
      14 * this.pad.axes[2].getValue(),
    this.controllerBg.y +
      this.controllerBg.height * 0.002 +
      14 * this.pad.axes[3].getValue()
  );
}

Phaser x Nintendo Switch Pro コントローラーにおけるボタンコード対応表

以下は、Phaser x Nintendo Switch Pro コントローラーの組み合わせにおけるボタンコード対応表です。

ボタン コード
B 0
A 1
Y 2
X 3
L 4
R 5
ZL 6
ZR 7
マイナス 8
プラス 9
左スティック 10
右スティック 11
十字カーソル ↑ 12
十字カーソル ↓ 13
十字カーソル ← 14
十字カーソル → 15
ホーム 16
キャプチャ 17

ボタンコードは、先述のpad: Phaser.Input.Gamepad.Gamepadに対して、ボタン操作イベントを設定時のコールバック関数の引数から取得できます。
> src/scenes/mainScene/mainScene.ts

src/scenes/mainScene/mainScene.ts
pad.on('down', (buttonCode: number) => {
  // ↑コールバック関数の引数がボタンコードです
  // 省略
});

注意点: Phaser x Nintendo Switch Pro コントローラーの罠

先ほど「Phaser x Nintendo Switch Pro コントローラーのボタン操作において、1つ罠が存在します。」と書いた点の詳細です。

実は Phaser において「どのボタンを押下したか」は、pad: Phaser.Input.Gamepad.Gamepad から、pad.ボタン名のカタチでも取得できます。

例えば、pad.L1pad.L2pad.R1pad.R2…など。
pad.upで十字カーソル↑、pad.downで十字カーソル↓…などなど、いろいろ用意されています。

this.input.gamepad.once(
  'connected',
  (pad: Phaser.Input.Gamepad.Gamepad) => {
    // button down
    pad.on('down', () => {
      console.log(pad.L1 ? "Lボタンを押下しました" : "Lボタン以外を押下しました")
    });
  }
);

しかし、本記事執筆時点のPhaser(v3.88.2)と、Nintendo Switch Pro コントローラーの組み合わせにおいて、pad.Aとpad.Bの実態が逆転します。
Aボタンを押下するとpad.Bがtrueとなり、Bボタンを押下するとpad.Aがtrueとなります。

原因は、よく分からないです…。

  • PhaserのPhaser.Input.Gamepad.Configsを見てみると、DUALSHOCK_4XBOX_360SNES_USBが定義されており、Nintendo Switch Pro コントローラーの定義はない。
    単純にPhaser側でNintendo Switch Pro コントローラーが最適化されていないから?
  • 海外製のコントローラーと、日本製のコントローラーとでキー配置が異なるから?(本当に?)
  • 私のPCが原因? MacOS Sequoiaなのですが、Windowsならうまくいく…とか?(本当に?)

…など、考えられる原因はいくつかあるのですが、ボタンのdownupイベントのコールバック関数のボタンコードから、ちゃんと判定できるので「まぁ ヨシッ!」としました。

まとめ

以上、Nintendo Switch Pro コントローラーで、Phaserのブラウザゲームを操作する方法でした。
無事にコントローラーに接続できましたので、ちょっとゲームを作ってみようと思います。

Discussion