Obsługa kilku kontrolerów do gier

Większość gier jest przeznaczona dla jednego użytkownika na urządzeniu z Androidem, ale można też obsługiwać wielu użytkowników za pomocą kontrolerów do gier, które są połączone jednocześnie na tym samym urządzeniu z Androidem.

W tej lekcji omówimy podstawowe techniki obsługi danych wejściowych w grze wieloosobowej na jednym urządzeniu z wielu podłączonych kontrolerów. Obejmuje to utrzymywanie mapowania między awatarami graczy a poszczególnymi urządzeniami sterującymi i odpowiednie przetwarzanie zdarzeń wejściowych kontrolera.

Mapowanie graczy na identyfikatory urządzeń sterujących

Gdy kontroler do gier jest podłączony do urządzenia z Androidem, system przypisuje mu identyfikator urządzenia w postaci liczby całkowitej. Identyfikatory podłączonych kontrolerów do gier możesz uzyskać, wywołując funkcję InputDevice.getDeviceIds(), jak pokazano w sekcji Sprawdzanie, czy kontroler do gier jest podłączony. Następnie możesz powiązać każdy identyfikator urządzenia z graczem w swojej grze i oddzielnie przetwarzać działania każdego gracza.

Fragment kodu pokazuje, jak za pomocą SparseArray powiązać awatar gracza z określonym kontrolerem. W tym przykładzie zmienna mShips zawiera kolekcję obiektów Ship. Gdy użytkownik podłączy nowy kontroler, w grze zostanie utworzony nowy awatar gracza. Zostanie on usunięty, gdy powiązany z nim kontroler zostanie odłączony.

Metody wywołania zwrotnego onInputDeviceAdded()onInputDeviceRemoved() są częścią warstwy abstrakcji wprowadzonej w artykule Obsługa kontrolerów na różnych wersjach Androida. Dzięki implementacji tych wywołań zwrotnych odbiornika gra może identyfikować identyfikator urządzenia kontrolera po dodaniu lub usunięciu kontrolera. Wykrywanie jest zgodne z Androidem 2.3 (API na poziomie 9) i nowszymi wersjami.

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

Przetwarzanie danych wejściowych z wielu kontrolerów

Aby przetwarzać dane wejściowe z wielu kontrolerów, gra powinna wykonywać tę pętlę:

  1. wykrywać, czy wystąpiło zdarzenie wejściowe;
  2. Określ źródło wejściowe i jego identyfikator urządzenia.
  3. Na podstawie działania wskazanego przez kod klucza zdarzenia wejściowego lub wartość osi zaktualizuj awatar gracza powiązany z tym identyfikatorem urządzenia.
  4. renderować i aktualizować interfejs użytkownika,

Zdarzenia wejściowe KeyEventMotionEvent są powiązane z identyfikatorami urządzeń. Twoja gra może to wykorzystać, aby określić, z którego kontrolera pochodzi zdarzenie wejściowe, i zaktualizować awatar gracza powiązany z tym kontrolerem.

Poniższy fragment kodu pokazuje, jak uzyskać odniesienie do awatara gracza odpowiadające identyfikatorowi urządzenia kontrolera do gier i zaktualizować grę na podstawie naciśnięcia przycisku przez użytkownika na tym kontrolerze.

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