Xử lý thao tác của bộ điều khiển

Ở cấp hệ thống, Android báo cáo mã sự kiện đầu vào từ tay điều khiển trò chơi làm mã khoá Android và giá trị trục. Trong trò chơi, bạn có thể nhận được những mã này và giá trị cũng như chuyển đổi chúng thành các hành động cụ thể trong trò chơi.

Khi người chơi kết nối thực tế hoặc ghép nối không dây tay điều khiển trò chơi với thiết bị chạy Android của họ, hệ thống sẽ tự động phát hiện bộ điều khiển làm thiết bị đầu vào và bắt đầu báo cáo các sự kiện đầu vào của thiết bị đó. Trò chơi của bạn có thể nhận các sự kiện đầu vào này bằng cách triển khai các phương thức gọi lại sau đây trong Activity hoặc View được lấy tiêu điểm (bạn nên triển khai các lệnh gọi lại cho Activity hoặc View, nhưng không phải cả hai):

Bạn nên nắm bắt các sự kiện từ đối tượng View cụ thể mà người dùng tương tác. Kiểm tra các đối tượng do lệnh gọi lại cung cấp sau đây để lấy thông tin về loại sự kiện đầu vào nhận được:

KeyEvent
Đối tượng mô tả phương hướng pad (D-pad) và sự kiện nút trên tay điều khiển trò chơi. Sự kiện chính đi kèm với mã phím cho biết nút cụ thể được kích hoạt, chẳng hạn như DPAD_DOWN hoặc BUTTON_A. Bạn có thể lấy mã phím bằng cách gọi getKeyCode() hoặc từ khoá các lệnh gọi lại sự kiện như onKeyDown()
MotionEvent
Đối tượng mô tả thao tác đầu vào bằng cần điều khiển và nút kích hoạt bằng vai chuyển động. Sự kiện chuyển động có một mã hành động và một tập hợp giá trị trục. Mã hành động chỉ định thay đổi trạng thái đã xảy ra chẳng hạn như cần điều khiển đang di chuyển. Các giá trị trục mô tả vị trí và các giá trị khác thuộc tính chuyển động cho một thiết bị điều khiển vật lý cụ thể, chẳng hạn như AXIS_X hoặc AXIS_RTRIGGER Bạn có thể lấy mã hành động bằng cách gọi getAction() và giá trị trục bằng cách đang gọi getAxisValue().

Bài học này tập trung vào cách bạn có thể xử lý thông tin đầu vào từ các loại phổ biến nhất các nút điều khiển vật lý (nút trên tay điều khiển trò chơi, bàn phím di chuyển và cần điều khiển) trong màn hình trò chơi bằng cách triển khai các thao tác nêu trên Đang xử lý và phương thức gọi lại View Các đối tượng KeyEventMotionEvent.

Xác minh rằng bạn đã kết nối tay điều khiển trò chơi

Khi báo cáo sự kiện đầu vào, Android không phân biệt giữa sự kiện đến từ một thiết bị không phải tay điều khiển trò chơi và sự kiện xảy ra trên tay điều khiển trò chơi. Ví dụ: thao tác trên màn hình cảm ứng sẽ tạo ra Sự kiện AXIS_X đại diện cho X nhưng cần điều khiển sẽ tạo ra Sự kiện AXIS_X đại diện cho vị trí X của cần điều khiển. Nếu trò chơi của bạn quan tâm đến việc xử lý đầu vào bằng tay điều khiển trò chơi, trước tiên, bạn nên kiểm tra rằng sự kiện đầu vào đến từ một loại nguồn có liên quan.

