Androidアプリでゲームパッドのイベントを取る

ミニ四駆ラジコンのプロポアプリを作って痛感したのは、タッチパネルUIだとスティックの感触がないから画面から目を離して操作しづらい、ということです。これはラジコンやロボットのコントローラとしては致命的です。というわけで、ゲームパッドを使えるようにしたいと思いました。

購入したゲームパッド

今回購入したゲームパッドは、ipegaのPG9023という機種。Bluetooth接続のゲームパッドで、スマホタブレットをがっちり挟み込める構造になっています。
PG9023
このゲームパッドはペアリング時のボタン押下でモードが決まります。

  • Xボタン+HOMEボタン → ゲームパッドモード (Android用)
  • Aボタン+HOMEボタン → キーボードモード
  • Bボタン+HOMEボタン → iCadeモード (iOS用)
  • Yボタン+HOMEボタン → SPPモード

今回はゲームパッドモードで使用します。ちなみにHOMEボタン長押しで電源OFF、一度ペアリングした後はHOMEボタン押下で電源ON&再接続できる仕様です。

イベントの取得

  • ボタンのイベントは onKeyDown/onKeyUp/onKeyLongPress
  • アナログスティックのイベントは onGenericMotionEvent

をActivityでオーバーライドすれば取得できます。
ボタンやスティックによってはプライマリ/セカンダリの2個のイベントを発生するものがあります。まずプライマリイベントが発生し、それがアプリによって処理されなかったときにセカンダリイベントが発生します。上記のイベントハンドラでtrueを返すと、イベントは処理されたことになります。

ボタンのイベント

どのボタンが押されたかはonKeyDownの引数のkeyCodeの値で得られます。PG9023で実際の各ボタンのkeyCode値を調べてみました。機種によって細かい違いはあろうかと思いますが、主要なボタンはたぶん互換性があるでしょう。値1/値2はプライマリ/セカンダリの値を示します。
【主なボタン】

ボタン プライマリ セカンダリ
十字↑ - - KEYCODE_DPAD_UP 19
十字↓ - - KEYCODE_DPAD_DOWN 20
十字← - - KEYCODE_DPAD_LEFT 21
十字→ - - KEYCODE_DPAD_RIGHT 22
A KEYCODE_BUTTON_A 96 KEYCODE_DPAD_CENTER 23
B KEYCODE_BUTTON_B 97 KEYCODE_BACK 4
X KEYCODE_BUTTON_X 99 KEYCODE_DEL 67
Y KEYCODE_BUTTON_Y 100 KEYCODE_SPACE 62
L1 KEYCODE_BUTTON_L1 102 - -
R1 KEYCODE_BUTTON_R1 103 - -
L2 - - KEYCODE_BUTTON_L2 104
R2 - - KEYCODE_BUTTON_R2 105

【左ジョイスティック】

ボタン プライマリ セカンダリ
押し下げ KEYCODE_BUTTON_THUMBL 106 KEYCODE_DPAD_CENTER 23
- - KEYCODE_DPAD_UP 19
- - KEYCODE_DPAD_DOWN 20
- - KEYCODE_DPAD_LEFT 21
- - KEYCODE_DPAD_RIGHT 22

【右ジョイスティック】

ボタン プライマリ セカンダリ
押し下げ KEYCODE_BUTTON_THUMBR 107 KEYCODE_DPAD_CENTER 23
- - - -
- - - -
- - - -
- - - -

【その他のボタン】

ボタン プライマリ セカンダリ
SELECT KEYCODE_BUTTON_SELECT 109 KEYCODE_MENU 82
HOME - - - -
START KEYCODE_BUTTON_START 108 KEYCODE_DPAD_CENTER 23
- KEYCODE_VOLUME_DOWN 25 - -
+ KEYCODE_VOLUME_UP 24 - -
巻き戻し KEYCODE_MEDIA_PREVIOUS 88 - -
再生/停止 KEYCODE_MEDIA_PLAY_PAUSE 85 - -
早送り KEYCODE_MEDIA_NEXT 87 - -

アナログスティックのイベント

各軸の値は、onGenericMotionEventのevent.getAxisValue(MotionEvent.軸名)で得られます。値の型はfloatです。

軸名 値の範囲
ジョイスティック 横 AXIS_X -1〜+1
ジョイスティック 縦 AXIS_Y -1〜+1
ジョイスティック 横 AXIS_Z -1〜+1
ジョイスティック 縦 AXIS_RZ -1〜+1
十字 横 AXIS_HAT_X -1〜+1
十字 縦 AXIS_HAT_Y -1〜+1
Lトリガ(L2) AXIS_LTRIGGER 0〜1 ※
Rトリガ(R2) AXIS_RTRIGGER 0〜1 ※

※ PG9023のL2,R2はアナログではないただのボタンのためか、アナログ値は常に0でした。

ソースコード

    // ボタンのイベント
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event){
        boolean handled = false; // 処理したらtrueに
        
        // キーコードをログ出力してみる
        String msg = "keyCode:" + keyCode;
        Log.e("GamePad", msg);

        return handled || super.onKeyDown(keyCode, event);
    }
    // アナログのイベント
    @Override
    public boolean onGenericMotionEvent(MotionEvent event){
        boolean handled = false; // 処理したらtrueに

        // 左ジョイスティックの値をログ出力してみる
        float x = event.getAxisValue(MotionEvent.AXIS_X);
        float y = event.getAxisValue(MotionEvent.AXIS_Y);
        String msg = "(x,y)=" + x + "," + y;
        Log.e("GamePad", msg);
        
        return handled || super.onGenericMotionEvent(event);
    }