Embora a maioria dos jogos seja projetada para oferecer suporte a um único usuário por dispositivo Android, também é possível oferecer suporte a vários usuários com controles de jogos conectados simultaneamente no mesmo dispositivo Android.
Esta lição aborda algumas técnicas básicas para processar entradas no seu jogo multiplayer de dispositivo único usando vários controles conectados. Isso inclui manter um mapeamento entre os avatares de jogador e cada dispositivo de controle e processar eventos de entrada do controle corretamente.
Mapear jogadores de acordo com códigos de dispositivo dos controles
Quando um controle de jogo está conectado a um dispositivo Android, o sistema
atribui a ele um ID de dispositivo em número inteiro. Você pode conseguir os IDs de dispositivos para controles
de jogos conectados chamando InputDevice.getDeviceIds()
, conforme mostrado em Verificar se um controle de jogo está conectado. Em seguida, você pode associar cada
ID de dispositivo a um jogador e processar as ações do jogo para cada jogador separadamente.
Observação : em dispositivos com o Android 4.1 (API
de nível 16) e versões mais recentes, é possível conseguir o descritor de um dispositivo de entrada usando
getDescriptor()
, que retorna um valor de string
persistente exclusivo para o dispositivo de entrada. Diferente de um ID de dispositivo, o valor
do descritor não muda, mesmo que o dispositivo de entrada seja desconectado, reconectado ou
reconfigurado.
O snippet de código abaixo mostra como usar um SparseArray
para associar o avatar de um jogador a um controle específico. Neste exemplo, a
variável mShips
armazena uma coleção de objetos Ship
. Um novo
avatar de jogador é criado no jogo quando um novo controle é conectado por um usuário
e removido quando o controle associado é removido.
Os métodos de callback onInputDeviceAdded()
e onInputDeviceRemoved()
fazem parte da camada de abstração apresentada em
Suporte a controles em diferentes versões do Android. Ao implementar esses
callbacks de listener, seu jogo pode identificar o ID do dispositivo do controle quando um
controle é adicionado ou removido. Essa detecção é compatível com o Android 2.3
(API de nível 9) e versões mais recentes.
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); }
Processar a entrada de vários controles
Seu jogo precisa executar o seguinte loop para processar entradas de vários controles:
- Detectar se ocorreu um evento de entrada.
- Identificar a origem da entrada e o código do dispositivo correspondente.
- Com base na ação indicada pelo código da tecla do evento de entrada ou pelo valor do eixo, atualiza o avatar do jogador associado a esse ID do dispositivo.
- Renderizar e atualizar a interface do usuário.
Os eventos de entrada KeyEvent
e MotionEvent
têm IDs de dispositivo associados a eles. Seu jogo pode aproveitar
isso para determinar de qual controle o evento de entrada veio e atualizar o
avatar do jogador associado a esse controle.
O snippet de código a seguir mostra como você pode ter uma referência de avatar de jogador correspondente a um ID de dispositivo de controle de jogo e atualizar o jogo com base no botão do usuário pressionado nesse controle.
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); }
Observação : como prática recomendada, quando o controle de jogo de um usuário se desconecta, pause o jogo e pergunte se o usuário quer se reconectar.