Để xác minh thiết bị đầu vào đã kết nối là tay điều khiển trò chơi, hãy gọi getSources() để lấy trường bit kết hợp của loại nguồn đầu vào được hỗ trợ trên thiết bị đó. Sau đó, bạn có thể kiểm tra xem các trường sau đây được đặt:

  • Loại nguồn của SOURCE_GAMEPAD cho biết thiết bị đầu vào có các nút trên tay điều khiển trò chơi (ví dụ: BUTTON_A). Lưu ý rằng nguồn này không chỉ rõ rằng bộ điều khiển trò chơi có các nút trên D-pad, mặc dù hầu hết các tay điều khiển trò chơi thường có tính năng điều khiển hướng.
  • Loại nguồn SOURCE_DPAD cho biết rằng thiết bị đầu vào có các nút D-pad (ví dụ: DPAD_UP).
  • Loại nguồn của SOURCE_JOYSTICK cho biết thiết bị đầu vào có cần điều khiển analog (ví dụ: cần điều khiển ghi lại các chuyển động dọc theo AXIS_XAXIS_Y).

Đoạn mã sau đây cho thấy một phương thức trợ giúp cho phép bạn kiểm tra xem thiết bị đầu vào được kết nối là tay điều khiển trò chơi. Nếu có, phương thức này sẽ truy xuất mã thiết bị cho tay điều khiển trò chơi. Sau đó, bạn có thể liên kết từng thiết bị Mã nhận dạng với người chơi trong trò chơi của bạn và xử lý hành động trong trò chơi cho mỗi người chơi được kết nối trình phát riêng biệt. Để tìm hiểu thêm về cách hỗ trợ nhiều 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, hãy xem Hỗ trợ nhiều tay điều khiển trò chơi.

Kotlin

fun getGameControllerIds(): List<Int> {
    val gameControllerDeviceIds = mutableListOf<Int>()
    val deviceIds = InputDevice.getDeviceIds()
    deviceIds.forEach { deviceId ->
        InputDevice.getDevice(deviceId).apply {

            // Verify that the device has gamepad buttons, control sticks, or both.
            if (sources and InputDevice.SOURCE_GAMEPAD == InputDevice.SOURCE_GAMEPAD
                    || sources and InputDevice.SOURCE_JOYSTICK == InputDevice.SOURCE_JOYSTICK) {
                // This device is a game controller. Store its device ID.
                gameControllerDeviceIds
                        .takeIf { !it.contains(deviceId) }
                        ?.add(deviceId)
            }
        }
    }
    return gameControllerDeviceIds
}

Java

