コントローラの操作を処理する

システムレベルでは、Android はゲーム コントローラからの入力イベントコードを Android キーコードと軸値としてレポートします。ゲームでは、これらのコードと値を受け取って、特定のゲーム内アクションに変換できます。

プレーヤーがゲーム コントローラを Android 搭載デバイスに物理的に接続するかワイヤレスでペア設定すると、システムはコントローラを入力デバイスとして自動検出し、入力イベントの報告を開始します。ゲームは、アクティブな Activity またはフォーカスされている View に次のコールバック メソッドを実装することで、これらの入力イベントを受信できます(Activity または View のいずれかのコールバックを実装する必要がありますが、両方を実装することはできません)。

ユーザーが操作する特定の View オブジェクトからイベントをキャプチャすることをおすすめします。 コールバックで提供される次のオブジェクトを調べて、受け取った入力イベントの種類に関する情報を取得します。

KeyEvent
十字キー(D-pad)とゲームパッド ボタンのイベントを記述するオブジェクト。キーイベントには、トリガーされる特定のボタンを示すキーコードDPAD_DOWNBUTTON_A など)が付属しています。キーコードは、getKeyCode() を呼び出すか、onKeyDown() などのキーイベント コールバックから取得できます。
MotionEvent
ジョイスティックとショルダー トリガーの動きからの入力を記述するオブジェクト。モーション イベントには、アクション コードと一連の軸の値が含まれます。アクション コードは、ジョイスティックの移動など、発生した状態変化を指定します。軸の値は、AXIS_XAXIS_RTRIGGER など、特定の物理コントロールの位置などの移動プロパティを表します。アクション コードを取得するには getAction() を、軸の値を取得するには getAxisValue() を呼び出します。

このレッスンでは、上記の View コールバック メソッドを実装し、KeyEvent オブジェクトと MotionEvent オブジェクトを処理することで、ゲーム画面の最も一般的なタイプの物理コントロール(ゲームパッド ボタン、十字キー、ジョイスティック)からの入力を処理する方法に焦点を当てます。

ゲーム コントローラが接続されていることを確認する

Android は入力イベントをレポートする際、ゲーム以外のコントローラ デバイスから発生したイベントとゲーム コントローラからのイベントを区別しません。たとえば、タッチ スクリーン操作の場合は、タッチ サーフェスの X 座標を表す AXIS_X イベントが生成されますが、ジョイスティックの場合は、ジョイスティックの X 位置を表す AXIS_X イベントが生成されます。ゲーム コントローラの入力処理を考慮する場合は、まず、入力イベントが関連するソースタイプから送信されていることを確認する必要があります。

接続された入力デバイスがゲーム コントローラであることを確認するには、getSources() を呼び出して、そのデバイスでサポートされている入力ソースタイプの結合ビット フィールドを取得します。その後、次のフィールドが設定されているかどうかをテストできます。

  • ソースタイプが SOURCE_GAMEPAD の場合、入力デバイスにゲームパッド ボタン(BUTTON_A など)があることを示します。このソースタイプは、ゲーム コントローラに D-pad ボタンがあるかどうかを厳密に示すものではありませんが、通常はほとんどのゲームパッドには方向コントロールがあります。
  • ソースタイプが SOURCE_DPAD の場合は、入力デバイスに D-pad ボタンがあることを示します(例: DPAD_UP)。
  • ソースタイプが SOURCE_JOYSTICK の場合、入力デバイスにアナログのコントロール スティック(AXIS_XAXIS_Y に沿った動きを記録するジョイスティックなど)があることを示します。

次のコード スニペットは、接続されている入力デバイスがゲーム コントローラかどうかを確認するためのヘルパー メソッドを示しています。デバイス ID があれば、このメソッドはゲーム コントローラのデバイス ID を取得します。これにより、各デバイス ID をゲーム内のプレーヤーに関連付けて、接続された各プレーヤーのゲーム アクションを個別に処理できます。同じ Android デバイスで同時に接続されている複数のゲーム コントローラをサポートする方法については、複数のゲーム コントローラをサポートするをご覧ください。

