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

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

Bài học này trình bày 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 bộ đ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ị điều khiển, cũng như xử lý các sự kiện đầu vào của bộ đ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 một tay điều khiển trò chơi được kết nối với một thiết bị chạy Android, hệ thống sẽ chỉ định cho tay điều khiển đó một mã nhận dạng thiết bị là số nguyên. Bạn có thể lấy mã nhận dạng thiết bị cho các tay điều khiển trò chơi đã kết nối bằng cách gọi InputDevice.getDeviceIds(), như minh hoạ trong phần 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ã nhận dạng thiết bị với một người chơi trong trò chơi của mình và xử lý riêng các hành động trong trò chơi cho từng người chơi.

Đoạn mã nà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 bộ điều khiển cụ thể. Trong ví dụ này, biến mShips lưu trữ một 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 gắn một bộ điều khiển mới và sẽ bị xoá khi bộ điều khiển được liên kết bị xoá.

Các phương thức gọi lại onInputDeviceAdded()onInputDeviceRemoved() là một phần của lớp trừu tượng được giới thiệu trong phần Hỗ trợ bộ điều khiển trên các phiên bản Android. Bằng cách triển khai các lệnh gọi lại của trình nghe này, trò chơi của bạn có thể xác định mã nhận dạng thiết bị của bộ điều khiển trò chơi khi một bộ điều khiển được thêm hoặc 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 phải thực thi vòng lặp sau để xử lý dữ liệu đầu vào từ nhiều bộ điều khiển:

  1. Phát hiện xem có sự kiện đầu vào 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 thao tác được chỉ ra bằng mã khoá sự kiện đầu vào hoặc giá trị trục, hãy cập nhật hình đại diện của người chơi được liên kết với mã nhận dạng thiết bị đó.
  4. Kết xuất và cập nhật giao diện người dùng.

Sự kiện KeyEvent và sự kiện đầu vào MotionEvent có mã nhận dạng thiết bị được liên kết với chúng. Trò chơi của bạn có thể tận dụng thông tin này để xác định 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 được 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ể nhận được một tham chiếu đến hình đại diện của người chơi tương ứng với mã nhận dạng thiết bị của tay điều khiển trò chơi và 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);
}