public ArrayList<Integer> getGameControllerIds() {
    ArrayList<Integer> gameControllerDeviceIds = new ArrayList<Integer>();
    int[] deviceIds = InputDevice.getDeviceIds();
    for (int deviceId : deviceIds) {
        InputDevice dev = InputDevice.getDevice(deviceId);
        int sources = dev.getSources();

        // Verify that the device has gamepad buttons, control sticks, or both.
        if (((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD)
                || ((sources & InputDevice.SOURCE_JOYSTICK)
                == InputDevice.SOURCE_JOYSTICK)) {
            // This device is a game controller. Store its device ID.
            if (!gameControllerDeviceIds.contains(deviceId)) {
                gameControllerDeviceIds.add(deviceId);
            }
        }
    }
    return gameControllerDeviceIds;
}

Ngoài ra, bạn nên kiểm tra khả năng nhập dữ liệu riêng lẻ được hỗ trợ bởi tay điều khiển trò chơi đã kết nối. Điều này có thể hữu ích, chẳng hạn như nếu bạn muốn trò chơi của mình chỉ sử dụng dữ liệu đầu vào từ một tập hợp các nút điều khiển vật lý hiểu.

Để phát hiện xem một mã phím hoặc mã trục cụ thể có được hỗ trợ bởi tay điều khiển trò chơi, hãy sử dụng các kỹ thuật sau:

  • Trong Android 4.4 (API cấp 19) trở lên, bạn có thể xác định xem mã khoá có được hỗ trợ trên tay điều khiển trò chơi đã kết nối bằng cách gọi hasKeys(int...).
  • Trong Android 3.1 (API cấp 12) trở lên, bạn có thể tìm thấy tất cả các trục hiện có được hỗ trợ trên tay điều khiển trò chơi đã kết nối bằng cách gọi trước getMotionRanges(). Sau đó, trên mỗi Đã trả về đối tượng InputDevice.MotionRange, hãy gọi getAxis() để nhận mã trục.

Xử lý thao tác nhấn nút trên tay điều khiển trò chơi

Hình 1 thể hiện cách Android ánh xạ các mã khoá và giá trị trục đến dữ liệu thực trên hầu hết các tay điều khiển trò chơi.

Hình 1. Hồ sơ cho tay điều khiển trò chơi thông thường.

Các chú thích trong hình này đề cập đến:

Các mã phím phổ biến do các thao tác nhấn nút trên tay điều khiển trò chơi tạo ra bao gồm BUTTON_A, BUTTON_B, BUTTON_SELECT, và BUTTON_START. Trận đấu nào đó các tay điều khiển cũng kích hoạt mã phím DPAD_CENTER khi nhấn vào giữa thanh ngang của D-pad. Thông tin trò chơi có thể kiểm tra mã phím bằng cách gọi getKeyCode() hoặc qua các lệnh gọi lại sự kiện chính như onKeyDown(), và nếu sự kiện đó đại diện cho một sự kiện có liên quan đến trò chơi, hãy xử lý sự kiện đó dưới dạng hành động trong trò chơi. Bảng 1 liệt kê các hành động phổ biến nhất trong trò chơi, được đề xuất nút trên tay điều khiển trò chơi.

Bảng 1. Các thao tác được đề xuất trong trò chơi cho tay điều khiển trò chơi các nút.

Hành động trong trò chơi Mã phím nút
Bắt đầu trò chơi trong trình đơn chính hoặc tạm dừng/bỏ tạm dừng trong khi chơi BUTTON_START*
Hiện trình đơn BUTTON_SELECT*KEYCODE_MENU*
Giống như hành vi điều hướng Quay lại của Android được mô tả trong Thiết kế điều hướng của chúng tôi. KEYCODE_BACK
Quay lại mục trước trong một trình đơn BUTTON_B
Xác nhận lựa chọn hoặc thực hiện hành động chính trong trò chơi BUTTON_ADPAD_CENTER

* Trò chơi của bạn không nên phụ thuộc vào sự hiện diện của nút Bắt đầu, Chọn hoặc Trình đơn .

Mẹo: Cân nhắc việc cung cấp màn hình cấu hình trong trò chơi của mình để cho phép người dùng cá nhân hoá việc ánh xạ tay điều khiển trò chơi của riêng họ cho hành động trong trò chơi.

Đoạn mã sau đây hướng dẫn cách bạn có thể ghi đè onKeyDown() thành liên kết BUTTON_ADPAD_CENTER lần nhấn nút hành động trong trò chơi.

Kotlin

class GameView(...) : View(...) {
    ...

    override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
        var handled = false
        if (event.source and InputDevice.SOURCE_GAMEPAD == InputDevice.SOURCE_GAMEPAD) {
            if (event.repeatCount == 0) {
                when (keyCode) {
                    // Handle gamepad and D-pad button presses to navigate the ship
                    ...

                    else -> {
                        keyCode.takeIf { isFireKey(it) }?.run {
                            // Update the ship object to fire lasers
                            ...
                            handled = true
                        }
                    }
                }
            }
            if (handled) {
                return true
            }
        }
        return super.onKeyDown(keyCode, event)
    }

    // Here we treat Button_A and DPAD_CENTER as the primary action
    // keys for the game.
    private fun isFireKey(keyCode: Int): Boolean =
            keyCode == KeyEvent.KEYCODE_DPAD_CENTER || keyCode == KeyEvent.KEYCODE_BUTTON_A
}

Java