Kotlin

fun getGameControllerIds(): List<Int> {
    val gameControllerDeviceIds = mutableListOf<Int>()
    val deviceIds = InputDevice.getDeviceIds()
    deviceIds.forEach { deviceId ->
        InputDevice.getDevice(deviceId).apply {

            // Verify that the device has gamepad buttons, control sticks, or both.
            if (sources and InputDevice.SOURCE_GAMEPAD == InputDevice.SOURCE_GAMEPAD
                    || sources and InputDevice.SOURCE_JOYSTICK == InputDevice.SOURCE_JOYSTICK) {
                // This device is a game controller. Store its device ID.
                gameControllerDeviceIds
                        .takeIf { !it.contains(deviceId) }
                        ?.add(deviceId)
            }
        }
    }
    return gameControllerDeviceIds
}

Java

public ArrayList<Integer> getGameControllerIds() {
    ArrayList<Integer> gameControllerDeviceIds = new ArrayList<Integer>();
    int[] deviceIds = InputDevice.getDeviceIds();
    for (int deviceId : deviceIds) {
        InputDevice dev = InputDevice.getDevice(deviceId);
        int sources = dev.getSources();

        // Verify that the device has gamepad buttons, control sticks, or both.
        if (((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD)
                || ((sources & InputDevice.SOURCE_JOYSTICK)
                == InputDevice.SOURCE_JOYSTICK)) {
            // This device is a game controller. Store its device ID.
            if (!gameControllerDeviceIds.contains(deviceId)) {
                gameControllerDeviceIds.add(deviceId);
            }
        }
    }
    return gameControllerDeviceIds;
}

また、接続されたゲーム コントローラでサポートされている入力機能を個別に確認することもできます。これは、ゲームで理解できる一連の物理コントロールからの入力のみを使用したい場合などに役立ちます。

接続されたゲーム コントローラによって特定のキーコードまたは軸コードがサポートされているかどうかを検出するには、次の方法を使用します。

  • Android 4.4(API レベル 19)以降では、hasKeys(int...) を呼び出すことで、接続されているゲーム コントローラでキーコードがサポートされているかどうかを確認できます。
  • Android 3.1(API レベル 12)以降では、最初に getMotionRanges() を呼び出すことで、接続済みのゲーム コントローラでサポートされているすべての使用可能な軸を確認できます。次に、返された各 InputDevice.MotionRange オブジェクトで getAxis() を呼び出して、その軸 ID を取得します。

ゲームパッド ボタンの押下を処理する

図 1 は、Android がほとんどのゲーム コントローラの物理コントロールにキーコードと軸値をマッピングする方法を示しています。

図 1. 汎用ゲーム コントローラのプロファイル。

図の吹き出しについては、以下を参照してください。

ゲームパッドのボタンの押下によって生成される一般的なキーコードには、BUTTON_ABUTTON_BBUTTON_SELECTBUTTON_START などがあります。一部のゲーム コントローラは、D-pad クロスバーの中心が押されたときに DPAD_CENTER キーコードをトリガーします。ゲームはキーコードを getKeyCode() を呼び出すか、キーイベント コールバック(onKeyDown() など)から検査できます。キーコードがゲームに関連するイベントを表す場合は、ゲーム アクションとして処理します。表 1 に、よく使用されるゲームパッド ボタンで推奨されるゲーム アクションを示します。

表 1. ゲームパッド ボタンに対して推奨されるゲーム アクション。

ゲーム アクション ボタンのキーコード
メインメニューでゲームを開始する、ゲーム中に一時停止 / 一時停止解除する BUTTON_START*
メニューを表示 BUTTON_SELECT* および KEYCODE_MENU*
ナビゲーションの設計ガイドに記載されている Android の「戻る」ナビゲーション動作と同じです。 KEYCODE_BACK
メニュー内の前の項目に戻る BUTTON_B
選択を確認するか、メインのゲーム アクションを実行する BUTTON_ADPAD_CENTER

* ゲームにおいて、スタートボタン、選択ボタン、メニューボタンの存在に依存しないでください。

ヒント: ゲームに構成画面を用意して、ユーザーがゲーム アクションについて独自のゲーム コントローラ マッピングをカスタマイズできるようにすることを検討してください。

次のスニペットは、onKeyDown() をオーバーライドして、BUTTON_ADPAD_CENTER のボタンの押下をゲーム アクションに関連付ける方法を示しています。

Kotlin

class GameView(...) : View(...) {
    ...

    override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
        var handled = false
        if (event.source and InputDevice.SOURCE_GAMEPAD == InputDevice.SOURCE_GAMEPAD) {
            if (event.repeatCount == 0) {
                when (keyCode) {
                    // Handle gamepad and D-pad button presses to navigate the ship
                    ...

                    else -> {
                        keyCode.takeIf { isFireKey(it) }?.run {
                            // Update the ship object to fire lasers
                            ...
                            handled = true
                        }
                    }
                }
            }
            if (handled) {
                return true
            }
        }
        return super.onKeyDown(keyCode, event)
    }

    // Here we treat Button_A and DPAD_CENTER as the primary action
    // keys for the game.
    private fun isFireKey(keyCode: Int): Boolean =
            keyCode == KeyEvent.KEYCODE_DPAD_CENTER || keyCode == KeyEvent.KEYCODE_BUTTON_A
}

