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

ที่ระดับระบบ 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() หรือจากคอลแบ็กเหตุการณ์สําคัญ เช่น onKeyDown()
MotionEvent
ออบเจ็กต์ที่อธิบายอินพุตจากการเคลื่อนไหวของจอยสติ๊กและทริกเกอร์ไหล่ เหตุการณ์การเคลื่อนไหวจะมาพร้อมกับรหัสการดำเนินการและชุดค่าแกน โค้ดการดำเนินการจะระบุการเปลี่ยนแปลงสถานะที่เกิดขึ้น เช่น การขยับจอยสติ๊ก ค่าแกนอธิบายตำแหน่งและพร็อพเพอร์ตี้การเคลื่อนไหวอื่นๆ สำหรับการควบคุมทางกายภาพที่เฉพาะเจาะจง เช่น AXIS_X หรือ AXIS_RTRIGGER คุณรับรหัสการดำเนินการได้ด้วยการเรียกใช้ getAction() และรับค่าแกนด้วยการเรียกใช้ getAxisValue()

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

ตรวจสอบว่าเชื่อมต่ออุปกรณ์ควบคุมเกมแล้ว

เมื่อรายงานเหตุการณ์อินพุต 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() ก่อน จากนั้นเรียกใช้ getAxis() ในออบเจ็กต์ InputDevice.MotionRange แต่ละรายการที่แสดงผลเพื่อรับรหัสแกน

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

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

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

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

รหัสคีย์ทั่วไปที่สร้างขึ้นโดยการกดปุ่มเกมแพด ได้แก่ BUTTON_A, BUTTON_B, BUTTON_SELECT และ BUTTON_START คอนโทรลเลอร์เกมบางรุ่นจะทริกเกอร์รหัสแป้น DPAD_CENTER ด้วยเมื่อกดแถบกลางของปุ่มบังคับทิศทาง เกมสามารถตรวจสอบคีย์โค้ดได้โดยเรียกใช้ getKeyCode() หรือจากคอลแบ็กเหตุการณ์สำคัญ เช่น 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_Y ที่มีช่วงตั้งแต่ -1.0 (ซ้าย) ถึง 1.0 (ขวา)

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

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

การดำเนินการในเกม รหัสแป้น 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)

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

คุณสามารถใช้คลาสตัวช่วยนี้ในเกมได้ทุกที่ที่ต้องการประมวลผลการป้อนข้อมูลด้วยปุ่มบังคับทิศทาง (เช่น ใน 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()

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

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 }

หากต้องการรองรับตัวควบคุมเกมที่มีฟีเจอร์ที่ซับซ้อนกว่าจอยสติ๊กเดียว ให้ทําตามแนวทางปฏิบัติแนะนําต่อไปนี้

  • จับที่จับของคอนโทรลเลอร์แบบ 2 แท่ง อุปกรณ์ควบคุมเกมหลายรุ่นมีจอยสติ๊กทั้งซ้ายและขวา สำหรับแท่งควบคุมด้านซ้าย Android จะรายงานการเคลื่อนไหวในแนวนอนเป็นเหตุการณ์ AXIS_X และการเคลื่อนไหวในแนวตั้งเป็นเหตุการณ์ AXIS_Y สําหรับแท่งควบคุมด้านขวา Android จะรายงานการเคลื่อนไหวในแนวนอนเป็นเหตุการณ์ AXIS_Z และการเคลื่อนไหวในแนวตั้งเป็นเหตุการณ์ AXIS_RZ อย่าลืมจัดการแท่งควบคุมทั้ง 2 แท่งในโค้ด
  • จัดการการกดไกปืนที่ไหล่ (และตรวจสอบว่าเกมของคุณทำงานร่วมกับเหตุการณ์ AXIS_ และ KEYCODE_BUTTON_ ได้) คอนโทรลเลอร์บางรุ่นมีปุ่มทริกเกอร์ที่ไหล่ซ้ายและขวา เมื่อทริกเกอร์เหล่านี้ปรากฏขึ้น ระบบจะส่งเหตุการณ์ AXIS_*TRIGGER หรือ KEYCODE_BUTTON_*2 หรือทั้ง 2 อย่าง สำหรับทริกเกอร์ซ้ายจะเป็น AXIS_LTRIGGER และ KEYCODE_BUTTON_L2 สําหรับทริกเกอร์ขวาจะเป็น AXIS_RTRIGGER และ KEYCODE_BUTTON_R2 เหตุการณ์แกนจะเกิดขึ้นหากทริกเกอร์ปล่อยช่วงของค่าระหว่าง 0 ถึง 1 เท่านั้น และตัวควบคุมบางรายการที่มีเอาต์พุตแบบแอนะล็อกจะแสดงเหตุการณ์ปุ่มนอกเหนือจากเหตุการณ์แกน เกมต้องรองรับทั้งเหตุการณ์ AXIS_ และ KEYCODE_BUTTON_ จึงจะใช้งานร่วมกับตัวควบคุมเกมทั่วไปได้อยู่ แต่ควรใช้เหตุการณ์ที่เหมาะกับเกมเพลย์มากที่สุดหากตัวควบคุมรายงานทั้ง 2 เหตุการณ์ ใน Android 4.3 (API ระดับ 18) ขึ้นไป ตัวควบคุมที่สร้าง AXIS_LTRIGGER จะรายงานค่าที่เหมือนกันสำหรับแกน AXIS_BRAKE ด้วย เช่นเดียวกับ AXIS_RTRIGGER และ AXIS_GAS Android จะรายงานการกดทริกเกอร์แบบอนาล็อกทั้งหมดด้วยค่าที่แปลงเป็นมาตรฐานตั้งแต่ 0.0 (ปล่อย) ถึง 1.0 (กดจนสุด)
  • ลักษณะการทำงานและการรองรับบางอย่างอาจแตกต่างกันไปในสภาพแวดล้อมการจําลอง แพลตฟอร์มจำลอง เช่น Google Play Games อาจทำงานแตกต่างกันเล็กน้อยโดยขึ้นอยู่กับความสามารถของระบบปฏิบัติการโฮสต์ เช่น ตัวควบคุมบางตัวที่ส่งทั้งเหตุการณ์ AXIS_ และ KEYCODE_BUTTON_ จะส่งเฉพาะเหตุการณ์ AXIS_ และอาจไม่รองรับตัวควบคุมบางตัวเลย