public class GameView extends View {
    ...

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        boolean handled = false;
        if ((event.getSource() & InputDevice.SOURCE_GAMEPAD)
                == InputDevice.SOURCE_GAMEPAD) {
            if (event.getRepeatCount() == 0) {
                switch (keyCode) {
                    // Handle gamepad and D-pad button presses to
                    // navigate the ship
                    ...

                    default:
                         if (isFireKey(keyCode)) {
                             // Update the ship object to fire lasers
                             ...
                             handled = true;
                         }
                     break;
                }
            }
            if (handled) {
                return true;
            }
        }
        return super.onKeyDown(keyCode, event);
    }

    private static boolean isFireKey(int keyCode) {
        // Here we treat Button_A and DPAD_CENTER as the primary action
        // keys for the game.
        return keyCode == KeyEvent.KEYCODE_DPAD_CENTER
                || keyCode == KeyEvent.KEYCODE_BUTTON_A;
    }
}

Lưu ý: Trên Android 4.2 (API cấp 17) trở xuống, thì hệ thống sẽ xử lý BUTTON_A làm Android Phím Quay lại theo mặc định. Nếu ứng dụng của bạn hỗ trợ các tính năng Android này phiên bản, hãy nhớ áp dụng BUTTON_A là trận đấu chính hành động. Để xác định SDK Android hiện tại phiên bản của thiết bị, hãy tham khảo Giá trị Build.VERSION.SDK_INT.

Xử lý phương thức nhập bằng bàn phím di chuyển

Bàn phím di chuyển 4 chiều (D-pad) là một chức năng điều khiển vật lý phổ biến trong nhiều trò chơi bộ điều khiển trò chơi. Android báo cáo số lần nhấn LÊN và XUỐNG D-pad là AXIS_HAT_Y sự kiện có phạm vi từ -1 (lên) đến 1 (xuống) và D-pad nhấn TRÁI hoặc PHẢI là AXIS_HAT_X sự kiện có phạm vi từ -1 (trái) đến 1,0 (phải).

Một số bộ điều khiển báo cáo các lần nhấn D-pad bằng một mã phím. Nếu trò chơi của bạn quan tâm đến thao tác nhấn D-pad, bạn nên xử lý các sự kiện trục mũ và D-pad các mã chính với các sự kiện đầu vào tương tự, như được đề xuất trong bảng 2.

Bảng 2. Các thao tác mặc định được đề xuất trong trò chơi cho phím trên D-pad mã và giá trị trục mũ.

Hành động trong trò chơi Mã phím trên D-pad Mã trục mũ
Di chuyển lên KEYCODE_DPAD_UP AXIS_HAT_Y (cho các giá trị từ 0 đến -1)
Di chuyển xuống KEYCODE_DPAD_DOWN AXIS_HAT_Y (cho các giá trị từ 0 đến 1.0)
Di chuyển sang trái KEYCODE_DPAD_LEFT AXIS_HAT_X (cho các giá trị từ 0 đến -1)
Di chuyển sang phải KEYCODE_DPAD_RIGHT AXIS_HAT_X (cho các giá trị từ 0 đến 1.0)

Đoạn mã sau đây cho thấy một lớp trợ giúp cho phép bạn kiểm tra mũ các giá trị mã trục và mã phím từ một sự kiện đầu vào để xác định hướng D-pad.

Kotlin

class Dpad {

    private var directionPressed = -1 // initialized to -1