Java

public class GameView extends View {
    ...

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        boolean handled = false;
        if ((event.getSource() & InputDevice.SOURCE_GAMEPAD)
                == InputDevice.SOURCE_GAMEPAD) {
            if (event.getRepeatCount() == 0) {
                switch (keyCode) {
                    // Handle gamepad and D-pad button presses to
                    // navigate the ship
                    ...

                    default:
                         if (isFireKey(keyCode)) {
                             // Update the ship object to fire lasers
                             ...
                             handled = true;
                         }
                     break;
                }
            }
            if (handled) {
                return true;
            }
        }
        return super.onKeyDown(keyCode, event);
    }

    private static boolean isFireKey(int keyCode) {
        // Here we treat Button_A and DPAD_CENTER as the primary action
        // keys for the game.
        return keyCode == KeyEvent.KEYCODE_DPAD_CENTER
                || keyCode == KeyEvent.KEYCODE_BUTTON_A;
    }
}

注: Android 4.2(API レベル 17)以前では、システムはデフォルトで BUTTON_A を Android の戻るキーとして扱います。アプリがこれらの Android バージョンをサポートしている場合は、BUTTON_A をメインのゲーム アクションとして扱います。デバイスの現在の Android SDK バージョンを確認するには、Build.VERSION.SDK_INT の値を参照します。

十字キーの入力を処理する

4 方向十字キー(D-pad)は、多くのゲーム コントローラで一般的な物理コントロールです。Android は、D-pad の上または下への押下を -1.0(上)から 1.0(下)の範囲の AXIS_HAT_Y イベントとしてレポートし、D-pad の左または右の押下は -1.0(左)から 1.0(右)の範囲の AXIS_HAT_X イベントとしてレポートします。

D-pad の押下をキーコードでレポートするコントローラもあります。ゲームで D-pad の押下を扱う場合は、表 2 で推奨されているように、ハット軸のイベントと D-pad キーコードを同じ入力イベントとして扱う必要があります。

表 2. D-pad キーコードとハット軸の値に関して推奨されるデフォルトのゲーム アクション。

ゲーム アクション D-pad のキーコード ハット軸のコード
上に移動 KEYCODE_DPAD_UP AXIS_HAT_Y(値が 0 から -1.0 の場合)
下に移動 KEYCODE_DPAD_DOWN AXIS_HAT_Y(値が 0 から 1.0 の場合)
左に移動 KEYCODE_DPAD_LEFT AXIS_HAT_X(値が 0 から -1.0 の場合)
右に移動 KEYCODE_DPAD_RIGHT AXIS_HAT_X(値が 0 から 1.0 の場合)

