While most games are designed to support a single user per Android-powered
device, it's also possible to support multiple users with game controllers that
are connected simultaneously on the same Android-powered device.

This lesson covers some basic techniques for handling input in your single
device multiplayer game from multiple connected controllers. This includes
maintaining a mapping between player avatars and each controller device and
processing controller input events appropriately.

## Map players to controller device IDs

When a game controller is connected to an Android-powered device, the system
assigns it an integer device ID. You can obtain the device IDs for connected
game controllers by calling [`InputDevice.getDeviceIds()`](https://developer.android.com/reference/android/view/InputDevice#getDeviceIds()), as shown in
[Verify a Game Controller is Connected](https://developer.android.com/games/sdk/game-controller/controller-input#input). You can then associate each device
ID with a player in your game, and process game actions for each player
separately.

> [!NOTE]
> **Note:** On devices running Android 4.1 (API level 16) and higher, you can obtain an input device descriptor using [`getDescriptor()`](https://developer.android.com/reference/android/view/InputDevice#getDescriptor()), which returns a unique persistent string value for the input device. Unlike a device ID, the descriptor value won't change even if the input device is disconnected, reconnected, or reconfigured.

The code snippet shows how to use a [`SparseArray`](https://developer.android.com/reference/android/util/SparseArray) to associate a
player's avatar with a specific controller. In this example, the `mShips`
variable stores a collection of `Ship` objects. A new player avatar is created
in-game when a new controller is attached by a user, and removed when its
associated controller is removed.

The `onInputDeviceAdded()` and `onInputDeviceRemoved()` callback methods
are part of the abstraction layer introduced in [Supporting Controllers Across
Android Versions](https://developer.android.com/training/game-controllers/compatibility#status_callbacks).
By implementing these listener callbacks, your game can identify the game
controller's device ID when a controller is added or removed. This detection
is compatible with Android 2.3 (API level 9) and higher.

### 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);
    }

## Process multiple controller input

Your game should execute the following loop to process input from multiple
controllers:

1. Detect whether an input event occurred.
2. Identify the input source and its device ID.
3. Based on the action indicated by the input event key code or axis value, update the player avatar associated with that device ID.
4. Render and update the user interface.

[`KeyEvent`](https://developer.android.com/reference/android/view/KeyEvent) and [`MotionEvent`](https://developer.android.com/reference/android/view/MotionEvent)
input events have device IDs
associated with them. Your game can take advantage of this to determine which
controller the input event came from, and update the player avatar associated
with that controller.

The following code snippet shows how you might get a player avatar reference
corresponding to a game controller device ID, and update the game based on the
user's button press on that controller.

### 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);
    }

> [!NOTE]
> **Note:** As a best practice, when a user's game controller disconnects, you should pause the game and ask if the user wants to reconnect.