จัดการการทำงานของตัวควบคุม

ในระดับระบบ Android จะรายงานการป้อนโค้ดเหตุการณ์จากตัวควบคุมเกม เป็นโค้ดคีย์ Android และค่าแกน คุณรับรหัสเหล่านี้ในเกมได้ มูลค่า และแปลงเป็นการดำเนินการที่เจาะจงในเกม

เมื่อผู้เล่นเชื่อมต่อตัวเกมหรือจับคู่อุปกรณ์ควบคุมเกมแบบไร้สาย อุปกรณ์ที่ใช้ Android ของตน ระบบจะตรวจพบตัวควบคุมโดยอัตโนมัติ เป็นอุปกรณ์อินพุตและเริ่มรายงานเหตุการณ์การป้อนข้อมูลของอุปกรณ์นั้น เกมของคุณสามารถรับได้ เหตุการณ์อินพุตเหล่านี้โดยใช้เมธอด Callback ต่อไปนี้ใน Activity หรือ View ที่โฟกัส (คุณควร ใช้ Callback สำหรับ Activity หรือ View แต่ไม่ใช่ทั้ง 2 อย่าง):

  • จาก Activity
    • dispatchGenericMotionEvent(android.view. MotionEvent)

      เรียกใช้เพื่อประมวลผลเหตุการณ์การเคลื่อนไหวทั่วไป เช่น การเคลื่อนที่ของจอยสติ๊ก

    • dispatchKeyEvent(android.view.KeyEvent)

      เรียกใช้เพื่อประมวลผลเหตุการณ์สำคัญ เช่น การแถลงข่าวหรือข่าวประชาสัมพันธ์ ปุ่มเกมแพดหรือ D-pad

  • จาก View
    • onGenericMotionEvent(android.view.MotionEvent)

      เรียกใช้เพื่อประมวลผลเหตุการณ์การเคลื่อนไหวทั่วไป เช่น การเคลื่อนที่ของจอยสติ๊ก

    • onKeyDown(int, android.view.KeyEvent)

      เรียกใช้ให้ประมวลผลการกดปุ่มจริง เช่น เกมแพด หรือ ปุ่ม D-pad

    • onKeyUp(int, android.view.KeyEvent)

      เรียกใช้เพื่อประมวลผลการเปิดตัวคีย์จริง เช่น เกมแพด หรือ ปุ่ม D-pad

วิธีที่แนะนำคือบันทึกเหตุการณ์จาก ออบเจ็กต์ View เฉพาะที่ผู้ใช้โต้ตอบกับ ตรวจสอบออบเจ็กต์ต่อไปนี้ที่ได้จาก Callback เพื่อรับข้อมูล เกี่ยวกับประเภทของเหตุการณ์อินพุตที่ได้รับ

KeyEvent
วัตถุที่อธิบายทิศทาง เหตุการณ์เกี่ยวกับแพด (D-pad) และปุ่มเกมแพด เหตุการณ์สำคัญมาพร้อมกับ โค้ดหลักที่ระบุถึงปุ่มเฉพาะที่เรียกใช้ เช่น DPAD_DOWN หรือ BUTTON_A คุณสามารถรับ โดยโทรหา getKeyCode() หรือจากกุญแจ Callback ของเหตุการณ์ เช่น onKeyDown()
MotionEvent
วัตถุที่อธิบายอินพุตจากจอยสติ๊กและทริกเกอร์ตามไหล่ การเคลื่อนไหว เหตุการณ์การเคลื่อนไหวมาพร้อมกับรหัสการดำเนินการและชุดของ ค่าแกน รหัสการกระทำจะระบุการเปลี่ยนแปลงสถานะที่เกิดขึ้น เช่น จอยสติ๊กที่กำลังขยับ ค่าแกนจะอธิบายตำแหน่งและ คุณสมบัติการเคลื่อนไหวสำหรับการควบคุมการเคลื่อนไหวบางอย่าง เช่น AXIS_X หรือ AXIS_RTRIGGER คุณสามารถรับรหัสการดำเนินการ โดยเรียก getAction() และค่าแกนตาม กำลังโทรหา getAxisValue()

บทเรียนนี้จะเน้นถึงวิธีการจัดการกับการป้อนข้อมูลจากประเภทที่พบบ่อยที่สุด การควบคุมทางกายภาพ (ปุ่มเกมแพด แผ่นควบคุมทิศทาง และ จอยสติ๊ก) ในหน้าจอเกมโดยใช้สิ่งที่กล่าวไว้ข้างต้น วิธีการและการประมวลผลของ Callback View รายการ KeyEvent และ MotionEvent ออบเจ็กต์