次のコード スニペットは、入力イベントからハット軸とキーコードの値を確認して D-pad の方向を判断するヘルパークラスを示しています。

Kotlin

class Dpad {

    private var directionPressed = -1 // initialized to -1

    fun getDirectionPressed(event: InputEvent): Int {
        if (!isDpadDevice(event)) {
            return -1
        }

        // If the input event is a MotionEvent, check its hat axis values.
        (event as? MotionEvent)?.apply {

            // Use the hat axis value to find the D-pad direction
            val xaxis: Float = event.getAxisValue(MotionEvent.AXIS_HAT_X)
            val yaxis: Float = event.getAxisValue(MotionEvent.AXIS_HAT_Y)

            directionPressed = when {
                // Check if the AXIS_HAT_X value is -1 or 1, and set the D-pad
                // LEFT and RIGHT direction accordingly.
                xaxis.compareTo(-1.0f) == 0 -> Dpad.LEFT
                xaxis.compareTo(1.0f) == 0 -> Dpad.RIGHT
                // Check if the AXIS_HAT_Y value is -1 or 1, and set the D-pad
                // UP and DOWN direction accordingly.
                yaxis.compareTo(-1.0f) == 0 -> Dpad.UP
                yaxis.compareTo(1.0f) == 0 -> Dpad.DOWN
                else -> directionPressed
            }
        }
        // If the input event is a KeyEvent, check its key code.
        (event as? KeyEvent)?.apply {

            // Use the key code to find the D-pad direction.
            directionPressed = when(event.keyCode) {
                KeyEvent.KEYCODE_DPAD_LEFT -> Dpad.LEFT
                KeyEvent.KEYCODE_DPAD_RIGHT -> Dpad.RIGHT
                KeyEvent.KEYCODE_DPAD_UP -> Dpad.UP
                KeyEvent.KEYCODE_DPAD_DOWN -> Dpad.DOWN
                KeyEvent.KEYCODE_DPAD_CENTER ->  Dpad.CENTER
                else -> directionPressed
            }
        }
        return directionPressed
    }

    companion object {
        internal const val UP = 0
        internal const val LEFT = 1
        internal const val RIGHT = 2
        internal const val DOWN = 3
        internal const val CENTER = 4

        fun isDpadDevice(event: InputEvent): Boolean =
            // Check that input comes from a device with directional pads.
            event.source and InputDevice.SOURCE_DPAD != InputDevice.SOURCE_DPAD
    }
}

Java

public class Dpad {
    final static int UP       = 0;
    final static int LEFT     = 1;
    final static int RIGHT    = 2;
    final static int DOWN     = 3;
    final static int CENTER   = 4;

    int directionPressed = -1; // initialized to -1

