ほとんどのゲームは 1 台の Android デバイスに対して 1 人のユーザーをサポートするように設計されていますが、1 台の Android デバイスに複数のゲーム コントローラを同時に接続することによって複数のユーザーをサポートすることも可能です。
このレッスンでは、1 台のデバイス上でのマルチプレーヤー型ゲームにおいて、接続されている複数のコントローラからの入力を処理するための基本的な手法について説明します。また、プレーヤーのアバターと各コントローラ デバイス間のマッピングを管理する方法と、コントローラの入力イベントを適切に処理する方法についても説明します。
プレーヤーをコントローラ デバイス ID にマッピングする
ゲーム コントローラが Android デバイスに接続されると、ゲーム コントローラに整数のデバイス ID が割り当てられます。InputDevice.getDeviceIds()
を呼び出すことで、接続されているゲーム コントローラのデバイス ID を取得できます(ゲーム コントローラが接続されていることを確認するを参照)。これにより、各デバイス 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); }
複数のコントローラからの入力を処理する
複数のコントローラからの入力を処理するには、ゲームで次のループを実行する必要があります。
- 入力イベントが発生したかどうかを検出します。
- 入力元とそのデバイス ID を特定します。
- 入力イベントのキーコードまたは軸の値で示されたアクションに基づいて、そのデバイス ID に関連付けられているプレーヤーのアバターを更新します。
- ユーザー インターフェースをレンダリングして更新します。
KeyEvent
と MotionEvent
の入力イベントには、関連付けられているデバイス ID が含まれています。ゲームではこのデバイス 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); }
注: ユーザーのゲーム コントローラが取り外された場合、ゲームを一時停止して、再接続するかどうかを尋ねることをおすすめします。