    fun getDirectionPressed(event: InputEvent): Int {
        if (!isDpadDevice(event)) {
            return -1
        }

        // If the input event is a MotionEvent, check its hat axis values.
        (event as? MotionEvent)?.apply {

            // Use the hat axis value to find the D-pad direction
            val xaxis: Float = event.getAxisValue(MotionEvent.AXIS_HAT_X)
            val yaxis: Float = event.getAxisValue(MotionEvent.AXIS_HAT_Y)

            directionPressed = when {
                // Check if the AXIS_HAT_X value is -1 or 1, and set the D-pad
                // LEFT and RIGHT direction accordingly.
                xaxis.compareTo(-1.0f) == 0 -> Dpad.LEFT
                xaxis.compareTo(1.0f) == 0 -> Dpad.RIGHT
                // Check if the AXIS_HAT_Y value is -1 or 1, and set the D-pad
                // UP and DOWN direction accordingly.
                yaxis.compareTo(-1.0f) == 0 -> Dpad.UP
                yaxis.compareTo(1.0f) == 0 -> Dpad.DOWN
                else -> directionPressed
            }
        }
        // If the input event is a KeyEvent, check its key code.
        (event as? KeyEvent)?.apply {

            // Use the key code to find the D-pad direction.
            directionPressed = when(event.keyCode) {
                KeyEvent.KEYCODE_DPAD_LEFT -> Dpad.LEFT
                KeyEvent.KEYCODE_DPAD_RIGHT -> Dpad.RIGHT
                KeyEvent.KEYCODE_DPAD_UP -> Dpad.UP
                KeyEvent.KEYCODE_DPAD_DOWN -> Dpad.DOWN
                KeyEvent.KEYCODE_DPAD_CENTER ->  Dpad.CENTER
                else -> directionPressed
            }
        }
        return directionPressed
    }

    companion object {
        internal const val UP = 0
        internal const val LEFT = 1
        internal const val RIGHT = 2
        internal const val DOWN = 3
        internal const val CENTER = 4

        fun isDpadDevice(event: InputEvent): Boolean =
            // Check that input comes from a device with directional pads.
            event.source and InputDevice.SOURCE_DPAD != InputDevice.SOURCE_DPAD
    }
}

Java

public class Dpad {
    final static int UP       = 0;
    final static int LEFT     = 1;
    final static int RIGHT    = 2;
    final static int DOWN     = 3;
    final static int CENTER   = 4;

    int directionPressed = -1; // initialized to -1

    public int getDirectionPressed(InputEvent event) {
        if (!isDpadDevice(event)) {
           return -1;
        }

        // If the input event is a MotionEvent, check its hat axis values.
        if (event instanceof MotionEvent) {

            // Use the hat axis value to find the D-pad direction
            MotionEvent motionEvent = (MotionEvent) event;
            float xaxis = motionEvent.getAxisValue(MotionEvent.AXIS_HAT_X);
            float yaxis = motionEvent.getAxisValue(MotionEvent.AXIS_HAT_Y);

            // Check if the AXIS_HAT_X value is -1 or 1, and set the D-pad
            // LEFT and RIGHT direction accordingly.
            if (Float.compare(xaxis, -1.0f) == 0) {
                directionPressed =  Dpad.LEFT;
            } else if (Float.compare(xaxis, 1.0f) == 0) {
                directionPressed =  Dpad.RIGHT;
            }
            // Check if the AXIS_HAT_Y value is -1 or 1, and set the D-pad
            // UP and DOWN direction accordingly.
            else if (Float.compare(yaxis, -1.0f) == 0) {
                directionPressed =  Dpad.UP;
            } else if (Float.compare(yaxis, 1.0f) == 0) {
                directionPressed =  Dpad.DOWN;
            }
        }

        // If the input event is a KeyEvent, check its key code.
        else if (event instanceof KeyEvent) {

           // Use the key code to find the D-pad direction.
            KeyEvent keyEvent = (KeyEvent) event;
            if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_LEFT) {
                directionPressed = Dpad.LEFT;
            } else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_RIGHT) {
                directionPressed = Dpad.RIGHT;
            } else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_UP) {
                directionPressed = Dpad.UP;
            } else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_DOWN) {
                directionPressed = Dpad.DOWN;
            } else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_CENTER) {
                directionPressed = Dpad.CENTER;
            }
        }
        return directionPressed;
    }

    public static boolean isDpadDevice(InputEvent event) {
        // Check that input comes from a device with directional pads.
        if ((event.getSource() & InputDevice.SOURCE_DPAD)
             != InputDevice.SOURCE_DPAD) {
             return true;
         } else {
             return false;
         }
     }
}