    public int getDirectionPressed(InputEvent event) {
        if (!isDpadDevice(event)) {
           return -1;
        }

        // If the input event is a MotionEvent, check its hat axis values.
        if (event instanceof MotionEvent) {

            // Use the hat axis value to find the D-pad direction
            MotionEvent motionEvent = (MotionEvent) event;
            float xaxis = motionEvent.getAxisValue(MotionEvent.AXIS_HAT_X);
            float yaxis = motionEvent.getAxisValue(MotionEvent.AXIS_HAT_Y);

            // Check if the AXIS_HAT_X value is -1 or 1, and set the D-pad
            // LEFT and RIGHT direction accordingly.
            if (Float.compare(xaxis, -1.0f) == 0) {
                directionPressed =  Dpad.LEFT;
            } else if (Float.compare(xaxis, 1.0f) == 0) {
                directionPressed =  Dpad.RIGHT;
            }
            // Check if the AXIS_HAT_Y value is -1 or 1, and set the D-pad
            // UP and DOWN direction accordingly.
            else if (Float.compare(yaxis, -1.0f) == 0) {
                directionPressed =  Dpad.UP;
            } else if (Float.compare(yaxis, 1.0f) == 0) {
                directionPressed =  Dpad.DOWN;
            }
        }

        // If the input event is a KeyEvent, check its key code.
        else if (event instanceof KeyEvent) {

           // Use the key code to find the D-pad direction.
            KeyEvent keyEvent = (KeyEvent) event;
            if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_LEFT) {
                directionPressed = Dpad.LEFT;
            } else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_RIGHT) {
                directionPressed = Dpad.RIGHT;
            } else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_UP) {
                directionPressed = Dpad.UP;
            } else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_DOWN) {
                directionPressed = Dpad.DOWN;
            } else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_CENTER) {
                directionPressed = Dpad.CENTER;
            }
        }
        return directionPressed;
    }

    public static boolean isDpadDevice(InputEvent event) {
        // Check that input comes from a device with directional pads.
        if ((event.getSource() & InputDevice.SOURCE_DPAD)
             != InputDevice.SOURCE_DPAD) {
             return true;
         } else {
             return false;
         }
     }
}

このヘルパークラスは、ゲームにおいて D-pad 入力を処理する場所(onGenericMotionEvent() コールバックや onKeyDown() コールバックなど)で使用できます。

次に例を示します。

Kotlin

private val dpad = Dpad()
...
override fun onGenericMotionEvent(event: MotionEvent): Boolean {
    if (Dpad.isDpadDevice(event)) {
        when (dpad.getDirectionPressed(event)) {
            Dpad.LEFT -> {
                // Do something for LEFT direction press
                ...
                return true
            }
            Dpad.RIGHT -> {
                // Do something for RIGHT direction press
                ...
                return true
            }
            Dpad.UP -> {
                // Do something for UP direction press
                ...
                return true
            }
            ...
        }
    }

    // Check if this event is from a joystick movement and process accordingly.
    ...
}

Java

Dpad dpad = new Dpad();
...
@Override
public boolean onGenericMotionEvent(MotionEvent event) {

    // Check if this event if from a D-pad and process accordingly.
    if (Dpad.isDpadDevice(event)) {

       int press = dpad.getDirectionPressed(event);
       switch (press) {
            case LEFT:
                // Do something for LEFT direction press
                ...
                return true;
            case RIGHT:
                // Do something for RIGHT direction press
                ...
                return true;
            case UP:
                // Do something for UP direction press
                ...
                return true;
            ...
        }
    }

    // Check if this event is from a joystick movement and process accordingly.
    ...
}

ジョイスティックの動きを処理する

プレーヤーがゲーム コントローラでジョイスティックを動かすと、Android は ACTION_MOVE アクション コードとジョイスティックの軸の更新された位置を含む MotionEvent を報告します。ゲームは MotionEvent から提供されたデータを使用して、ジョイスティックの動きが発生したかどうかを判断できます。

ジョイスティックのモーション イベントにより、1 つのオブジェクト内の複数の動きのサンプルがバッチ処理される場合があります。MotionEvent オブジェクトには、各ジョイスティックの軸の現在の位置と、各軸の複数の過去の位置が含まれています。アクション コード ACTION_MOVE でモーション イベント(ジョイスティックの動きなど)をレポートする場合、Android では効率を高めるために軸の値を一括処理します。軸の過去の値は、現在の軸の値よりも古い個別の値と、以前のモーション イベントで報告された値よりも新しい一連の個別の値で構成されます。詳しくは、MotionEvent のリファレンスをご覧ください。

履歴情報を使用することで、ジョイスティックの入力に基づくゲーム オブジェクトの動きをより正確にレンダリングできます。現在と過去の値を取得するには、getAxisValue() または getHistoricalAxisValue() を呼び出します。getHistorySize() を呼び出すと、ジョイスティック イベントの履歴ポイントの数も確認できます。

