複数のゲーム コントローラをサポートする

ほとんどのゲームは Android デバイスごとに 1 人のユーザーをサポートするように設計されていますが、同じ Android デバイスで同時に接続されたゲーム コントローラを使用して複数のユーザーをサポートすることもできます。

このレッスンでは、単一デバイスのマルチプレーヤー ゲームで、接続された複数のコントローラからの入力を処理する基本的な手法について説明します。これには、プレーヤーのアバターと各コントローラ デバイス間のマッピングの維持や、コントローラ入力イベントを適切に処理することが含まれます。

プレーヤーをコントローラ デバイス ID にマッピングする

ゲーム コントローラが Android デバイスに接続されると、システムによって整数のデバイス ID が割り当てられます。接続されているゲーム コントローラのデバイス ID を取得するには、ゲーム コントローラが接続されていることを確認するで説明されているように、InputDevice.getDeviceIds() を呼び出します。これにより、各デバイス ID をゲーム内のプレーヤーに関連付けて、各プレーヤーのゲーム アクションを個別に処理できます。

注: Android 4.1(API レベル 16)以降を搭載したデバイスでは、入力デバイスに対して一意の永続的な文字列値を返す getDescriptor() を使用して、入力デバイスの記述子を取得できます。デバイス ID とは異なり、入力デバイスの接続解除、再接続、再構成が行われても、記述子の値は変更されません。

以下のコード スニペットは、SparseArray を使用してプレーヤーのアバターを特定のコントローラに関連付ける方法を示しています。この例では、mShips 変数に Ship オブジェクトのコレクションが格納されます。新しいプレーヤー アバターは、ユーザーが新しいコントローラを追加するときにゲーム内で作成され、関連付けられているコントローラが削除されると削除されます。

onInputDeviceAdded() および onInputDeviceRemoved() コールバック メソッドは、 Android バージョン間でのコントローラのサポートで導入された抽象化レイヤの一部です。これらのリスナー コールバックを実装することで、コントローラが追加または削除されたときにゲーム コントローラのデバイス ID を識別できます。この検出は Android 2.3(API レベル 9)以降と互換性があります。

Kotlin

private val ships = SparseArray<Ship>()

override fun onInputDeviceAdded(deviceId: Int) {
    getShipForID(deviceId)
}

override fun onInputDeviceRemoved(deviceId: Int) {
    removeShipForID(deviceId)
}

private fun getShipForID(shipID: Int): Ship {
    return ships.get(shipID) ?: Ship().also {
        ships.append(shipID, it)
    }
}

private fun removeShipForID(shipID: Int) {
    ships.remove(shipID)
}

Java

private final SparseArray<Ship> ships = new SparseArray<Ship>();

@Override
public void onInputDeviceAdded(int deviceId) {
    getShipForID(deviceId);
}

@Override
public void onInputDeviceRemoved(int deviceId) {
    removeShipForID(deviceId);
}

private Ship getShipForID(int shipID) {
    Ship currentShip = ships.get(shipID);
    if ( null == currentShip ) {
        currentShip = new Ship();
        ships.append(shipID, currentShip);
    }
    return currentShip;
}

private void removeShipForID(int shipID) {
    ships.remove(shipID);
}

複数のコントローラからの入力を処理する

複数のコントローラからの入力を処理するために、ゲームで次のループを実行する必要があります。

  1. 入力イベントが発生したかどうかを検出します。
  2. 入力元とそのデバイス ID を特定します。
  3. 入力イベント キーコードまたは軸値で指定されたアクションに基づいて、そのデバイス ID に関連付けられているプレーヤーのアバターを更新します。
  4. ユーザー インターフェースをレンダリングして更新します。

KeyEvent および MotionEvent 入力イベントにはデバイス ID が関連付けられています。ゲームではこの機能を利用して、入力イベントの送信元コントローラを特定し、そのコントローラに関連付けられたプレーヤー アバターを更新できます。

次のコード スニペットは、ゲーム コントローラのデバイス ID に対応するプレーヤーのアバターの参照を取得し、ユーザーがそのコントローラのボタンを押すたびにゲームを更新する方法を示しています。

Kotlin

override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
    if (event.source and InputDevice.SOURCE_GAMEPAD == InputDevice.SOURCE_GAMEPAD) {
        event.deviceId.takeIf { it != -1 }?.also { deviceId ->
            val currentShip: Ship = getShipForID(deviceId)
            // Based on which key was pressed, update the player avatar
            // (e.g. set the ship headings or fire lasers)
            return true
        }
    }
    return super.onKeyDown(keyCode, event)
}

Java

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
    if ((event.getSource() & InputDevice.SOURCE_GAMEPAD)
                == InputDevice.SOURCE_GAMEPAD) {
        int deviceId = event.getDeviceId();
        if (deviceId != -1) {
            Ship currentShip = getShipForId(deviceId);
            // Based on which key was pressed, update the player avatar
            // (e.g. set the ship headings or fire lasers)
            ...
            return true;
        }
    }
    return super.onKeyDown(keyCode, event);
}

注: ユーザーのゲーム コントローラが切断された場合は、ゲームを一時停止して、再接続を希望するかどうかを尋ねることをおすすめします。