ตรวจสอบว่าได้เชื่อมต่อเกมคอนโทรลเลอร์แล้ว

เมื่อรายงานเหตุการณ์อินพุต Android จะไม่แยก ระหว่างเหตุการณ์ที่มาจากอุปกรณ์ที่ไม่ใช่ตัวควบคุมเกมกับเหตุการณ์ที่เกิดขึ้น จากเกมคอนโทรลเลอร์ ตัวอย่างเช่น การทำงานบนหน้าจอสัมผัสจะสร้าง AXIS_X เหตุการณ์ที่แสดง X พิกัดของพื้นผิวสัมผัส แต่จอยสติ๊กจะสร้าง AXIS_X เหตุการณ์ที่แสดงตำแหน่ง X ของจอยสติ๊ก ถ้า เกมของคุณจะให้ความสำคัญกับการจัดการ อินพุตตัวควบคุมเกม ก่อนอื่นคุณควรตรวจสอบ ว่าเหตุการณ์อินพุตมาจากประเภทแหล่งที่มาที่เกี่ยวข้อง

ในการยืนยันว่าอุปกรณ์อินพุตที่เชื่อมต่อเป็นตัวควบคุมเกม โปรดโทร getSources() เพื่อรับฟิลด์บิตรวมของ ประเภทแหล่งที่มาของอินพุตที่อุปกรณ์นั้นรองรับ จากนั้นคุณสามารถทดสอบเพื่อดูว่า มีการตั้งค่าฟิลด์ต่อไปนี้:

  • ประเภทแหล่งที่มา SOURCE_GAMEPAD บ่งชี้ อุปกรณ์อินพุตมีปุ่มเกมแพด (เช่น BUTTON_A) โปรดทราบว่าแหล่งข้อมูลนี้ ประเภทไม่ได้ระบุไว้ชัดเจนว่าตัวควบคุมเกมมีปุ่ม D-pad หรือไม่ แม้ว่าเกมแพดส่วนใหญ่ จะมีการควบคุมทิศทาง
  • ประเภทแหล่งที่มา SOURCE_DPAD บ่งชี้ว่า อุปกรณ์อินพุตมีปุ่ม D-pad (ตัวอย่างเช่น DPAD_UP)
  • ประเภทแหล่งที่มา SOURCE_JOYSTICK บ่งบอกว่าอุปกรณ์อินพุตมีแท่งควบคุมแบบแอนะล็อก (ตัวอย่างเช่น จอยสติ๊กที่บันทึกการเคลื่อนที่ตลอด AXIS_X และ AXIS_Y)

ข้อมูลโค้ดต่อไปนี้แสดงวิธีตัวช่วยที่ให้คุณตรวจสอบว่า อุปกรณ์อินพุตที่เชื่อมต่อคือตัวควบคุมเกม ถ้าใช่ เมธอดจะเรียกคืน รหัสอุปกรณ์ของตัวควบคุมเกม จากนั้นคุณจะสามารถเชื่อมโยงอุปกรณ์แต่ละเครื่อง ใส่รหัสด้วยผู้เล่นในเกม และประมวลผลการดำเนินการในเกมสำหรับแต่ละเกมที่เชื่อมต่อ โปรแกรมเล่นวิดีโอแยกกัน หากต้องการดูข้อมูลเพิ่มเติมเกี่ยวกับการรองรับตัวควบคุมเกมหลายเครื่อง ที่เชื่อมต่อบนอุปกรณ์ Android เครื่องเดียวกันพร้อมๆ กัน โปรดดู รองรับตัวควบคุมเกมได้หลายรายการ

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

นอกจากนี้ คุณอาจต้องตรวจสอบความสามารถในการป้อนข้อมูลแต่ละรายการ รองรับโดยตัวควบคุมเกมที่เชื่อมต่อ ซึ่งอาจเป็นประโยชน์ เช่น หาก คุณต้องการให้เกมใช้เฉพาะอินพุตจากชุดการควบคุมทางกายภาพ เข้าใจ

