虽然大多数游戏都设计为每个 Android 设备支持单个用户,但也可以支持在同一 Android 设备上同时连接的多个用户使用游戏控制器。
本课程介绍了一些处理来自多个连接控制器的单设备多人游戏中输入的基本技术。这包括维护玩家化身和每个控制器设备之间的映射以及适当地处理控制器输入事件。
将玩家映射到控制器设备 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 的玩家化身引用,并根据用户在该控制器上的按键更新游戏。
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); }
注意:作为最佳实践,当用户的游戏控制器断开连接时,您应该暂停游戏并询问用户是否要重新连接。