Bạn có thể sử dụng lớp trợ giúp này trong trò chơi ở bất cứ nơi nào bạn muốn xử lý Phương thức nhập bằng D-pad (ví dụ: trong onGenericMotionEvent() hoặc onKeyDown() lệnh gọi lại).

Ví dụ:

Kotlin

private val dpad = Dpad()
...
override fun onGenericMotionEvent(event: MotionEvent): Boolean {
    if (Dpad.isDpadDevice(event)) {
        when (dpad.getDirectionPressed(event)) {
            Dpad.LEFT -> {
                // Do something for LEFT direction press
                ...
                return true
            }
            Dpad.RIGHT -> {
                // Do something for RIGHT direction press
                ...
                return true
            }
            Dpad.UP -> {
                // Do something for UP direction press
                ...
                return true
            }
            ...
        }
    }

    // Check if this event is from a joystick movement and process accordingly.
    ...
}

Java

Dpad dpad = new Dpad();
...
@Override
public boolean onGenericMotionEvent(MotionEvent event) {

    // Check if this event if from a D-pad and process accordingly.
    if (Dpad.isDpadDevice(event)) {

       int press = dpad.getDirectionPressed(event);
       switch (press) {
            case LEFT:
                // Do something for LEFT direction press
                ...
                return true;
            case RIGHT:
                // Do something for RIGHT direction press
                ...
                return true;
            case UP:
                // Do something for UP direction press
                ...
                return true;
            ...
        }
    }

    // Check if this event is from a joystick movement and process accordingly.
    ...
}

Xử lý chuyển động của cần điều khiển

Khi người chơi di chuyển cần điều khiển trên tay điều khiển trò chơi, Android sẽ báo cáo MotionEvent chứa phần tử Mã hành động ACTION_MOVE và mã hành động được cập nhật vị trí các trục của cần điều khiển. Trò chơi của bạn có thể sử dụng dữ liệu do MotionEvent để xác định xem cần điều khiển có di chuyển nó hay không quan tâm đã xảy ra.

Lưu ý rằng các sự kiện chuyển động của cần điều khiển có thể nhóm nhiều mẫu chuyển động lại với nhau trong một đối tượng duy nhất. Đối tượng MotionEvent chứa vị trí hiện tại của từng trục cần điều khiển cũng như nhiều trục vị trí cho mỗi trục. Khi báo cáo sự kiện chuyển động bằng mã thao tác ACTION_MOVE (chẳng hạn như di chuyển cần điều khiển), Android sẽ gộp nhóm giá trị trục cho hiệu quả. Giá trị lịch sử cho một trục bao gồm các biến một tập hợp các giá trị riêng biệt cũ hơn giá trị trục hiện tại và gần đây hơn các giá trị được báo cáo trong mọi sự kiện chuyển động trước đó. Xem Tham khảo MotionEvent để biết thông tin chi tiết.

Bạn có thể sử dụng thông tin trong quá khứ để kết xuất trò chơi một cách chính xác hơn chuyển động của đối tượng dựa trên đầu vào của cần điều khiển. Người nhận truy xuất giá trị hiện tại và giá trị trước đây, hãy gọi getAxisValue() hoặc getHistoricalAxisValue(). Bạn cũng có thể tìm thấy số lượng điểm trong sự kiện cần điều khiển bằng cách gọi getHistorySize().

Đoạn mã sau đây cho biết cách bạn có thể ghi đè Lệnh gọi lại onGenericMotionEvent() để xử lý thao tác đầu vào của cần điều khiển. Trước tiên, bạn nên xử lý các giá trị trước đây cho một trục, sau đó xử lý vị trí hiện tại của trục đó.

Kotlin

class GameView(...) : View(...) {

    override fun onGenericMotionEvent(event: MotionEvent): Boolean {

        // Check that the event came from a game controller
        return if (event.source and InputDevice.SOURCE_JOYSTICK == InputDevice.SOURCE_JOYSTICK
                && event.action == MotionEvent.ACTION_MOVE) {

            // Process the movements starting from the
            // earliest historical position in the batch
            (0 until event.historySize).forEach { i ->
                // Process the event at historical position i
                processJoystickInput(event, i)
            }

            // Process the current movement sample in the batch (position -1)
            processJoystickInput(event, -1)
            true
        } else {
            super.onGenericMotionEvent(event)
        }
    }
}