หากต้องการตรวจสอบว่าเครื่องมือที่เชื่อมต่อรองรับรหัสคีย์หรือรหัสแกนที่เฉพาะเจาะจงหรือไม่ เกมคอนโทรลเลอร์ ใช้เทคนิคเหล่านี้

  • ใน Android 4.4 (API ระดับ 19) ขึ้นไป คุณระบุได้ว่ารหัสคีย์คือ รองรับบนอุปกรณ์ควบคุมเกมที่เชื่อมต่ออยู่โดยการเรียกใช้ hasKeys(int...)
  • ใน Android 3.1 (API ระดับ 12) ขึ้นไป คุณสามารถค้นหาแกนทั้งหมดที่มีอยู่ได้ รองรับอุปกรณ์ควบคุมเกมที่เชื่อมต่ออยู่โดยการเรียกใช้ครั้งแรก getMotionRanges() จากนั้น ในแต่ละ ส่งคืนออบเจ็กต์ InputDevice.MotionRange รายการ เรียก getAxis() เพื่อรับรหัสแกน

ประมวลผลการกดปุ่มเกมแพด

รูปที่ 1 แสดงวิธีที่ Android แมปโค้ดคีย์และค่าแกนกับฟิวเจอร์ส ในตัวควบคุมเกมส่วนใหญ่

รูปที่ 1 โปรไฟล์สำหรับตัวควบคุมเกมทั่วไป

ไฮไลต์ในรูปอ้างอิงถึงข้อมูลต่อไปนี้

โค้ดคีย์ทั่วไปที่สร้างขึ้นจากการกดปุ่มเกมแพดมีดังนี้ BUTTON_A, BUTTON_B, BUTTON_SELECT, และ BUTTON_START บางเกม ตัวควบคุมยังจะทริกเกอร์โค้ดคีย์ DPAD_CENTER ด้วยเมื่อกดตรงกลางของข้ามแถบ D-pad บัญชี เกมจะตรวจสอบรหัสคีย์ได้โดยการโทรหา getKeyCode() หรือจาก Callback ของเหตุการณ์สําคัญ เช่น onKeyDown(), และถ้าเหตุการณ์นั้นแสดงถึงเหตุการณ์ที่เกี่ยวข้องกับเกมของคุณ ให้ประมวลผลเป็น กับแอ็กชันในเกม ตารางที่ 1 แสดงรายการการดำเนินการที่แนะนำสำหรับเกมที่พบบ่อยที่สุด ปุ่มเกมแพด

ตาราง 1 การดำเนินการที่แนะนำสำหรับเกมแพด

แอ็กชันในเกม รหัสคีย์ของปุ่ม
เริ่มเกมในเมนูหลัก หรือหยุดเล่นชั่วคราว/ยกเลิกการหยุดชั่วคราวระหว่างเกม BUTTON_START*
แสดงเมนู BUTTON_SELECT* และ KEYCODE_MENU*
เช่นเดียวกับลักษณะการไปยังส่วนต่างๆ แบบกลับของ Android ที่อธิบายไว้ใน การออกแบบการนำทาง KEYCODE_BACK
กลับไปยังรายการก่อนหน้าในเมนู BUTTON_B
ยืนยันการเลือก หรือดำเนินการกับเกมหลัก BUTTON_A และ วันที่ DPAD_CENTER

* เกมของคุณไม่ควรพึ่งพาการแสดงข้อมูล "เริ่ม", "เลือก" หรือ "เมนู"

เคล็ดลับ: ลองใส่หน้าจอการกำหนดค่า ในเกมของคุณ เพื่ออนุญาตให้ผู้ใช้สามารถปรับเปลี่ยนการแมปเกมคอนโทรลเลอร์ของตนเอง การดำเนินการในเกม

ข้อมูลโค้ดต่อไปนี้แสดงวิธีลบล้าง onKeyDown() ถึง เชื่อมโยง BUTTON_A กับ การกดปุ่ม DPAD_CENTER กับแอ็กชันในเกม

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

หมายเหตุ: ใน Android 4.2 (API ระดับ 17) และต่ำกว่า ระบบจะพิจารณา BUTTON_A ในฐานะ Android ปุ่ม ย้อนกลับ โดยค่าเริ่มต้น ถ้าแอปของคุณสนับสนุน Android เหล่านี้ เวอร์ชันต่างๆ อย่าลืมพิจารณา BUTTON_A เป็นเกมหลัก การดำเนินการ เพื่อดู Android SDK ปัจจุบัน เวอร์ชันในอุปกรณ์ โปรดไปที่ Build.VERSION.SDK_INT

ประมวลผลการป้อนข้อมูลแป้นทิศทาง

