Hỗ trợ nhiều tay điều khiển trò chơi

Mặc dù hầu hết các trò chơi được thiết kế để hỗ trợ một người dùng trên mỗi thiết bị Android, nhưng bạn cũng có thể hỗ trợ nhiều người dùng bằng tay điều khiển trò chơi được kết nối đồng thời trên cùng một thiết bị Android.

Bài học này đề cập đến một số kỹ thuật cơ bản để xử lý dữ liệu đầu vào trong trò chơi nhiều người chơi trên một thiết bị từ nhiều tay điều khiển được kết nối. Điều này bao gồm việc duy trì mối liên kết giữa hình đại diện của người chơi và từng thiết bị tay điều khiển, đồng thời xử lý các sự kiện nhập của tay điều khiển một cách thích hợp.

Liên kết người chơi với mã thiết bị của tay điều khiển

Khi tay điều khiển trò chơi được kết nối với một thiết bị Android, hệ thống sẽ chỉ định cho thiết bị đó một mã thiết bị bằng số nguyên. Bạn có thể lấy mã thiết bị cho tay điều khiển trò chơi đã kết nối bằng cách gọi InputDevice.getDeviceIds(), như minh hoạ trong bài viết Xác minh tay điều khiển trò chơi đã kết nối. Sau đó, bạn có thể liên kết từng mã thiết bị với người chơi trong trò chơi và xử lý các hành động trong trò chơi cho từng người chơi riêng biệt.

Lưu ý: Trên các thiết bị chạy Android 4.1 (API cấp 16) trở lên, bạn có thể lấy chỉ số mô tả của thiết bị đầu vào bằng cách sử dụng getDescriptor(). Mã này sẽ trả về một giá trị chuỗi cố định duy nhất cho thiết bị đầu vào. Không giống như mã thiết bị, giá trị mô tả sẽ không thay đổi ngay cả khi thiết bị đầu vào bị ngắt kết nối, kết nối lại hoặc định cấu hình lại.

Đoạn mã dưới đây cho biết cách sử dụng SparseArray để liên kết hình đại diện của người chơi với một tay điều khiển cụ thể. Trong ví dụ này, biến mShips lưu trữ tập hợp các đối tượng Ship. Hình đại diện mới của người chơi sẽ được tạo trong trò chơi khi người dùng đính kèm một tay điều khiển mới và sẽ xoá khi tay điều khiển liên kết bị xoá.

Phương thức gọi lại onInputDeviceAdded()onInputDeviceRemoved() là một phần của tầng trừu tượng được giới thiệu trong Bộ điều khiển hỗ trợ trên các phiên bản Android. Bằng cách triển khai các lệnh gọi lại trình nghe này, trò chơi của bạn có thể xác định mã thiết bị của tay điều khiển trò chơi khi một tay điều khiển được thêm vào hoặc bị xoá. Tính năng phát hiện này tương thích với Android 2.3 (API cấp 9) trở lên.

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

Xử lý dữ liệu đầu vào của nhiều bộ điều khiển

Trò chơi của bạn nên thực thi vòng lặp sau để xử lý dữ liệu đầu vào từ nhiều tay điều khiển:

  1. Phát hiện xem có sự kiện nhập nào xảy ra hay không.
  2. Xác định nguồn đầu vào và mã thiết bị của nguồn đó.
  3. Dựa trên hành động được biểu thị bằng mã khoá hoặc giá trị trục của sự kiện đầu vào, hãy cập nhật hình đại diện của người chơi liên kết với mã thiết bị đó.
  4. Kết xuất và cập nhật giao diện người dùng.

Các sự kiện đầu vào KeyEventMotionEvent được liên kết với mã thiết bị. Trò chơi của bạn có thể tận dụng khả năng này để xác định xem sự kiện đầu vào đến từ tay điều khiển nào và cập nhật hình đại diện của người chơi liên kết với tay điều khiển đó.

Đoạn mã sau đây cho biết cách bạn có thể lấy thông tin tham chiếu hình đại diện của người chơi tương ứng với mã thiết bị tay điều khiển trò chơi, đồng thời cập nhật trò chơi dựa trên thao tác nhấn nút của người dùng trên tay điều khiển đó.

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

Lưu ý: Phương pháp hay nhất là khi tay điều khiển trò chơi của người dùng ngắt kết nối, bạn nên tạm dừng trò chơi và hỏi xem người dùng có muốn kết nối lại hay không.