Java

public class GameView extends View {

    @Override
    public boolean onGenericMotionEvent(MotionEvent event) {

        // Check that the event came from a game controller
        if ((event.getSource() & InputDevice.SOURCE_JOYSTICK) ==
                InputDevice.SOURCE_JOYSTICK &&
                event.getAction() == MotionEvent.ACTION_MOVE) {

            // Process all historical movement samples in the batch
            final int historySize = event.getHistorySize();

            // Process the movements starting from the
            // earliest historical position in the batch
            for (int i = 0; i < historySize; i++) {
                // Process the event at historical position i
                processJoystickInput(event, i);
            }

            // Process the current movement sample in the batch (position -1)
            processJoystickInput(event, -1);
            return true;
        }
        return super.onGenericMotionEvent(event);
    }
}

Trước khi sử dụng phương thức nhập bằng cần điều khiển, bạn cần xác định xem cần điều khiển có đang căn giữa, sau đó tính chuyển động trục của nó sao cho phù hợp. Cần điều khiển thông thường có vùng phẳng, tức là một phạm vi các giá trị gần toạ độ (0,0) tại đó trục được coi là trọng tâm. Nếu giá trị trục được báo cáo bởi Android nằm trong khu vực phẳng, bạn nên coi bộ điều khiển là ở nghỉ (tức là không chuyển động dọc theo cả hai trục).

Đoạn mã dưới đây cho thấy một phương thức trợ giúp để tính toán chuyển động dọc từng trục. Bạn gọi trình trợ giúp này trong phương thức processJoystickInput() được mô tả kỹ hơn bên dưới.

Kotlin

private fun getCenteredAxis(
        event: MotionEvent,
        device: InputDevice,
        axis: Int,
        historyPos: Int
): Float {
    val range: InputDevice.MotionRange? = device.getMotionRange(axis, event.source)

    // A joystick at rest does not always report an absolute position of
    // (0,0). Use the getFlat() method to determine the range of values
    // bounding the joystick axis center.
    range?.apply {
        val value: Float = if (historyPos < 0) {
            event.getAxisValue(axis)
        } else {
            event.getHistoricalAxisValue(axis, historyPos)
        }

        // Ignore axis values that are within the 'flat' region of the
        // joystick axis center.
        if (Math.abs(value) > flat) {
            return value
        }
    }
    return 0f
}

Java

private static float getCenteredAxis(MotionEvent event,
        InputDevice device, int axis, int historyPos) {
    final InputDevice.MotionRange range =
            device.getMotionRange(axis, event.getSource());

    // A joystick at rest does not always report an absolute position of
    // (0,0). Use the getFlat() method to determine the range of values
    // bounding the joystick axis center.
    if (range != null) {
        final float flat = range.getFlat();
        final float value =
                historyPos < 0 ? event.getAxisValue(axis):
                event.getHistoricalAxisValue(axis, historyPos);

        // Ignore axis values that are within the 'flat' region of the
        // joystick axis center.
        if (Math.abs(value) > flat) {
            return value;
        }
    }
    return 0;
}

Kết hợp kiến thức đã học, sau đây là cách xử lý các chuyển động của cần điều khiển trò chơi của bạn:

Kotlin