ปุ่มบังคับทิศทาง 4 ทิศทาง (D-pad) เป็นการควบคุมทางกายภาพที่ใช้กันโดยทั่วไปในหลายๆ เกม Android รายงานการกด D-pad ขึ้นและลงเป็น AXIS_HAT_Y เหตุการณ์ที่มีช่วง จาก -1.0 (ขึ้น) ถึง 1.0 (ลง) และกด D-pad ซ้ายหรือขวาเป็น AXIS_HAT_X เหตุการณ์ที่มีช่วงตั้งแต่ -1.0 (ซ้าย) เป็น 1.0 (ขวา)

ตัวควบคุมบางรายจะรายงานการกด D-pad โดยใช้รหัสคีย์แทน หากเกมของคุณ ให้ความสำคัญกับการกด D-pad คุณควรดูเหตุการณ์ของแกนหมวกและ D-pad รหัสคีย์เป็นเหตุการณ์อินพุตเดียวกับที่แนะนำในตาราง 2

ตาราง 2 การดำเนินการที่แนะนำสำหรับเกมเริ่มต้นสำหรับปุ่ม D-pad รหัสและค่าแกนหมวก

แอ็กชันในเกม รหัสปุ่ม D-pad รหัสแกนหมวก
ย้ายขึ้น KEYCODE_DPAD_UP AXIS_HAT_Y (สำหรับค่า 0 ถึง -1.0)
ย้ายลง KEYCODE_DPAD_DOWN AXIS_HAT_Y (สำหรับค่า 0 ถึง 1.0)
ย้ายไปทางซ้าย KEYCODE_DPAD_LEFT AXIS_HAT_X (สำหรับค่า 0 ถึง -1.0)
ย้ายไปทางขวา KEYCODE_DPAD_RIGHT AXIS_HAT_X (สำหรับค่า 0 ถึง 1.0)

ข้อมูลโค้ดต่อไปนี้แสดงคลาสตัวช่วยที่ให้คุณตรวจหมวก ค่าแกนและคีย์โค้ดจากเหตุการณ์อินพุตเพื่อกำหนดทิศทาง 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;
         }
     }
}

คุณจะใช้คลาสผู้ช่วยนี้ในเกมได้ทุกที่ที่ต้องการประมวลผล อินพุต D-pad (ตัวอย่างเช่น ใน onGenericMotionEvent() หรือ วันที่ onKeyDown() Callback)

เช่น

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.
    ...
}

ประมวลผลการเคลื่อนที่ของจอยสติ๊ก

เมื่อผู้เล่นเคลื่อนจอยสติ๊กบนตัวควบคุมเกม Android จะรายงาน MotionEvent ที่มีฟังก์ชัน โค้ดดำเนินการ ACTION_MOVE และอัปเดตแล้ว ที่ตำแหน่งแกนของจอยสติ๊ก เกมของคุณสามารถใช้ข้อมูลที่ให้ไว้โดย MotionEvent เพื่อดูว่าจอยสติ๊กเคลื่อนที่หรือไม่ สิ่งที่เกิดขึ้น

โปรดทราบว่าเหตุการณ์การเคลื่อนไหวของจอยสติ๊กอาจรวมกลุ่มตัวอย่างการเคลื่อนไหวหลายรายการเข้าด้วยกัน ภายในออบเจ็กต์เดียว ออบเจ็กต์ MotionEvent ประกอบด้วย ตำแหน่งปัจจุบันของแกนจอยสติ๊กแต่ละแกน รวมถึงประวัติหลายครั้ง สำหรับแต่ละแกน เมื่อรายงานเหตุการณ์การเคลื่อนไหวที่มีรหัสการกระทำ ACTION_MOVE (เช่น การเคลื่อนที่ของจอยสติ๊ก) Android จะจัดกลุ่ม สำหรับประสิทธิภาพ ค่าประวัติสำหรับแกนประกอบด้วย ชุดของค่าที่ไม่ซ้ำกันซึ่งเก่ากว่าค่าแกนปัจจุบัน และใหม่กว่า มูลค่าที่รายงานในเหตุการณ์การเคลื่อนไหวก่อนหน้า โปรดดู ข้อมูลอ้างอิงของ MotionEvent สำหรับรายละเอียด

