Support multiple game controllers

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(), as shown in Verify a Game Controller is Connected. You can then associate each device ID with a player in your game, and process game actions for each player separately.

The code snippet shows how to use a 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. 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

privatevalships=SparseArray<Ship>()
overridefunonInputDeviceAdded(deviceId:Int){
getShipForID(deviceId)
}
overridefunonInputDeviceRemoved(deviceId:Int){
removeShipForID(deviceId)
}
privatefungetShipForID(shipID:Int):Ship{
returnships.get(shipID)?:Ship().also{
ships.append(shipID,it)
}
}
privatefunremoveShipForID(shipID:Int){
ships.remove(shipID)
}

Java

privatefinalSparseArray<Ship>ships=newSparseArray<Ship>();
@Override
publicvoidonInputDeviceAdded(intdeviceId){
getShipForID(deviceId);
}
@Override
publicvoidonInputDeviceRemoved(intdeviceId){
removeShipForID(deviceId);
}
privateShipgetShipForID(intshipID){
ShipcurrentShip=ships.get(shipID);
if(null==currentShip){
currentShip=newShip();
ships.append(shipID,currentShip);
}
returncurrentShip;
}
privatevoidremoveShipForID(intshipID){
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 and 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

overridefunonKeyDown(keyCode:Int,event:KeyEvent):Boolean{
if(event.sourceandInputDevice.SOURCE_GAMEPAD==InputDevice.SOURCE_GAMEPAD){
event.deviceId.takeIf{it!=-1}?.also{deviceId->
valcurrentShip:Ship=getShipForID(deviceId)
// Based on which key was pressed, update the player avatar
// (e.g. set the ship headings or fire lasers)
returntrue
}
}
returnsuper.onKeyDown(keyCode,event)
}

Java

@Override
publicbooleanonKeyDown(intkeyCode,KeyEventevent){
if((event.getSource() & InputDevice.SOURCE_GAMEPAD)
==InputDevice.SOURCE_GAMEPAD){
intdeviceId=event.getDeviceId();
if(deviceId!=-1){
ShipcurrentShip=getShipForId(deviceId);
// Based on which key was pressed, update the player avatar
// (e.g. set the ship headings or fire lasers)
...
returntrue;
}
}
returnsuper.onKeyDown(keyCode,event);
}

Content and code samples on this page are subject to the licenses described in the Content License. Java and OpenJDK are trademarks or registered trademarks of Oracle and/or its affiliates.

Last updated 2026年02月26日 UTC.