次のスニペットは、onGenericMotionEvent() コールバックをオーバーライドしてジョイスティックの入力を処理する方法を示しています。まず、軸の過去の値を処理してから、現在の位置を処理する必要があります。

Kotlin

class GameView(...) : View(...) {

    override fun onGenericMotionEvent(event: MotionEvent): Boolean {

        // Check that the event came from a game controller
        return if (event.source and InputDevice.SOURCE_JOYSTICK == InputDevice.SOURCE_JOYSTICK
                && event.action == MotionEvent.ACTION_MOVE) {

            // Process the movements starting from the
            // earliest historical position in the batch
            (0 until event.historySize).forEach { i ->
                // Process the event at historical position i
                processJoystickInput(event, i)
            }

            // Process the current movement sample in the batch (position -1)
            processJoystickInput(event, -1)
            true
        } else {
            super.onGenericMotionEvent(event)
        }
    }
}

Java

public class GameView extends View {

    @Override
    public boolean onGenericMotionEvent(MotionEvent event) {

        // Check that the event came from a game controller
        if ((event.getSource() & InputDevice.SOURCE_JOYSTICK) ==
                InputDevice.SOURCE_JOYSTICK &&
                event.getAction() == MotionEvent.ACTION_MOVE) {

            // Process all historical movement samples in the batch
            final int historySize = event.getHistorySize();

            // Process the movements starting from the
            // earliest historical position in the batch
            for (int i = 0; i < historySize; i++) {
                // Process the event at historical position i
                processJoystickInput(event, i);
            }

            // Process the current movement sample in the batch (position -1)
            processJoystickInput(event, -1);
            return true;
        }
        return super.onGenericMotionEvent(event);
    }
}

ジョイスティック入力を使用する前に、ジョイスティックが中央に配置されているかどうかを判断し、それに応じて軸の動きを計算する必要があります。ジョイスティックは通常、平坦な領域、つまり、軸が中心とみなされる座標(0,0)付近の値の範囲を持ちます。Android によってレポートされる軸の値が平らな領域内にある場合、コントローラは静止している(つまり、両軸に沿って動かない)ように扱う必要があります。

以下のスニペットは、各軸に沿った動きを計算するヘルパー メソッドを示しています。このヘルパーは、後述の processJoystickInput() メソッド内で呼び出します。

Kotlin

private fun getCenteredAxis(
        event: MotionEvent,
        device: InputDevice,
        axis: Int,
        historyPos: Int
): Float {
    val range: InputDevice.MotionRange? = device.getMotionRange(axis, event.source)

    // A joystick at rest does not always report an absolute position of
    // (0,0). Use the getFlat() method to determine the range of values
    // bounding the joystick axis center.
    range?.apply {
        val value: Float = if (historyPos < 0) {
            event.getAxisValue(axis)
        } else {
            event.getHistoricalAxisValue(axis, historyPos)
        }

        // Ignore axis values that are within the 'flat' region of the
        // joystick axis center.
        if (Math.abs(value) > flat) {
            return value
        }
    }
    return 0f
}

Java

private static float getCenteredAxis(MotionEvent event,
        InputDevice device, int axis, int historyPos) {
    final InputDevice.MotionRange range =
            device.getMotionRange(axis, event.getSource());

    // A joystick at rest does not always report an absolute position of
    // (0,0). Use the getFlat() method to determine the range of values
    // bounding the joystick axis center.
    if (range != null) {
        final float flat = range.getFlat();
        final float value =
                historyPos < 0 ? event.getAxisValue(axis):
                event.getHistoricalAxisValue(axis, historyPos);

        // Ignore axis values that are within the 'flat' region of the
        // joystick axis center.
        if (Math.abs(value) > flat) {
            return value;
        }
    }
    return 0;
}

まとめると、ゲームでジョイスティックの動きを処理する方法は次のようになります。

Kotlin

