Supporto di più controller di gioco

Sebbene la maggior parte dei giochi sia progettata per supportare un singolo utente per dispositivo Android, è anche possibile supportare più utenti con controller di gioco collegati contemporaneamente sullo stesso dispositivo Android.

Questa lezione illustra alcune tecniche di base per la gestione dell'input nel gioco multiplayer su dispositivo singolo da più controller collegati. Ciò include il mantenimento di una mappatura tra gli avatar dei giocatori e ciascun controller, nonché l'elaborazione appropriata degli eventi di input del controller.

Mappa i giocatori agli ID dispositivo del controller

Quando un controller di gioco è collegato a un dispositivo Android, il sistema gli assegna un ID dispositivo intero. Puoi ottenere gli ID dispositivo dei controller di gioco collegati chiamando InputDevice.getDeviceIds(), come mostrato nella sezione Verificare che un controller di gioco sia collegato. Puoi quindi associare ogni ID dispositivo a un giocatore nel gioco ed elaborare le azioni di gioco separatamente per ogni giocatore.

Nota: sui dispositivi con Android 4.1 (livello API 16) e versioni successive, puoi ottenere il descrittore di un dispositivo di input utilizzando getDescriptor(), che restituisce un valore stringa permanente univoco per il dispositivo di input. A differenza di un ID dispositivo, il valore descrittore non cambia anche se il dispositivo di input viene disconnesso, ricollegato o riconfigurato.

Lo snippet di codice riportato di seguito mostra come utilizzare SparseArray per associare l'avatar di un giocatore a un controller specifico. In questo esempio, la variabile mShips archivia una raccolta di oggetti Ship. Un nuovo avatar giocatore viene creato in-game quando un nuovo controller viene collegato da un utente e rimosso quando viene rimosso il controller associato.

I metodi di callback onInputDeviceAdded() e onInputDeviceRemoved() fanno parte del livello di astrazione introdotto in Supporto dei controller nelle versioni di Android. Se implementi questi callback di ascoltatori, il tuo gioco può identificare l'ID dispositivo del controller quando viene aggiunto o rimosso un controller. Questo rilevamento è compatibile con Android 2.3 (livello API 9) e versioni successive.

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);
}

Elabora l'input di più controller

Il gioco dovrebbe eseguire il seguente loop per elaborare l'input da più controller:

  1. Rileva se si è verificato un evento di input.
  2. Identifica l'origine di input e il relativo ID dispositivo.
  3. Aggiorna l'avatar del player associato all'ID dispositivo in base all'azione indicata dal codice chiave dell'evento di input o dal valore dell'asse.
  4. Esegui il rendering e aggiorna l'interfaccia utente.

Gli eventi di input KeyEvent e MotionEvent hanno ID dispositivo associati. Il gioco può approfittarne per determinare da quale controller proviene l'evento di input e aggiornare l'avatar del giocatore associato a quel controller.

Lo snippet di codice riportato di seguito mostra come ottenere il riferimento dell'avatar di un giocatore corrispondente all'ID dispositivo di un controller di gioco e aggiornare il gioco in base alla pressione del pulsante da parte dell'utente sul controller.

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);
}

Nota: come best practice, quando il controller di gioco di un utente si disconnette, devi mettere in pausa il gioco e chiedere se l'utente vuole riconnettersi.