Bộ điều khiển có hai loại thao tác:
KeyEventđược dùng cho mọi nút có trạng thái nhị phân là "bật" và "tắt"MotionEventđược dùng cho mọi trục trả về một dải giá trị. Chẳng hạn như -1 đến 1 cho cần điều khiển tương tự hoặc 0 đến 1 cho cò tương tự.
Bạn có thể đọc các dữ liệu đầu vào này từ View có focus.
onGenericMotionEventđược tăng lên cho mọiMotionEvent.onKeyDownvàonKeyUpđược tăng lên choKeyEventkhi các nút được nhấn và nhả.
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);
}
Nếu cần, bạn có thể đọc trực tiếp các sự kiện từ Activity.
dispatchGenericMotionEventđược đưa ra cho mọiMotionEventdispatchKeyEvent. được tăng cho mọiKeyEvent.
Xác minh rằng bộ điều khiển trò chơi đã được kết nối
Khi báo cáo các sự kiện đầu vào, Android sẽ dùng lại cùng một khoá hoặc mã nhận dạng trục cho các loại thiết bị đầu vào khác nhau. Ví dụ: thao tác trên màn hình cảm ứng sẽ tạo ra sự kiện AXIS_X biểu thị toạ độ X của bề mặt cảm ứng, nhưng tay cầm chơi game sẽ tạo ra sự kiện AXIS_X biểu thị vị trí X của cần điều khiển bên trái. Điều này có nghĩa là bạn phải kiểm tra loại nguồn để diễn giải đúng các sự kiện đầu vào.
Để xác minh rằng InputDevice đã kết nối là tay điều khiển trò chơi, hãy sử dụng hàm supportsSource(int):
- Loại nguồn
SOURCE_GAMEPADcho biết thiết bị đầu vào có các nút trên tay điều khiển (ví dụ:KEYCODE_BUTTON_A). Xin lưu ý rằng loại nguồn này không cho biết chính xác liệu tay điều khiển trò chơi có các nút trên D-pad hay không, mặc dù hầu hết các tay điều khiển thường có các chế độ điều khiển hướng. - Loại nguồn
SOURCE_DPADcho biết thiết bị đầu vào có các nút D-pad (ví dụ:DPAD_UP). - Loại nguồn
SOURCE_JOYSTICKcho biết thiết bị đầu vào có cần điều khiển tương tự (ví dụ: cần điều khiển ghi lại các chuyển động dọc theoAXIS_XvàAXIS_Y).
Đoạn mã sau đây cho thấy một phương thức trợ giúp cho phép bạn kiểm tra xem các thiết bị đầu vào đã kết nối có phải là bộ điều khiển trò chơi hay không. Nếu có, phương thức này sẽ truy xuất mã nhận dạng thiết bị cho tay điều khiển trò chơi. Sau đó, bạn có thể liên kết từng mã nhận dạng thiết bị với một người chơi trong trò chơi của mình và xử lý riêng các thao tác trong trò chơi cho từng người chơi đã kết nối. Để tìm hiểu thêm về cách hỗ trợ nhiều tay điều khiển trò chơi được kết nối đồng thời trên cùng một thiết bị chạy Android, hãy xem phần Hỗ trợ nhiều tay điều khiển trò chơi.
Kotlin
fun getGameControllerIds(): List<Int> {
val gameControllerDeviceIds = mutableListOf<Int>()
val deviceIds = InputDevice.getDeviceIds()
deviceIds.forEach { deviceId ->
InputDevice.getDevice(deviceId)?.apply {
// Verify that the device has gamepad buttons, control sticks, or both.
if (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;
}
Xử lý dữ liệu đầu vào của bộ điều khiển
Phần này mô tả các loại tay điều khiển trò chơi được hỗ trợ trên Android.
Nhà phát triển C++ nên sử dụng Thư viện Tay điều khiển trò chơi. Nó hợp nhất tất cả các bộ điều khiển thành nhóm con phổ biến nhất của các tính năng và cung cấp một giao diện nhất quán giữa các bộ điều khiển, bao gồm cả khả năng phát hiện bố cục nút.
Hình này cho thấy hình dạng của một bộ điều khiển thông thường mà nhà phát triển trò chơi Android có thể thấy trên Android.
Bảng này liệt kê các tên và loại sự kiện chuẩn cho tay điều khiển trò chơi. Để xem danh sách đầy đủ các sự kiện, hãy xem phần Các biến thể phổ biến. Hệ thống gửi các sự kiện MotionEvent thông qua các sự kiện onGenericMotionEvent và KeyEvent thông qua onKeyDown và onKeyUp.
| Thông tin đầu vào của bộ điều khiển | KeyEvent | MotionEvent |
|---|---|---|
| 1. D-Pad |
AXIS_HAT_X(đầu vào ngang) AXIS_HAT_Y(đầu vào dọc) |
|
| 2. Cần điều khiển tương tự bên trái |
KEYCODE_BUTTON_THUMBL(khi nhấn vào) |
AXIS_X(chuyển động ngang) AXIS_Y(chuyển động dọc) |
| 3. Cần điều khiển tương tự bên phải |
KEYCODE_BUTTON_THUMBR(khi nhấn vào) |
AXIS_Z(chuyển động ngang) AXIS_RZ(chuyển động dọc) |
| 4. Nút X | KEYCODE_BUTTON_X |
|
| 5. Nút A | KEYCODE_BUTTON_A |
|
| 6. Nút Y | KEYCODE_BUTTON_Y |
|
| 7. Nút B | KEYCODE_BUTTON_B |
|
| 8. Nút Right Bumper |
KEYCODE_BUTTON_R1 |
|
| 9. Nút cò bên phải |
AXIS_RTRIGGER |
|
| 10. Nút kích hoạt bên trái | AXIS_LTRIGGER |
|
| 11. Nút L1 | KEYCODE_BUTTON_L1 |
|
| 12. Bắt đầu | KEYCODE_BUTTON_START |
|
| 13. Chọn | KEYCODE_BUTTON_SELECT |
Xử lý thao tác nhấn nút
Vì Android báo cáo các lần nhấn nút trên bộ điều khiển giống như các lần nhấn nút trên bàn phím, nên bạn cần:
- Xác thực rằng sự kiện đến từ một
SOURCE_GAMEPAD. - Đảm bảo bạn chỉ nhận được nút một lần bằng
KeyEvent.getRepeatCount(), Android sẽ gửi các sự kiện lặp lại của phím giống như khi bạn nhấn và giữ một phím trên bàn phím. - Cho biết rằng một sự kiện được xử lý bằng cách trả về
true. Truyền các sự kiện chưa được xử lý đến
superđể xác minh rằng các lớp tương thích khác nhau của Android hoạt động phù hợp.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); } }
Xử lý dữ liệu đầu vào của bàn phím di chuyển
Bàn phím di chuyển 4 hướng (D-pad) là một nút điều khiển vật lý phổ biến trong nhiều tay điều khiển trò chơi. Android báo cáo các lần nhấn D-pad LÊN và XUỐNG dưới dạng sự kiện AXIS_HAT_Y, trong đó -1.0 cho biết hướng lên và 1.0 cho biết hướng xuống. Nó báo cáo các lần nhấn D-pad LEFT hoặc RIGHT dưới dạng sự kiện AXIS_HAT_X, trong đó -1.0 cho biết bên trái và 1.0 cho biết bên phải.
Thay vào đó, một số bộ điều khiển báo cáo các lần nhấn D-pad bằng mã khoá. Nếu trò chơi của bạn quan tâm đến các lần nhấn phím điều hướng, bạn nên coi các sự kiện trục mũ và mã khoá phím điều hướng là cùng một sự kiện đầu vào, như được đề xuất trong bảng 2.
Bảng 2. Các thao tác mặc định được đề xuất cho trò chơi đối với mã khoá D-pad và giá trị trục mũ.
| Game Action | Mã khoá D-pad | Mã trục mũ |
|---|---|---|
| Di chuyển lên | KEYCODE_DPAD_UP |
AXIS_HAT_Y (đối với các giá trị từ 0 đến -1,0) |
| Di chuyển xuống | KEYCODE_DPAD_DOWN |
AXIS_HAT_Y (đối với các giá trị từ 0 đến 1) |
| Di chuyển sang trái | KEYCODE_DPAD_LEFT |
AXIS_HAT_X (đối với các giá trị từ 0 đến -1,0) |
| Di chuyển sang phải | KEYCODE_DPAD_RIGHT |
AXIS_HAT_X (đối với các giá trị từ 0 đến 1) |
Đoạn mã sau đây cho thấy một lớp trợ giúp cho phép bạn kiểm tra các giá trị mã khoá và trục mũ từ một sự kiện đầu vào để xác định hướng của 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);
}
}
Bạn có thể dùng lớp trợ giúp này trong trò chơi của mình bất cứ khi nào bạn muốn xử lý dữ liệu đầu vào của D-pad (ví dụ: trong các lệnh gọi lại onGenericMotionEvent() hoặc onKeyDown()).
Ví dụ:
Kotlin
private val dpad = Dpad()
...
override fun onGenericMotionEvent(event: MotionEvent): Boolean {
if (Dpad.isDpadDevice(event)) {
when (dpad.getDirectionPressed(event)) {
Dpad.LEFT -> {
// Do something for LEFT direction press
...
return true
}
Dpad.RIGHT -> {
// Do something for RIGHT direction press
...
return true
}
Dpad.UP -> {
// Do something for UP direction press
...
return true
}
...
}
}
// Check if this event is from a joystick movement and process accordingly.
...
}
Java
Dpad dpad = new Dpad();
...
@Override
public boolean onGenericMotionEvent(MotionEvent event) {
// Check if this event if from a D-pad and process accordingly.
if (Dpad.isDpadDevice(event)) {
int press = dpad.getDirectionPressed(event);
switch (press) {
case LEFT:
// Do something for LEFT direction press
...
return true;
case RIGHT:
// Do something for RIGHT direction press
...
return true;
case UP:
// Do something for UP direction press
...
return true;
...
}
}
// Check if this event is from a joystick movement and process accordingly.
...
}
Xử lý chuyển động của cần điều khiển
Khi người chơi di chuyển cần điều khiển trên bộ điều khiển trò chơi, Android sẽ báo cáo một MotionEvent chứa mã thao tác ACTION_MOVE và vị trí mới của các trục cần điều khiển. Trò chơi của bạn có thể sử dụng dữ liệu do MotionEvent cung cấp để xác định xem có chuyển động nào của cần điều khiển mà trò chơi quan tâm hay không.
Xin lưu ý rằng các sự kiện chuyển động của cần điều khiển có thể kết hợp nhiều mẫu chuyển động với nhau trong một đối tượng duy nhất. Đối tượng MotionEvent chứa vị trí hiện tại cho từng trục cần điều khiển cũng như nhiều vị trí trước đây cho từng trục. Khi báo cáo các sự kiện chuyển động bằng mã thao tác ACTION_MOVE (chẳng hạn như chuyển động của cần điều khiển), Android sẽ gộp các giá trị trục để tăng hiệu quả. Các giá trị trước đây cho một trục bao gồm tập hợp các giá trị riêng biệt cũ hơn giá trị trục hiện tại và gần đây hơn các giá trị được báo cáo trong mọi sự kiện chuyển động trước đó. Hãy xem tài liệu tham khảo MotionEvent để biết thông tin chi tiết.
Để hiển thị chính xác chuyển động của một đối tượng trong trò chơi dựa trên đầu vào của cần điều khiển, bạn có thể sử dụng thông tin trong quá khứ do các đối tượng MotionEvent cung cấp.
Bạn có thể truy xuất các giá trị hiện tại và trước đây bằng các phương thức sau:
getAxisValue()getHistoricalAxisValue()getHistorySize()(để tìm số điểm trong nhật ký của sự kiện cần điều khiển)
Đoạn mã sau đây cho thấy cách bạn có thể ghi đè phương thức gọi lại onGenericMotionEvent() để xử lý dữ liệu đầu vào của cần điều khiển. Trước tiên, bạn nên xử lý các giá trị trước đây cho một trục, sau đó xử lý vị trí hiện tại của trục đó.
Kotlin
class GameView(...) : View(...) {
override fun onGenericMotionEvent(event: MotionEvent): Boolean {
// Check that the event came from a game controller
return if (event.source and InputDevice.SOURCE_JOYSTICK == InputDevice.SOURCE_JOYSTICK
&& event.action == MotionEvent.ACTION_MOVE) {
// Process the movements starting from the
// earliest historical position in the batch
(0 until event.historySize).forEach { i ->
// Process the event at historical position i
processJoystickInput(event, i)
}
// Process the current movement sample in the batch (position -1)
processJoystickInput(event, -1)
true
} else {
super.onGenericMotionEvent(event)
}
}
}
Java
public class GameView extends View {
@Override
public boolean onGenericMotionEvent(MotionEvent event) {
// Check that the event came from a game controller
if ((event.getSource() & InputDevice.SOURCE_JOYSTICK) ==
InputDevice.SOURCE_JOYSTICK &&
event.getAction() == MotionEvent.ACTION_MOVE) {
// Process all historical movement samples in the batch
final int historySize = event.getHistorySize();
// Process the movements starting from the
// earliest historical position in the batch
for (int i = 0; i < historySize; i++) {
// Process the event at historical position i
processJoystickInput(event, i);
}
// Process the current movement sample in the batch (position -1)
processJoystickInput(event, -1);
return true;
}
return super.onGenericMotionEvent(event);
}
}
Trước khi sử dụng đầu vào của cần điều khiển, bạn cần xác định xem cần điều khiển có được đặt ở vị trí trung tâm hay không, sau đó tính toán các chuyển động của trục tương ứng. Cần điều khiển thường có một vùng phẳng, tức là một dải giá trị gần toạ độ (0,0) mà tại đó trục được coi là ở vị trí trung tâm. Nếu giá trị trục do Android báo cáo nằm trong vùng phẳng, bạn nên coi bộ điều khiển ở trạng thái nghỉ (tức là không chuyển động dọc theo cả hai trục).
Đoạn mã này cho thấy một phương thức trợ giúp tính toán chuyển động dọc theo từng trục. Bạn gọi trình trợ giúp này trong phương thức processJoystickInput() được mô tả thêm trong mẫu sau:
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;
}
Tổng hợp lại, đây là cách bạn có thể xử lý chuyển động của cần điều khiển trong trò chơi:
Kotlin
private fun processJoystickInput(event: MotionEvent, historyPos: Int) {
val inputDevice = event.device
// Calculate the horizontal distance to move by
// using the input value from one of these physical controls:
// the left control stick, hat axis, or the right control stick.
var x: Float = getCenteredAxis(event, inputDevice, MotionEvent.AXIS_X, historyPos)
if (x == 0f) {
x = getCenteredAxis(event, inputDevice, MotionEvent.AXIS_HAT_X, historyPos)
}
if (x == 0f) {
x = getCenteredAxis(event, inputDevice, MotionEvent.AXIS_Z, historyPos)
}
// Calculate the vertical distance to move by
// using the input value from one of these physical controls:
// the left control stick, hat switch, or the right control stick.
var y: Float = getCenteredAxis(event, inputDevice, MotionEvent.AXIS_Y, historyPos)
if (y == 0f) {
y = getCenteredAxis(event, inputDevice, MotionEvent.AXIS_HAT_Y, historyPos)
}
if (y == 0f) {
y = getCenteredAxis(event, inputDevice, MotionEvent.AXIS_RZ, historyPos)
}
// Update the ship object based on the new x and y values
}
Java
private void processJoystickInput(MotionEvent event,
int historyPos) {
InputDevice inputDevice = event.getDevice();
// Calculate the horizontal distance to move by
// using the input value from one of these physical controls:
// the left control stick, hat axis, or the right control stick.
float x = getCenteredAxis(event, inputDevice,
MotionEvent.AXIS_X, historyPos);
if (x == 0) {
x = getCenteredAxis(event, inputDevice,
MotionEvent.AXIS_HAT_X, historyPos);
}
if (x == 0) {
x = getCenteredAxis(event, inputDevice,
MotionEvent.AXIS_Z, historyPos);
}
// Calculate the vertical distance to move by
// using the input value from one of these physical controls:
// the left control stick, hat switch, or the right control stick.
float y = getCenteredAxis(event, inputDevice,
MotionEvent.AXIS_Y, historyPos);
if (y == 0) {
y = getCenteredAxis(event, inputDevice,
MotionEvent.AXIS_HAT_Y, historyPos);
}
if (y == 0) {
y = getCenteredAxis(event, inputDevice,
MotionEvent.AXIS_RZ, historyPos);
}
// Update the ship object based on the new x and y values
}
Để hỗ trợ những tay điều khiển trò chơi có các tính năng phức tạp hơn ngoài một cần điều khiển duy nhất, hãy làm theo các phương pháp hay nhất sau:
- Xử lý hai cần điều khiển. Nhiều tay điều khiển trò chơi có cả cần điều khiển bên trái và bên phải. Đối với cần điều khiển bên trái, Android báo cáo các chuyển động ngang dưới dạng sự kiện
AXIS_Xvà các chuyển động dọc dưới dạng sự kiệnAXIS_Y. Đối với cần điều khiển bên phải, Android báo cáo các chuyển động ngang dưới dạng sự kiệnAXIS_Zvà các chuyển động dọc dưới dạng sự kiệnAXIS_RZ. Đảm bảo xử lý cả hai cần điều khiển trong mã của bạn. - Xử lý các lần nhấn nút kích hoạt ở vai (và đảm bảo trò chơi của bạn hoạt động với các sự kiện
AXIS_vàKEYCODE_BUTTON_). Một số bộ điều khiển có nút kích hoạt ở vai trái và vai phải. Khi có các trình kích hoạt này, chúng sẽ phát ra sự kiệnAXIS_*TRIGGERhoặcKEYCODE_BUTTON_*2hoặc cả hai. Đối với cò súng bên trái, đây sẽ làAXIS_LTRIGGERvàKEYCODE_BUTTON_L2. Đối với cò phải, đây sẽ làAXIS_RTRIGGERvàKEYCODE_BUTTON_R2. Sự kiện trục chỉ xảy ra nếu bộ kích hoạt phát ra một dải giá trị từ 0 đến 1 và một số bộ điều khiển có đầu ra tương tự sẽ phát ra các sự kiện nút ngoài các sự kiện trục. Các trò chơi phải hỗ trợ cả sự kiệnAXIS_vàKEYCODE_BUTTON_để duy trì khả năng tương thích với tất cả các tay điều khiển trò chơi thông thường, nhưng nên ưu tiên sự kiện phù hợp nhất với lối chơi của bạn nếu một tay điều khiển báo cáo cả hai. Trên Android 4.3 (API cấp 18) trở lên, bộ điều khiển tạo raAXIS_LTRIGGERcũng báo cáo một giá trị giống hệt cho trụcAXIS_BRAKE. Điều này cũng đúng đối vớiAXIS_RTRIGGERvàAXIS_GAS. Android báo cáo tất cả các lần nhấn nút kích hoạt tương tự bằng một giá trị được chuẩn hoá từ 0,0 (nhả) đến 1,0 (nhấn hoàn toàn). - Hành vi và khả năng hỗ trợ cụ thể có thể khác nhau trong môi trường mô phỏng.
Các nền tảng mô phỏng, chẳng hạn như Google Play Games, có thể có hành vi khác biệt đôi chút dựa trên các chức năng của hệ điều hành máy chủ. Ví dụ: một số bộ điều khiển phát cả sự kiện
AXIS_vàKEYCODE_BUTTON_chỉ phát sự kiệnAXIS_và có thể hoàn toàn không hỗ trợ một số bộ điều khiển.
Các biến thể phổ biến
Vì Android hỗ trợ nhiều loại tay điều khiển, nên có thể bạn sẽ không biết cách tạo và kiểm thử để xác minh rằng trò chơi của bạn hoạt động mà không có lỗi trong cơ sở người chơi. Chúng tôi nhận thấy rằng mặc dù có vẻ đa dạng, nhưng các nhà sản xuất bộ điều khiển trên khắp thế giới thường tuân thủ nhất quán 3 kiểu bộ điều khiển khác nhau. Một số thiết bị cung cấp các nút bật/tắt phần cứng giữa hai hoặc nhiều chế độ trong số này.
Điều này có nghĩa là bạn có thể kiểm thử chỉ với 3 bộ điều khiển trong nhóm phát triển và vẫn tự tin rằng trò chơi của bạn có thể chơi được mà không cần dùng đến danh sách cho phép và danh sách từ chối.
Các loại tay điều khiển phổ biến
Kiểu tay cầm phổ biến nhất thường mô phỏng bố cục của các máy chơi trò chơi phổ biến. Điều này vừa mang tính thẩm mỹ trong bố cục và nhãn nút, vừa mang tính chức năng theo những sự kiện được tạo ra. Bộ điều khiển có các nút bật/tắt phần cứng giữa các loại bảng điều khiển khác nhau sẽ thay đổi các sự kiện mà chúng gửi và thường là cả bố cục nút logic.
Khi kiểm thử, bạn nên xác thực rằng trò chơi của bạn hoạt động với một bộ điều khiển trong mỗi danh mục. Bạn có thể chọn kiểm thử bằng bộ điều khiển bên thứ nhất hoặc các nhà sản xuất bên thứ ba phổ biến. Nhìn chung, chúng tôi sẽ cố gắng hết sức để liên kết các bộ điều khiển phổ biến nhất với định nghĩa ở trên.
| Loại bộ điều khiển | Sự khác biệt về hành vi | Biến thể về việc gắn nhãn |
|---|---|---|
| Tay cầm theo kiểu Xbox
Đây là những bộ điều khiển thường được sản xuất cho nền tảng Microsoft Xbox và Windows*. |
Các bộ điều khiển này khớp với bộ tính năng được nêu trong phần Xử lý dữ liệu đầu vào của bộ điều khiển | Các nút L2/R2 trên những bộ điều khiển này được gắn nhãn LT/RT |
| Bộ điều khiển kiểu công tắc
Những tay cầm này thường được thiết kế cho dòng máy chơi trò chơi Nintendo Switch*. |
Các bộ điều khiển này gửi KeyEvent
KEYCODE_BUTTON_R2
KEYCODE_BUTTON_L2
MotionEvent |
Các nút L2/R2 trên những bộ điều khiển này được gắn nhãn ZL/ZR.
Các bộ điều khiển này cũng đổi nút A và B, cũng như nút X và Y, nên |
| Tay cầm kiểu PlayStation
Những bộ điều khiển này thường được thiết kế cho dòng máy chơi trò chơi Sony PlayStation*. |
Các bộ điều khiển này gửi MotionEvent, chẳng hạn như Bộ điều khiển kiểu Xbox, nhưng cũng gửi KeyEvent, chẳng hạn như Bộ điều khiển kiểu Switch khi được nhấn hoàn toàn. |
Các bộ điều khiển này sử dụng một bộ ký tự khác cho các nút trên mặt. |
* Microsoft, Xbox và Windows là các nhãn hiệu đã đăng ký của Microsoft; Nintendo Switch là nhãn hiệu đã đăng ký của Nintendo of America Inc.; PlayStation là nhãn hiệu đã đăng ký của Sony Interactive Entertainment Inc.
Phân biệt các nút kích hoạt
Một số bộ điều khiển gửi AXIS_LTRIGGER và AXIS_RTRIGGER, một số gửi KEYCODE_BUTTON_L2 và KEYCODE_BUTTON_R2, còn những bộ điều khiển khác gửi tất cả các sự kiện này dựa trên khả năng phần cứng của chúng. Tối đa hoá khả năng tương thích bằng cách hỗ trợ tất cả các sự kiện này.
Tất cả tay điều khiển gửi AXIS_LTRIGGER cũng sẽ gửi AXIS_BRAKE, tương tự đối với AXIS_RTRIGGER và AXIS_GAS để giúp tối đa hoá khả năng tương thích giữa vô lăng đua xe và tay điều khiển trò chơi thông thường. Nhìn chung, điều này sẽ không gây ra vấn đề, nhưng hãy lưu ý đối với các tính năng như màn hình gán lại khoá.
| Trigger | MotionEvent |
KeyEvent |
|---|---|---|
| Nút kích hoạt bên trái | AXIS_LTRIGGERAXIS_BRAKE
|
KEYCODE_BUTTON_L2
|
| Nút cò bên phải | AXIS_RTRIGGERAXIS_GAS
|
KEYCODE_BUTTON_R2
|
Bạn nên cẩn thận xác minh rằng trò chơi của bạn có thể xử lý cả KeyEvent và MotionEvent để duy trì khả năng tương thích với nhiều tay điều khiển nhất có thể và các sự kiện không bị trùng lặp.
Bộ điều khiển được hỗ trợ
Khi kiểm thử, bạn nên xác thực rằng trò chơi của bạn hoạt động với một bộ điều khiển trong mỗi danh mục.
- Kiểu Xbox
- Kiểu Nintendo Switch
- Phong cách PlayStation
Bạn có thể kiểm thử bằng bộ điều khiển của bên thứ nhất hoặc các nhà sản xuất bên thứ ba phổ biến. Nhìn chung, chúng tôi sẽ liên kết các bộ điều khiển phổ biến nhất với định nghĩa một cách chặt chẽ nhất có thể.