คอนโทรลเลอร์มีการทำงาน 2 ประเภท ได้แก่
KeyEventใช้กับปุ่มที่มีสถานะไบนารีเป็น "เปิด" และ "ปิด"MotionEventใช้กับแกนที่แสดงผลช่วงของค่า เช่น -1 ถึง 1 สำหรับ อนาล็อกสติ๊ก หรือ 0 ถึง 1 สำหรับทริกเกอร์แบบอนาล็อก
คุณอ่านข้อมูลเหล่านี้ได้จาก View ที่มี focus
onGenericMotionEventจะเพิ่มขึ้นสำหรับMotionEventonKeyDownและonKeyUpจะยกขึ้นสำหรับKeyEventเมื่อกดและปล่อยปุ่ม
Kotlin
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
if (event.isFromSource(SOURCE_GAMEPAD)
&& event.repeatCount == 0
) {
Log.d("GameView", "Gamepad key pressed: $keyCode")
return true
}
return super.onKeyDown(keyCode, event)
}
override fun onGenericMotionEvent(event: MotionEvent): Boolean {
if (event.isFromSource(SOURCE_JOYSTICK)) {
Log.d("GameView", "Gamepad event: $event")
return true
}
return super.onGenericMotionEvent(event)
}
Java
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (event.isFromSource(SOURCE_GAMEPAD)
&& event.getRepeatCount() == 0
) {
Log.d("GameView", "Gamepad key pressed: " + keyCode);
return true;
}
return super.onKeyDown(keyCode, event);
}
@Override
public boolean onGenericMotionEvent(MotionEvent event) {
if (event.isFromSource(SOURCE_JOYSTICK)) {
Log.d("GameView", "Gamepad event: " + event);
return true;
}
return super.onGenericMotionEvent(event);
}
หากจำเป็น คุณสามารถอ่านเหตุการณ์จาก Activity ได้โดยตรงแทน
dispatchGenericMotionEventจะเพิ่มขึ้นสำหรับMotionEventdispatchKeyEventระดมทุนสำหรับKeyEvent
ตรวจสอบว่าได้เชื่อมต่ออุปกรณ์ควบคุมเกมแล้ว
เมื่อรายงานเหตุการณ์อินพุต Android จะใช้รหัสคีย์หรือรหัสแกนเดียวกันซ้ำสำหรับ
อุปกรณ์อินพุตประเภทต่างๆ เช่น การกระทำบนหน้าจอสัมผัสจะสร้างเหตุการณ์
AXIS_X ที่แสดงถึงพิกัด X
ของพื้นผิวสัมผัส แต่เกมแพดจะสร้างเหตุการณ์
AXIS_X ที่แสดงถึงตำแหน่ง X
ของก้านซ้าย ซึ่งหมายความว่าคุณต้องตรวจสอบประเภทแหล่งที่มาเพื่อ
ตีความเหตุการณ์อินพุตอย่างถูกต้อง
หากต้องการยืนยันว่า InputDevice ที่เชื่อมต่อเป็นอุปกรณ์ควบคุมเกม ให้ใช้ฟังก์ชัน
supportsSource(int)
- ประเภทแหล่งที่มาของ
SOURCE_GAMEPADแสดงว่าอุปกรณ์อินพุตมีปุ่มควบคุม (เช่นKEYCODE_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 (supportsSource(SOURCE_GAMEPAD)
|| supportsSource(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);
if (dev == null) {
continue;
}
// Verify that the device has gamepad buttons, control sticks, or both.
if (dev.supportsSource(SOURCE_GAMEPAD) || dev.supportsSource(SOURCE_JOYSTICK)) {
// This device is a game controller. Store its device ID.
if (!gameControllerDeviceIds.contains(deviceId)) {
gameControllerDeviceIds.add(deviceId);
}
}
}
return gameControllerDeviceIds;
}
อินพุตของตัวควบคุมกระบวนการ
ส่วนนี้อธิบายประเภทของเกมคอนโทรลเลอร์ที่รองรับใน Android
นักพัฒนาแอป C++ ควรใช้ไลบรารีตัวควบคุมเกม โดยจะรวมตัวควบคุมทั้งหมดเข้ากับชุดฟีเจอร์ย่อยที่ใช้กันมากที่สุด และมอบอินเทอร์เฟซที่สอดคล้องกันระหว่างตัวควบคุมเหล่านั้น ซึ่งรวมถึงความสามารถในการตรวจหาเลย์เอาต์ปุ่ม
รูปนี้แสดงให้เห็นว่านักพัฒนาเกม Android คาดหวังให้คอนโทรลเลอร์ทั่วไป มีลักษณะอย่างไรใน Android
ตารางนี้แสดงชื่อและประเภทเหตุการณ์มาตรฐานสำหรับเกมคอนโทรลเลอร์ ดูรายการเหตุการณ์ทั้งหมดได้ที่ตัวแปรทั่วไป ระบบ
ส่งเหตุการณ์ MotionEvent ผ่านเหตุการณ์ onGenericMotionEvent และเหตุการณ์ KeyEvent
ผ่าน onKeyDown และ onKeyUp
| อินพุตของเกมแพด | KeyEvent | MotionEvent |
|---|---|---|
| 1. D-Pad |
AXIS_HAT_X(อินพุตแนวนอน) AXIS_HAT_Y(อินพุตแนวตั้ง) |
|
| 2. ปุ่มอนาล็อกซ้าย |
KEYCODE_BUTTON_THUMBL(เมื่อกดเข้าไป) |
AXIS_X(การเคลื่อนไหวในแนวนอน) AXIS_Y(การเคลื่อนไหวในแนวตั้ง) |
| 3. ปุ่มอนาล็อกขวา |
KEYCODE_BUTTON_THUMBR(เมื่อกดเข้าไป) |
AXIS_Z(การเคลื่อนไหวในแนวนอน) AXIS_RZ(การเคลื่อนไหวในแนวตั้ง) |
| 4. ปุ่ม X | KEYCODE_BUTTON_X |
|
| 5. ปุ่ม A | KEYCODE_BUTTON_A |
|
| 6. ปุ่ม Y | KEYCODE_BUTTON_Y |
|
| 7. ปุ่ม B | KEYCODE_BUTTON_B |
|
| 8. ปุ่มกันชนขวา |
KEYCODE_BUTTON_R1 |
|
| 9. ทริกเกอร์ขวา |
AXIS_RTRIGGER |
|
| 10. ทริกเกอร์ซ้าย | AXIS_LTRIGGER |
|
| 11. ปุ่มกันชนซ้าย | KEYCODE_BUTTON_L1 |
|
| 12. เริ่ม | KEYCODE_BUTTON_START |
|
| 13. เลือก | KEYCODE_BUTTON_SELECT |
จัดการการกดปุ่ม
เนื่องจาก Android จะรายงานการกดปุ่มของตัวควบคุมเหมือนกับการกดปุ่มแป้นพิมพ์ คุณจึงต้องทำดังนี้
- ตรวจสอบว่ากิจกรรมมาจาก
SOURCE_GAMEPAD - ตรวจสอบว่าคุณได้รับปุ่มเพียงครั้งเดียวด้วย
KeyEvent.getRepeatCount()Android จะส่งเหตุการณ์แป้นซ้ำเช่นเดียวกับที่คุณกดแป้นพิมพ์ค้างไว้ - ระบุว่ามีการจัดการเหตุการณ์โดยการส่งคืน
true ส่งเหตุการณ์ที่ไม่ได้จัดการไปยัง
superเพื่อยืนยันว่าเลเยอร์ความเข้ากันได้ต่างๆ ของ Android ทํางานได้อย่างเหมาะสมKotlin
class GameView : View { // ... override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean { event.apply { var handled = false // make sure we're handling gamepad events if (isFromSource(SOURCE_GAMEPAD)) { // avoid processing the keycode repeatedly if (repeatCount == 0) { when (keyCode) { // handle the "A" button KEYCODE_BUTTON_A -> { handled = true } } // ... } } if (handled) { return true } } return super.onKeyDown(keyCode, event) } }Java
public class GameView extends View { // ... @Override public boolean onKeyDown(int keyCode, KeyEvent event) { boolean handled = false; // make sure we're handling gamepad events if (event.isFromSource(SOURCE_GAMEPAD)) { // avoid processing the keycode repeatedly if (event.getRepeatCount() == 0) { switch (keyCode) { case KEYCODE_BUTTON_A: // handle the "A" button handled = true; break; // ... } } // mark this event as handled if (handled) { return true; } } // Always do this instead of "return false" // it allows Android's input compatibility layers to work return super.onKeyDown(keyCode, event); } }
ประมวลผลอินพุตปุ่มบังคับทิศทาง
ปุ่มบังคับทิศทาง 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) |
ข้อมูลโค้ดต่อไปนี้แสดงคลาส Helper ที่ช่วยให้คุณตรวจสอบค่าแกนหมวก และรหัสคีย์จากเหตุการณ์อินพุตเพื่อกำหนดทิศทางของ 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.
return event.isFromSource(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.
return event.isFromSource(InputDevice.SOURCE_DPAD);
}
}
คุณใช้คลาสตัวช่วยนี้ในเกมได้ทุกที่ที่ต้องการประมวลผลอินพุต D-pad (เช่น ใน
onGenericMotionEvent()
หรือ
onKeyDown()
การเรียกกลับ)
เช่น
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
หากต้องการแสดงการเคลื่อนไหวของออบเจ็กต์ในเกมอย่างถูกต้องตามอินพุตจอยสติ๊ก คุณสามารถ
ใช้ข้อมูลประวัติที่จัดทำโดยออบเจ็กต์ MotionEvent
คุณสามารถดึงค่าปัจจุบันและค่าในอดีตได้โดยใช้วิธีการต่อไปนี้
getAxisValue()getHistoricalAxisValue()getHistorySize()(เพื่อค้นหาจำนวนจุดประวัติใน เหตุการณ์จอยสติ๊ก)
ข้อมูลโค้ดต่อไปนี้แสดงวิธีลบล้าง
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
}
หากต้องการรองรับตัวควบคุมเกมที่มีฟีเจอร์ซับซ้อนกว่าจอยสติ๊กเดียว ให้ทำตามแนวทางปฏิบัติแนะนำต่อไปนี้
- ควบคุมแท่งควบคุมคู่ ตัวควบคุมเกมหลายตัวมีทั้งจอยสติ๊กซ้าย
และขวา สำหรับก้านซ้าย 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_GASAndroid จะรายงานการกดทริกเกอร์แบบอนาล็อกทั้งหมดด้วยค่าที่ปรับให้เป็นมาตรฐานตั้งแต่ 0.0 (ปล่อย) ถึง 1.0 (กดจนสุด) - ลักษณะการทำงานและการสนับสนุนที่เฉพาะเจาะจงอาจแตกต่างกันในสภาพแวดล้อมที่จำลอง
แพลตฟอร์มที่จำลอง เช่น Google Play Games
อาจมีลักษณะการทำงานแตกต่างกันเล็กน้อยตามความสามารถของระบบปฏิบัติการโฮสต์
ตัวอย่างเช่น คอนโทรลเลอร์บางตัวที่ปล่อยทั้งเหตุการณ์
AXIS_และKEYCODE_BUTTON_จะปล่อยเฉพาะเหตุการณ์AXIS_และอาจไม่มีการรองรับคอนโทรลเลอร์บางตัวเลย
รูปแบบที่พบบ่อย
เนื่องจาก Android รองรับตัวควบคุมหลากหลายประเภท คุณอาจไม่แน่ใจ วิธีสร้างและทดสอบเพื่อยืนยันว่าเกมของคุณทำงานได้โดยไม่มีข้อบกพร่องในกลุ่มผู้เล่น เราพบว่าแม้จะมีความหลากหลายอย่างเห็นได้ชัด แต่ผู้ผลิตคอนโทรลเลอร์ทั่วโลกมักจะยึดมั่นในคอนโทรลเลอร์ 3 รูปแบบที่แตกต่างกัน บางรุ่น มีสวิตช์ฮาร์ดแวร์สำหรับสลับระหว่างโหมดเหล่านี้ 2 โหมดขึ้นไป
ซึ่งหมายความว่าคุณสามารถทดสอบด้วยอุปกรณ์ควบคุมเพียง 3 เครื่องในทีมพัฒนา และมั่นใจได้ว่าเกมของคุณเล่นได้โดยไม่ต้องใช้รายการที่อนุญาตและรายการที่ไม่อนุญาต
ประเภทรีโมตที่พบบ่อย
โดยปกติแล้วรูปแบบของคอนโทรลเลอร์มักจะเลียนแบบเลย์เอาต์ของเกมคอนโซลยอดนิยม ซึ่งเป็นทั้งความสวยงามในป้ายกำกับปุ่มและเลย์เอาต์ รวมถึงการทำงาน ตามเหตุการณ์ที่เกิดขึ้น คอนโทรลเลอร์ที่มีสวิตช์ฮาร์ดแวร์สำหรับสลับระหว่างคอนโซลประเภทต่างๆ จะเปลี่ยนเหตุการณ์ที่ส่งและมักจะเปลี่ยนแม้กระทั่งเลย์เอาต์ปุ่มเชิงตรรกะ
เมื่อทดสอบ เราขอแนะนำให้คุณตรวจสอบว่าเกมทำงานร่วมกับคอนโทรลเลอร์ 1 ตัว ในแต่ละหมวดหมู่ได้ คุณอาจเลือกทดสอบกับคอนโทรลเลอร์ของบุคคลที่หนึ่ง หรือผู้ผลิตบุคคลที่สามยอดนิยม โดยทั่วไปแล้ว เราจะแมปคอนโทรลเลอร์ที่ได้รับความนิยมมากที่สุดกับคำจำกัดความด้านบนอย่างเต็มความสามารถ
| ประเภทรีโมต | ความแตกต่างทางพฤติกรรม | การติดฉลากรูปแบบต่างๆ |
|---|---|---|
| คอนโทรลเลอร์สไตล์ Xbox
โดยปกติแล้ว ตัวควบคุมเหล่านี้จะสร้างขึ้นสำหรับแพลตฟอร์ม Microsoft Xbox และ Windows* |
ตัวควบคุมเหล่านี้ตรงกับชุดฟีเจอร์ที่ระบุไว้ในประมวลผลอินพุตของตัวควบคุม | ปุ่ม L2/R2 บนตัวควบคุมเหล่านี้มีป้ายกำกับเป็น LT/RT |
| ตัวควบคุมสไตล์ Switch
โดยปกติแล้ว คอนโทรลเลอร์เหล่านี้ออกแบบมาสำหรับคอนโซลตระกูล Nintendo Switch* |
ตัวควบคุมเหล่านี้จะส่ง KeyEvent
KEYCODE_BUTTON_R2
KEYCODE_BUTTON_L2
MotionEvent |
ปุ่ม L2/R2 บนตัวควบคุมเหล่านี้มีป้ายกำกับเป็น ZL/ZR
นอกจากนี้ คอนโทรลเลอร์เหล่านี้ยังสลับปุ่ม A กับ B และปุ่ม
X กับ Y ด้วย ดังนั้น |
| จอยบังคับสไตล์ PlayStation
โดยปกติแล้ว ตัวควบคุมเหล่านี้ออกแบบมาสำหรับคอนโซลตระกูล Sony PlayStation* |
โดยตัวควบคุมเหล่านี้จะส่ง MotionEvent
เช่น Xbox Style Controllers แต่ก็ส่ง KeyEvent
เช่น Switch Style Controllers เมื่อกดจนสุดด้วย |
โดยคอนโทรลเลอร์เหล่านี้จะใช้ชุดสัญลักษณ์ที่แตกต่างกันสำหรับปุ่มบนใบหน้า |
* Microsoft, Xbox และ Windows เป็นเครื่องหมายการค้าจดทะเบียนของ Microsoft Nintendo Switch เป็นเครื่องหมายการค้าจดทะเบียนของ Nintendo of America Inc. PlayStation เป็นเครื่องหมายการค้าจดทะเบียนของ Sony Interactive Entertainment Inc.
ปุ่มทริกเกอร์ที่แยกความแตกต่าง
คอนโทรลเลอร์บางรุ่นจะส่ง AXIS_LTRIGGER และ AXIS_RTRIGGER บางรุ่นจะส่ง KEYCODE_BUTTON_L2 และ KEYCODE_BUTTON_R2 และบางรุ่นจะส่งเหตุการณ์ทั้งหมดนี้
ตามความสามารถของฮาร์ดแวร์ เพิ่มความเข้ากันได้สูงสุดด้วยการรองรับเหตุการณ์ทั้งหมด
เหล่านี้
ตัวควบคุมทั้งหมดที่ส่ง AXIS_LTRIGGER จะส่ง AXIS_BRAKE ด้วยเช่นกัน และในทำนองเดียวกัน AXIS_RTRIGGER และ AXIS_GAS จะช่วยเพิ่มความเข้ากันได้สูงสุดระหว่างพวงมาลัยแข่งกับตัวควบคุมเกมทั่วไป โดยทั่วไปแล้วการดำเนินการนี้จะไม่ทำให้เกิดปัญหา แต่โปรด
ระมัดระวังสำหรับฟีเจอร์ต่างๆ เช่น หน้าจอการแมปแป้นพิมพ์ใหม่
| ทริกเกอร์ | MotionEvent |
KeyEvent |
|---|---|---|
| ทริกเกอร์ซ้าย | AXIS_LTRIGGERAXIS_BRAKE
|
KEYCODE_BUTTON_L2
|
| ทริกเกอร์ขวา | AXIS_RTRIGGERAXIS_GAS
|
KEYCODE_BUTTON_R2
|
คุณควรตรวจสอบว่าเกมรองรับทั้ง KeyEvent และ
MotionEvent เพื่อรักษาความเข้ากันได้กับคอนโทรลเลอร์ให้ได้มากที่สุด
และตรวจสอบว่าระบบได้นำเหตุการณ์ที่ซ้ำกันออกแล้ว
คอนโทรลเลอร์ที่รองรับ
เมื่อทดสอบ เราขอแนะนำให้คุณตรวจสอบว่าเกมทำงานร่วมกับคอนโทรลเลอร์ 1 ตัว ในแต่ละหมวดหมู่ได้
- สไตล์ Xbox
- สไตล์ Nintendo Switch
- สไตล์ PlayStation
คุณสามารถทดสอบด้วยคอนโทรลเลอร์ของบุคคลที่หนึ่งหรือผู้ผลิตบุคคลที่สามยอดนิยม และโดยทั่วไปเราจะจับคู่คอนโทรลเลอร์ยอดนิยมกับคำจำกัดความให้ใกล้เคียง มากที่สุด