虽然大多数游戏的设计宗旨是支持每台 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); }
注意 :最佳实践是,当用户的游戏控制器断开连接时,您应暂停游戏并询问用户是否想要重新连接。