Поддержка нескольких игровых контроллеров

Хотя большинство игр рассчитаны на одного пользователя на устройстве под управлением Android, также возможно одновременное подключение нескольких пользователей с помощью игровых контроллеров к одному и тому же устройству Android.

В этом уроке рассматриваются некоторые основные методы обработки ввода в многопользовательской игре на одном устройстве с нескольких подключенных контроллеров. Это включает в себя поддержание соответствия между аватарами игроков и каждым контроллером, а также соответствующую обработку событий ввода с контроллера.

Сопоставьте игроков с идентификаторами контроллеров.

Когда игровой контроллер подключается к устройству на базе Android, система присваивает ему целочисленный идентификатор устройства. Вы можете получить идентификаторы подключенных игровых контроллеров, вызвав метод InputDevice.getDeviceIds() , как показано в разделе «Проверка подключения игрового контроллера» . Затем вы можете связать каждый идентификатор устройства с игроком в вашей игре и обрабатывать игровые действия для каждого игрока отдельно.

Приведённый фрагмент кода демонстрирует, как использовать SparseArray для связывания аватара игрока с конкретным контроллером. В этом примере переменная mShips хранит коллекцию объектов Ship . Новый аватар игрока создаётся в игре, когда пользователь подключает новый контроллер, и удаляется, когда удаляется связанный с ним контроллер.

Методы обратного вызова onInputDeviceAdded() и onInputDeviceRemoved() являются частью уровня абстракции, представленного в документе «Поддержка контроллеров в разных версиях Android» . Реализовав эти обработчики событий, ваша игра сможет определять идентификатор устройства игрового контроллера при его добавлении или удалении. Это определение совместимо с Android 2.3 (уровень API 9) и выше.

Котлин

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

Обработка входных данных от нескольких контроллеров.

В вашей игре должен выполняться следующий цикл для обработки ввода с нескольких контроллеров:

  1. Определите, произошло ли входное событие.
  2. Определите источник входного сигнала и его идентификатор устройства.
  3. В зависимости от действия, указанного кодом клавиши входного события или значением оси, обновите аватар игрока, связанный с этим идентификатором устройства.
  4. Отобразить и обновить пользовательский интерфейс.

События ввода KeyEvent и MotionEvent имеют связанные с ними идентификаторы устройств. Ваша игра может использовать это для определения того, с какого контроллера поступило событие ввода, и обновить аватар игрока, связанный с этим контроллером.

Приведенный ниже фрагмент кода показывает, как можно получить ссылку на аватар игрока, соответствующую идентификатору игрового контроллера, и обновлять игру в зависимости от нажатия пользователем кнопки на этом контроллере.

Котлин

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