private fun processJoystickInput(event: MotionEvent, historyPos: Int) {

    val inputDevice = event.device

    // Calculate the horizontal distance to move by
    // using the input value from one of these physical controls:
    // the left control stick, hat axis, or the right control stick.
    var x: Float = getCenteredAxis(event, inputDevice, MotionEvent.AXIS_X, historyPos)
    if (x == 0f) {
        x = getCenteredAxis(event, inputDevice, MotionEvent.AXIS_HAT_X, historyPos)
    }
    if (x == 0f) {
        x = getCenteredAxis(event, inputDevice, MotionEvent.AXIS_Z, historyPos)
    }

    // Calculate the vertical distance to move by
    // using the input value from one of these physical controls:
    // the left control stick, hat switch, or the right control stick.
    var y: Float = getCenteredAxis(event, inputDevice, MotionEvent.AXIS_Y, historyPos)
    if (y == 0f) {
        y = getCenteredAxis(event, inputDevice, MotionEvent.AXIS_HAT_Y, historyPos)
    }
    if (y == 0f) {
        y = getCenteredAxis(event, inputDevice, MotionEvent.AXIS_RZ, historyPos)
    }

    // Update the ship object based on the new x and y values
}

Java

private void processJoystickInput(MotionEvent event,
        int historyPos) {

    InputDevice inputDevice = event.getDevice();

    // Calculate the horizontal distance to move by
    // using the input value from one of these physical controls:
    // the left control stick, hat axis, or the right control stick.
    float x = getCenteredAxis(event, inputDevice,
            MotionEvent.AXIS_X, historyPos);
    if (x == 0) {
        x = getCenteredAxis(event, inputDevice,
                MotionEvent.AXIS_HAT_X, historyPos);
    }
    if (x == 0) {
        x = getCenteredAxis(event, inputDevice,
                MotionEvent.AXIS_Z, historyPos);
    }

    // Calculate the vertical distance to move by
    // using the input value from one of these physical controls:
    // the left control stick, hat switch, or the right control stick.
    float y = getCenteredAxis(event, inputDevice,
            MotionEvent.AXIS_Y, historyPos);
    if (y == 0) {
        y = getCenteredAxis(event, inputDevice,
                MotionEvent.AXIS_HAT_Y, historyPos);
    }
    if (y == 0) {
        y = getCenteredAxis(event, inputDevice,
                MotionEvent.AXIS_RZ, historyPos);
    }

    // Update the ship object based on the new x and y values
}

Để hỗ trợ tay điều khiển trò chơi tinh vi hơn ngoài một cần điều khiển, hãy làm theo các phương pháp hay nhất sau đây:

  • Xử lý các thanh điều khiển kép. Nhiều tay điều khiển trò chơi có cả cần điều khiển bên trái và bên phải. Đối với thanh bên trái, Android báo cáo chuyển động ngang dưới dạng sự kiện AXIS_X và chuyển động theo chiều dọc dưới dạng sự kiện AXIS_Y. Đối với thanh bên phải, Android báo cáo chuyển động ngang là AXIS_Z sự kiện và chuyển động theo chiều dọc dưới dạng AXIS_RZ sự kiện. Hãy nhớ xử lý cả hai thẻ điều khiển trong mã của bạn.
  • Xử lý thao tác nhấn nút kích hoạt vai (nhưng cung cấp phương thức nhập thay thế ). Một số bộ điều khiển có vai trái và vai phải điều kiện kích hoạt. Nếu có các điều kiện kích hoạt này, Android sẽ báo cáo một lượt nhấn điều kiện kích hoạt bên trái dưới dạng sự kiện AXIS_LTRIGGER và nhấn nút kích hoạt bên phải Sự kiện AXIS_RTRIGGER. Trên Android 4.3 (API cấp 18), bộ điều khiển tạo ra AXIS_LTRIGGER cũng báo cáo một giá trị giống hệt cho trục AXIS_BRAKE. Chiến lược phát hành đĩa đơn điều tương tự cũng đúng với AXIS_RTRIGGERAXIS_GAS Android báo cáo tất cả điều kiện kích hoạt analog có giá trị chuẩn hóa từ 0,0 (nhả) đến 1,0 (nhấn hoàn toàn). Không phải tất cả tay điều khiển đều có trình kích hoạt, vì vậy, hãy cân nhắc cho phép người chơi thực hiện các thao tác đó hành động trong trò chơi bằng các nút khác.