日別アーカイブ: 2011年8月11日

Android 3.1 と GamePad のイベントの詳細 (2)

引き続き Android OS 3.1 に Xbox360 Pad と Playstation3 Pad を
つないだ場合の値の取り方です。(前回)
アナログスティックの情報は onGenericMotionEvent で受け取れます。

@Override
public boolean onGenericMotionEvent( MotionEvent event ) {
  float posx= event.getAxisValue( MotionEvent.AXIS_X );
  ~
}

入力値に差が生じた場合のみイベントが発生します。
getX()/getY() は getAxisValue() の AXIS_X, AXIS_Y と同値です。

対応付は下記の通り

axis            Xbox360Pad   PS3 Pad       範囲  Source
-----------------------------------------------------------------------
AXIS_X          左stick 横   左stick 横   -1~1  SOURCE_CLASS_JOYSTICK
AXIS_Y          左stick 縦   左stick 縦   -1~1  SOURCE_CLASS_JOYSTICK
AXIS_Z          右stick 横   右stick 横   -1~1  SOURCE_CLASS_JOYSTICK
AXIS_RZ         右stick 縦   右stick 縦   -1~1  SOURCE_CLASS_JOYSTICK
AXIS_HAT_X      十字キー横   ----         -1~1  SOURCE_CLASS_JOYSTICK
AXIS_HAT_Y      十字キー縦   ----         -1~1  SOURCE_CLASS_JOYSTICK
AXIS_LTRIGGER   Lトリガ      L2            0~1
AXIS_RTRIGGER   Rトリガ      R2            0~1

左上が (-1,-1), 右下が (1,1)

Xbox360 Pad のみ、デジタル方向ボタン(十字キー) が MotionEvent の
AXIS_HAT_X/AXIS_HAT_Y を返します。(KeyEvent も発生します)

他にも MotionEvent と同時に KeyEvent を発生させるものがあります。

◎Xbox360 Pad / PS3 Pad
・AXIS_X           KEYCODE_DPAD_LEFT/KEYCODE_DPAD_RIGHT
・AXIS_Y           KEYCODE_DPAD_UP/KEYCODE_DPAD_DOWN

◎PS3 Pad のみ
・AXIS_LTRIGGER    KEYCODE_L2
・AXIS_RTRIGGER    KEYCODE_R2

◎Xbox360 Pad のみ
・AXIS_HAT_X       KEYCODE_DPAD_LEFT/KEYCODE_DPAD_RIGHT
・AXIS_HAT_Y       KEYCODE_DPAD_UP/KEYCODE_DPAD_DOWN

●デジタル方向キーとアナログスティックの区別方法

ここで問題となるのは、左アナログスティックで MotionEvent だけでなく
KeyEvent も発生してしまうことです。
デジタル方向ボタンの代わりに左アナログスティックを使うことができる反面、
アナログとデジタルに異なる操作を割り当てたいときに困ります。

KeyCode だけではこの両者を区別できないので方法を探しました。

Xbox360 Pad の方向キーは AXIS_HAT_X/AXIS_HAT_Y からも値をとれるため
KeyEvent を参照せずにこちらを使えば区別することができます。

PS3 Pad の場合は SOURCE_CLASS で区別できるようです。

訂正 2011/08/11 不要でした。Xbox360 のみ AXIS → KeyCode 変換だけで OK です。

KeyEvent.getSource() & InputDevice.SOURCE_CLASS_MASK

PS3 のみデジタル方向キーが SOURCE_CLASS_BUTTON となります。

◎KeyEvent の SOURCE_CLASS 値

                    Xbox360 Pad              PS3 Pad
--------------------------------------------------------------------
デジタル方向キー    SOURCE_CLASS_JOYSTICK    SOURCE_CLASS_BUTTON
アナログ方向キー    SOURCE_CLASS_JOYSTICK    SOURCE_CLASS_JOYSTICK


◎MotionEvent の情報

                    Xbox360 Pad              PS3 Pad
--------------------------------------------------------------------
デジタル方向キー    AXIS_HAT_X/AXIS_HAT_Y    無し
アナログ方向キー    AXIS_X/AXIS_Y            AXIS_X/AXIS_Y