คุณสามารถใช้ข้อมูลประวัติเพื่อให้แสดงผลเกมได้แม่นยำยิ่งขึ้น การเคลื่อนที่ของวัตถุตามอินพุตจอยสติ๊ก ถึง ดึงค่าปัจจุบันและที่ผ่านมา getAxisValue() หรือ getHistoricalAxisValue() คุณสามารถดูจำนวนพร็อพเพอร์ตี้ ในเหตุการณ์จอยสติ๊กด้วยการเรียก getHistorySize()

ข้อมูลโค้ดต่อไปนี้แสดงวิธีลบล้าง onGenericMotionEvent() Callback เพื่อประมวลผลอินพุตจอยสติ๊ก ก่อนอื่น ประมวลผลค่าในอดีตสำหรับแกน จากนั้นประมวลผลตำแหน่งปัจจุบันของแกน

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

ก่อนใช้อินพุตจอยสติ๊ก คุณต้องตรวจสอบว่าจอยสติ๊ก ให้อยู่กึ่งกลาง แล้วคำนวณการเคลื่อนที่ตามแกนของแกนนั้นๆ โดยทั่วไปจอยสติ๊ก มีพื้นที่ราบ ซึ่งก็คือช่วงของค่าที่อยู่ใกล้กับพิกัด (0,0) ที่ถือว่าแกนอยู่กึ่งกลาง หากค่าแกนที่รายงานโดย Android อยู่ในพื้นที่ราบ คุณควรถือว่าตัวควบคุมอยู่ที่ ไม่เคลื่อนไหว (กล่าวคือ ไม่เคลื่อนไหวไปตามแกนทั้ง 2 แกน)

ข้อมูลโค้ดด้านล่างแสดงวิธีตัวช่วยที่คํานวณการเคลื่อนไหว แต่ละแกน คุณเรียกใช้ตัวช่วยนี้ในเมธอด processJoystickInput() ดังที่อธิบายไว้ด้านล่าง

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

นำทุกอย่างมารวมเข้าด้วยกัน คุณจะใช้การเคลื่อนที่ของจอยสติ๊กได้โดยทำดังนี้ เกมของคุณ

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
}

เพื่อรองรับเกมคอนโทรลเลอร์ที่ซับซ้อนยิ่งขึ้น คุณลักษณะที่นอกเหนือจากจอยสติ๊กเดียว ให้ทำตามแนวทางปฏิบัติที่ดีที่สุดต่อไปนี้:

  • ใช้ด้ามจับไม้คอนโทรลเลอร์แบบคู่ เกมคอนโทรลเลอร์จำนวนมาก ทั้งจอยสติ๊กซ้ายและขวา สำหรับคันโยกซ้าย Android รายงานการเคลื่อนไหวในแนวนอนเป็น AXIS_X เหตุการณ์ และการเคลื่อนไหวแนวตั้งเป็น AXIS_Y เหตุการณ์ สำหรับแท่งด้านขวา Android จะรายงานการเคลื่อนที่ในแนวนอนเป็น AXIS_Z เหตุการณ์และการเคลื่อนไหวแนวตั้งเป็น AXIS_RZ กิจกรรม อย่าลืมจัดการ ตัวควบคุมทั้ง 2 ตัวจะติดอยู่ในโค้ด
  • จัดการการกดทริกเกอร์ไหล่ (แต่มีอินพุตสำรอง) วิธีการ) ตัวควบคุมบางอย่างมีไหล่ซ้ายและขวา ทริกเกอร์ หากมีตัวทริกเกอร์เหล่านี้ Android จะรายงานการกดทริกเกอร์ด้านซ้าย เป็นเหตุการณ์ AXIS_LTRIGGER และ การกดทริกเกอร์ด้านขวาเป็น AXIS_RTRIGGER เหตุการณ์ บน Android 4.3 (API ระดับ 18) ซึ่งเป็นตัวควบคุมที่สร้าง AXIS_LTRIGGER ยังรายงาน ค่าที่เหมือนกันสำหรับแกน AXIS_BRAKE สำหรับ AXIS_RTRIGGER และ AXIS_GAS Android รายงานทริกเกอร์แบบแอนะล็อกทั้งหมด การกดด้วยค่ามาตรฐานจาก 0.0 (ปล่อยแล้ว) เป็น 1.0 (กดทั้งหมด) ไม่ใช่ ตัวควบคุมทั้งหมดมีทริกเกอร์ คุณจึงควรอนุญาตให้ผู้เล่นใช้ทริกเกอร์ การทำงานในเกมด้วยปุ่มอื่นๆ