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