支持多个游戏控制器
使用集合让一切井井有条
根据您的偏好保存内容并对其进行分类。
虽然大多数游戏的设计宗旨是让每台 Android 设备支持一个用户,
我们还可以使用
同时连接了同一台 Android 设备。
本课介绍了在单个 API 中处理输入的一些基本技巧,
支持通过多个已连接的控制器畅玩设备多人游戏。这包括
保持玩家头像和每个控制器设备之间的映射,
正确处理控制器输入事件。
将玩家映射到控制器设备 ID
当游戏控制器连接到 Android 设备时,系统
会为其分配一个整数设备 ID。您可以获取已连接的
游戏控制器,如验证游戏控制器是否已连接中所示。InputDevice.getDeviceIds()
然后,您可以将每个
设备 ID,并分别处理每个玩家的游戏操作。
注意 :在搭载 Android 4.1 (API) 的设备上
级别 16)及更高版本,则可使用
getDescriptor()
,此方法会返回一个唯一的
输入设备的永久性字符串值。与设备 ID 不同
即使输入设备断开连接、重新连接或
重新配置。
以下代码段展示了如何使用 SparseArray
将玩家的头像与特定控制器相关联。在此示例中,
mShips
变量用于存储 Ship
对象的集合。一种新的
当用户连接新控制器时,系统会在游戏中创建玩家头像;
并在其关联的控制器被移除时移除。
onInputDeviceAdded()
和 onInputDeviceRemoved()
回调
方法属于
<ph type="x-smartling-placeholder"></ph>
跨 Android 版本支持控制器。通过实施这些
监听器回调时,您的游戏可以在发生
添加或移除了控制器此检测功能与 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 对应的操作,并根据
按钮。
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);
}
注意 :根据最佳做法,当用户的
您应暂停游戏并询问用户是否
想要重新连接。
本页面上的内容和代码示例受内容许可部分所述许可的限制。Java 和 OpenJDK 是 Oracle 和/或其关联公司的注册商标。
最后更新时间 (UTC):2025-07-26。
[null,null,["最后更新时间 (UTC):2025-07-26。"],[],[],null,["# Support multiple game controllers\n\nWhile most games are designed to support a single user per Android device,\nit's also possible to support multiple users with game controllers that are\nconnected simultaneously on the same Android device.\n\nThis lesson covers some basic techniques for handling input in your single\ndevice multiplayer game from multiple connected controllers. This includes\nmaintaining a mapping between player avatars and each controller device and\nprocessing controller input events appropriately.\n\nMap players to controller device IDs\n------------------------------------\n\nWhen a game controller is connected to an Android device, the system\nassigns it an integer device ID. You can obtain the device IDs for connected\ngame controllers by calling [InputDevice.getDeviceIds()](/reference/android/view/InputDevice#getDeviceIds()), as shown in [Verify a Game Controller is Connected](/develop/ui/views/touch-and-input/game-controllers/controller-input#input). You can then associate each\ndevice ID with a player in your game, and process game actions for each player separately.\n\n**Note:** On devices running Android 4.1 (API\nlevel 16) and higher, you can obtain an input device's descriptor using\n[getDescriptor()](/reference/android/view/InputDevice#getDescriptor()), which returns a unique\npersistent string value for the input device. Unlike a device ID, the descriptor\nvalue won't change even if the input device is disconnected, reconnected, or\nreconfigured.\n\nThe code snippet below shows how to use a [SparseArray](/reference/android/util/SparseArray)\nto associate a player's avatar with a specific controller. In this example, the\n`mShips` variable stores a collection of `Ship` objects. A new\nplayer avatar is created in-game when a new controller is attached by a user,\nand removed when its associated controller is removed.\n\nThe `onInputDeviceAdded()` and `onInputDeviceRemoved()` callback\nmethods are part of the abstraction layer introduced in\n[Supporting Controllers Across Android Versions](/training/game-controllers/compatibility#status_callbacks}). By implementing these\nlistener callbacks, your game can identify the game controller's device ID when a\ncontroller is added or removed. This detection is compatible with Android 2.3\n(API level 9) and higher. \n\n### Kotlin\n\n```kotlin\nprivate val ships = SparseArray\u003cShip\u003e()\n\noverride fun onInputDeviceAdded(deviceId: Int) {\n getShipForID(deviceId)\n}\n\noverride fun onInputDeviceRemoved(deviceId: Int) {\n removeShipForID(deviceId)\n}\n\nprivate fun getShipForID(shipID: Int): Ship {\n return ships.get(shipID) ?: Ship().also {\n ships.append(shipID, it)\n }\n}\n\nprivate fun removeShipForID(shipID: Int) {\n ships.remove(shipID)\n}\n```\n\n### Java\n\n```java\nprivate final SparseArray\u003cShip\u003e ships = new SparseArray\u003cShip\u003e();\n\n@Override\npublic void onInputDeviceAdded(int deviceId) {\n getShipForID(deviceId);\n}\n\n@Override\npublic void onInputDeviceRemoved(int deviceId) {\n removeShipForID(deviceId);\n}\n\nprivate Ship getShipForID(int shipID) {\n Ship currentShip = ships.get(shipID);\n if ( null == currentShip ) {\n currentShip = new Ship();\n ships.append(shipID, currentShip);\n }\n return currentShip;\n}\n\nprivate void removeShipForID(int shipID) {\n ships.remove(shipID);\n}\n```\n\nProcess multiple controller input\n---------------------------------\n\nYour game should execute the following loop to process\ninput from multiple controllers:\n\n1. Detect whether an input event occurred.\n2. Identify the input source and its device ID.\n3. Based on the action indicated by the input event key code or axis value, update the player avatar associated with that device ID.\n4. Render and update the user interface.\n\n[KeyEvent](/reference/android/view/KeyEvent) and [MotionEvent](/reference/android/view/MotionEvent) input\nevents have device IDs associated with them. Your game can take advantage of\nthis to determine which controller the input event came from, and update the\nplayer avatar associated with that controller.\n\nThe following code snippet shows how you might get a player avatar reference\ncorresponding to a game controller device ID, and update the game based on the\nuser's button press on that controller. \n\n### Kotlin\n\n```kotlin\noverride fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {\n if (event.source and InputDevice.SOURCE_GAMEPAD == InputDevice.SOURCE_GAMEPAD) {\n event.deviceId.takeIf { it != -1 }?.also { deviceId -\u003e\n val currentShip: Ship = getShipForID(deviceId)\n // Based on which key was pressed, update the player avatar\n // (e.g. set the ship headings or fire lasers)\n return true\n }\n }\n return super.onKeyDown(keyCode, event)\n}\n```\n\n### Java\n\n```java\n@Override\npublic boolean onKeyDown(int keyCode, KeyEvent event) {\n if ((event.getSource() & InputDevice.SOURCE_GAMEPAD)\n == InputDevice.SOURCE_GAMEPAD) {\n int deviceId = event.getDeviceId();\n if (deviceId != -1) {\n Ship currentShip = getShipForId(deviceId);\n // Based on which key was pressed, update the player avatar\n // (e.g. set the ship headings or fire lasers)\n ...\n return true;\n }\n }\n return super.onKeyDown(keyCode, event);\n}\n```\n\n**Note:**As a best practice, when a user's\ngame controller disconnects, you should pause the game and ask if the user\nwants to reconnect."]]