実際のコード(Activity 抜粋)は下記の通り。
これで Xbox360 Pad / PS3 Pad 共に、デジタル方向ボタンを
KEYCODE_DPAD_UP/DOWN/LEFT/RIGHT を受け取れるようになります。
(プログラムコード訂正しました 2011/08/11 )

final static int   SENDKEY_UP      =   0;
final static int   SENDKEY_DOWN    =   1;

private int mPrevState= 0;

private boolean sendKey( int KeyCode, int up_down ) {
   // 実際のキーコード処理

   ~
}

@Override
public boolean onKeyDown( int KeyCode, KeyEvent event ) {
    return  sendKey( KeyCode, SENDKEY_DOWN );
}

@Override
public boolean onKeyUp( int KeyCode, KeyEvent event ) {
    return  sendKey( KeyCode, SENDKEY_UP );
}

@Override
public boolean onGenericMotionEvent( MotionEvent event ) {
    // Xbox360 の 十字キーはこれで判別
    final int   KEYFLAG_UP      = (1<<0);
    final int   KEYFLAG_DOWN    = (1<<1);
    final int   KEYFLAG_LEFT    = (1<<2);
    final int   KEYFLAG_RIGHT   = (1<<3);
    final float HAT_BORDER= 0.5f;

    float hatx= event.getAxisValue( MotionEvent.AXIS_HAT_X );
    float haty= event.getAxisValue( MotionEvent.AXIS_HAT_Y );

    int code= 0;
    if( hatx < -HAT_BORDER ){
        code|= KEYFLAG_LEFT;
    }
    if( hatx >  HAT_BORDER ){
        code|= KEYFLAG_RIGHT;
    }
    if( haty < -HAT_BORDER ){
        code|= KEYFLAG_UP;
    }
    if( haty >  HAT_BORDER ){
        code|= KEYFLAG_DOWN;
    }

    int diff= code ^ mPrevState;
    mPrevState= code;
    for( int kc= KeyEvent.KEYCODE_DPAD_UP ; diff != 0 ; diff>>= 1, code>>= 1, kc++ ){
        if( (diff & 1) != 0 ){
            // 代わりのキーコードを発生させる
            sendKey( kc, (code & 1) != 0 ? SENDKEY_DOWN : SENDKEY_UP );
        }
    }


    // アナログ値の受け取り
    float lx= event.getAxisValue( MotionEvent.AXIS_X );
    float ly= event.getAxisValue( MotionEvent.AXIS_Y );
    float rx= event.getAxisValue( MotionEvent.AXIS_Z );
    float ry= event.getAxisValue( MotionEvent.AXIS_RZ );
    float tl= event.getAxisValue( MotionEvent.AXIS_LTRIGGER );
    float tr= event.getAxisValue( MotionEvent.AXIS_RTRIGGER );

    ~
}

●複数のパッドの認識

USB ハブを経由して、同時に複数のコントローラを接続することができました。
どのコントローラも上記の同じイベントを発生させます。

getDeviceId() を参照することで、どのコントローラが送ったイベントなのか
区別できるようです。

Xbox360 Pad と PS3 Pad の組み合わせは OK、PS3 Pad を同時に 2個つないでも
きちんと区別できました。バスパワーハブだったため 3個以上は試していません。

● Playstation3 Pad (PS3 Pad) の注意点

PS3 Pad はもともとワイヤレスコントローラですが、Android 3.1 の場合
USB ケーブルで繋ぎます。

 1. PS3 Pad が電源 OFF の状態を確認
 2. USB ケーブルで Android 3.1 端末に繋ぐ
 3. (PS) ボタンを押す
 4. LED 4つが点滅したままだけど問題なし。そのまま使える。

接続前に (PS) ボタンを押してしまうとペアリングしてある PS3 本体の
電源が入ってしまうので要注意です。
ワイヤレスで PS3 本体につながってしまうと USB ケーブルをつないでも
PS3 が優先され Android で認識できません。
最初の頃、遠くにある PS3 本体の電源がいつの間にか入っていて、
なかなかつながらずにはまりました。

関連エントリ
Android 3.1 と GamePad のイベントコード