private fun processJoystickInput(event: MotionEvent, historyPos: Int) {

    val inputDevice = event.device

    // Calculate the horizontal distance to move by
    // using the input value from one of these physical controls:
    // the left control stick, hat axis, or the right control stick.
    var x: Float = getCenteredAxis(event, inputDevice, MotionEvent.AXIS_X, historyPos)
    if (x == 0f) {
        x = getCenteredAxis(event, inputDevice, MotionEvent.AXIS_HAT_X, historyPos)
    }
    if (x == 0f) {
        x = getCenteredAxis(event, inputDevice, MotionEvent.AXIS_Z, historyPos)
    }

    // Calculate the vertical distance to move by
    // using the input value from one of these physical controls:
    // the left control stick, hat switch, or the right control stick.
    var y: Float = getCenteredAxis(event, inputDevice, MotionEvent.AXIS_Y, historyPos)
    if (y == 0f) {
        y = getCenteredAxis(event, inputDevice, MotionEvent.AXIS_HAT_Y, historyPos)
    }
    if (y == 0f) {
        y = getCenteredAxis(event, inputDevice, MotionEvent.AXIS_RZ, historyPos)
    }

    // Update the ship object based on the new x and y values
}

Java

private void processJoystickInput(MotionEvent event,
        int historyPos) {

    InputDevice inputDevice = event.getDevice();

    // Calculate the horizontal distance to move by
    // using the input value from one of these physical controls:
    // the left control stick, hat axis, or the right control stick.
    float x = getCenteredAxis(event, inputDevice,
            MotionEvent.AXIS_X, historyPos);
    if (x == 0) {
        x = getCenteredAxis(event, inputDevice,
                MotionEvent.AXIS_HAT_X, historyPos);
    }
    if (x == 0) {
        x = getCenteredAxis(event, inputDevice,
                MotionEvent.AXIS_Z, historyPos);
    }

    // Calculate the vertical distance to move by
    // using the input value from one of these physical controls:
    // the left control stick, hat switch, or the right control stick.
    float y = getCenteredAxis(event, inputDevice,
            MotionEvent.AXIS_Y, historyPos);
    if (y == 0) {
        y = getCenteredAxis(event, inputDevice,
                MotionEvent.AXIS_HAT_Y, historyPos);
    }
    if (y == 0) {
        y = getCenteredAxis(event, inputDevice,
                MotionEvent.AXIS_RZ, historyPos);
    }

    // Update the ship object based on the new x and y values
}

1 つのジョイスティックにはない高度な機能を備えたゲーム コントローラをサポートするには、次のベスト プラクティスに従ってください。

  • デュアル コントローラ スティックに対応する。多くのゲーム コントローラには、左右両方のジョイスティックがあります。左スティックの場合、Android は水平方向の動きを AXIS_X イベントとして、垂直方向の動きを AXIS_Y イベントとしてレポートします。右スティックの場合、Android は水平方向の動きを AXIS_Z イベントとして、垂直方向の動きを AXIS_RZ イベントとしてレポートします。コードで両方のコントローラ スティックを処理するようにしてください。
  • ショルダー トリガーの押下を処理します(ただし、代替の入力方法を提供します)。一部のコントローラには、左右のショルダー トリガーがあります。これらのトリガーが存在する場合、Android は左のトリガーの押下を AXIS_LTRIGGER イベントとして、右のトリガーの押下を AXIS_RTRIGGER イベントとしてレポートします。Android 4.3(API レベル 18)では、AXIS_LTRIGGER を生成するコントローラも AXIS_BRAKE 軸で同じ値をレポートします。AXIS_RTRIGGERAXIS_GAS についても同様です。Android は、0.0(リリース済み)から 1.0(完全に押下)までの正規化された値で、すべてのアナログ トリガーの押下を報告します。すべてのコントローラにトリガーがあるわけではないため、プレーヤーが他のボタンでそのようなゲーム アクションを実行できるようにすることを検討してください。