Ở cấp hệ thống, Android báo cáo mã sự kiện đầu vào từ tay điều khiển trò chơi dưới dạng mã phím và giá trị trục Android. Trong trò chơi, bạn có thể nhận được các mã và giá trị này rồi chuyển đổi chúng thành các hành động cụ thể trong trò chơi.
Khi người chơi kết nối thực tế hoặc ghép nối không dây tay điều khiển trò chơi với thiết bị chạy Android, hệ thống sẽ tự động phát hiện tay điều khiển đó là thiết bị đầu vào và bắt đầu báo cáo các sự kiện đầu vào của tay điều khiển. Trò chơi của bạn có thể nhận được các sự kiện đầu vào này bằng cách triển khai các phương thức gọi lại sau đây trong Activity
đang hoạt động hoặc View
được lấy tiêu điểm (bạn nên triển khai các lệnh gọi lại cho Activity
hoặc View
, nhưng không phải cả hai):
- Từ
Activity
:dispatchGenericMotionEvent(android.view. MotionEvent)
Được gọi để xử lý các sự kiện chuyển động chung chung như chuyển động của cần điều khiển.
dispatchKeyEvent(android.view.KeyEvent)
Được gọi để xử lý các sự kiện chính như nhấn hoặc nhả tay điều khiển trò chơi hoặc nút D-pad.
- Từ
View
:onGenericMotionEvent(android.view.MotionEvent)
Được gọi để xử lý các sự kiện chuyển động chung, chẳng hạn như chuyển động của cần điều khiển.
onKeyDown(int, android.view.KeyEvent)
Được gọi để xử lý thao tác nhấn phím vật lý, chẳng hạn như tay điều khiển trò chơi hoặc nút D-pad.
onKeyUp(int, android.view.KeyEvent)
Được gọi để xử lý việc nhả một phím vật lý, chẳng hạn như tay điều khiển trò chơi hoặc nút D-pad.
Bạn nên chụp các sự kiện từ đối tượng View
cụ thể mà người dùng tương tác.
Kiểm tra các đối tượng do lệnh gọi lại cung cấp sau đây để biết thông tin về loại sự kiện đầu vào đã nhận được:
KeyEvent
- Một đối tượng mô tả các sự kiện nút bàn phím di chuyển (D-pad) và tay điều khiển trò chơi. Sự kiện phím đi kèm với một mã phím cho biết nút cụ thể đã được kích hoạt, chẳng hạn như
DPAD_DOWN
hoặcBUTTON_A
. Bạn có thể lấy mã khoá bằng cách gọigetKeyCode()
hoặc từ lệnh gọi lại sự kiện khoá, chẳng hạn nhưonKeyDown()
. MotionEvent
- Đối tượng mô tả dữ liệu đầu vào từ các thao tác di chuyển cần điều khiển và cò vai. Sự kiện chuyển động đi kèm với mã hành động và một tập hợp giá trị trục. Mã hành động chỉ định thay đổi trạng thái đã xảy ra, chẳng hạn như cần điều khiển đang được di chuyển. Các giá trị trục mô tả vị trí và các thuộc tính di chuyển khác cho một thành phần điều khiển thực cụ thể, chẳng hạn như
AXIS_X
hoặcAXIS_RTRIGGER
. Bạn có thể lấy mã hành động bằng cách gọigetAction()
và giá trị trục bằng cách gọigetAxisValue()
.
Bài học này tập trung vào cách bạn có thể xử lý dữ liệu đầu vào từ các loại chế độ điều khiển vật lý phổ biến nhất (nút tay điều khiển trò chơi, bàn phím di chuyển và cần điều khiển) trong màn hình trò chơi bằng cách triển khai các phương thức gọi lại View
đã đề cập ở trên và xử lý các đối tượng KeyEvent
và MotionEvent
.
Xác minh rằng bạn đã kết nối bộ điều khiển trò chơi
Khi báo cáo sự kiện đầu vào, Android không phân biệt giữa các sự kiện đến từ thiết bị không phải tay điều khiển trò chơi và các sự kiện đến từ tay điều khiển trò chơi. Ví dụ: một thao tác trên màn hình cảm ứng sẽ tạo ra một sự kiện AXIS_X
biểu thị toạ độ X của bề mặt cảm ứng, nhưng một cần điều khiển sẽ tạo ra một sự kiện AXIS_X
biểu thị vị trí X của cần điều khiển. Nếu trò chơi của bạn quan tâm đến việc xử lý dữ liệu đầu vào của tay điều khiển trò chơi, trước tiên, bạn nên kiểm tra để đảm bảo sự kiện đầu vào đến từ một loại nguồn có liên quan.
Để xác minh rằng thiết bị đầu vào đã kết nối là tay điều khiển trò chơi, hãy gọi getSources()
để lấy trường bit kết hợp của các loại nguồn đầu vào được hỗ trợ trên thiết bị đó. Sau đó, bạn có thể kiểm thử để xem các trường sau đây có được đặt hay không:
- Loại nguồn
SOURCE_GAMEPAD
cho biết thiết bị đầu vào có các nút tay điều khiển trò chơi (ví dụ:BUTTON_A
). Xin lưu ý rằng loại nguồn này không chỉ ra chính xác liệu tay điều khiển trò chơi có các nút D-pad hay không, mặc dù hầu hết các tay điều khiển trò chơi thường có các nút điều khiển hướng. - Loại nguồn
SOURCE_DPAD
cho biết thiết bị đầu vào có các nút D-pad (ví dụ:DPAD_UP
). - Loại nguồn
SOURCE_JOYSTICK
cho biết thiết bị đầu vào có các cần điều khiển analog (ví dụ: cần điều khiển ghi lại chuyển động dọc theoAXIS_X
và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à tay điều khiển trò chơi hay không. Nếu có, phương thức này sẽ truy xuất mã thiết bị cho tay điều khiển trò chơi. Sau đó, bạn có thể liên kết từng mã thiết bị với một người chơi trong trò chơi và xử lý riêng các hành động 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ị 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 (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; }
Ngoài ra, bạn nên kiểm tra các chức năng nhập riêng lẻ mà tay điều khiển trò chơi đã kết nối hỗ trợ. Điều này có thể hữu ích, chẳng hạn như nếu bạn muốn trò chơi của mình chỉ sử dụng phương thức nhập từ bộ điều khiển vật lý mà trò chơi hiểu được.
Để phát hiện xem tay điều khiển trò chơi đã kết nối có hỗ trợ một mã phím hoặc mã trục cụ thể hay không, hãy sử dụng các kỹ thuật sau:
- Trong Android 4.4 (API cấp 19) trở lên, bạn có thể xác định xem một mã phím có được hỗ trợ trên tay điều khiển trò chơi đã kết nối hay không bằng cách gọi
hasKeys(int...)
. - Trong Android 3.1 (API cấp 12) trở lên, bạn có thể tìm thấy tất cả các trục có sẵn được hỗ trợ trên tay điều khiển trò chơi đã kết nối bằng cách gọi
getMotionRanges()
trước tiên. Sau đó, trên mỗi đối tượngInputDevice.MotionRange
được trả về, hãy gọigetAxis()
để lấy mã nhận dạng trục của đối tượng đó.
Xử lý thao tác nhấn nút trên tay điều khiển trò chơi
Hình 1 cho thấy cách Android ánh xạ các mã phím và giá trị trục với các nút điều khiển thực trên hầu hết tay điều khiển trò chơi.
Chú thích trong hình đề cập đến những nội dung sau:
Các mã phím phổ biến do thao tác nhấn nút trên tay điều khiển trò chơi tạo ra bao gồm BUTTON_A
, BUTTON_B
, BUTTON_SELECT
và BUTTON_START
. Một số tay điều khiển trò chơi cũng kích hoạt mã phím DPAD_CENTER
khi nhấn vào giữa thanh điều khiển D-pad. Trò chơi của bạn có thể kiểm tra mã phím bằng cách gọi getKeyCode()
hoặc từ các lệnh gọi lại sự kiện chính như onKeyDown()
. Nếu mã này đại diện cho một sự kiện có liên quan đến trò chơi, hãy xử lý mã đó dưới dạng một hành động trong trò chơi. Bảng 1 liệt kê các thao tác trò chơi được đề xuất cho các nút tay điều khiển trò chơi phổ biến nhất.
Hành động trong trò chơi | Mã phím nút |
---|---|
Bắt đầu trò chơi trong trình đơn chính hoặc tạm dừng/bỏ tạm dừng trong trò chơi | BUTTON_START * |
Hiện trình đơn | BUTTON_SELECT *
và KEYCODE_MENU * |
Giống như hành vi điều hướng Quay lại của Android được mô tả trong hướng dẫn thiết kế Điều hướng. | KEYCODE_BACK |
Quay lại mục trước trong trình đơn | BUTTON_B |
Xác nhận lựa chọn hoặc thực hiện hành động chính trong trò chơi | BUTTON_A và
DPAD_CENTER |
* Trò chơi của bạn không được phụ thuộc vào sự hiện diện của các nút Bắt đầu, Chọn hoặc Trình đơn.
Mẹo: Hãy cân nhắc việc cung cấp màn hình cấu hình trong trò chơi để cho phép người dùng cá nhân hoá các mối liên kết tay điều khiển trò chơi của riêng họ cho các hành động trong trò chơi.
Đoạn mã sau đây cho biết cách bạn có thể ghi đè onKeyDown()
để liên kết các thao tác nhấn nút BUTTON_A
và DPAD_CENTER
với một hành động trong trò chơi.
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; } }
Lưu ý: Trên Android 4.2 (API cấp 17) trở xuống, theo mặc định, hệ thống coi BUTTON_A
là phím Back (Quay lại) của Android. Nếu ứng dụng của bạn hỗ trợ các phiên bản Android này, hãy nhớ coi BUTTON_A
là thao tác chính trong trò chơi. Để xác định phiên bản SDK Android hiện tại trên thiết bị, hãy tham khảo giá trị Build.VERSION.SDK_INT
.
Xử lý phương thức nhập bằng bàn phím di chuyển
Bàn phím di chuyển 4 chiều (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 thao tác nhấn D-pad UP (LÊN) và DOWN (XUỐNG) dưới dạng sự kiện AXIS_HAT_Y
có phạm vi từ -1.0 (lên) đến 1.0 (xuống) và các thao tác nhấn D-pad LEFT (TRẢ LẠI) hoặc RIGHT (PHẢI) dưới dạng sự kiện AXIS_HAT_X
có phạm vi từ -1.0 (trái) đến 1.0 (phải).
Một số bộ điều khiển báo cáo các lần nhấn D-pad bằng một mã phím. Nếu trò chơi của bạn quan tâm đến các thao tác nhấn trên D-pad, bạn nên coi các sự kiện trục mũ và mã phím D-pad là các sự kiện đầu vào tương tự, như đề xuất trong bảng 2.
Hành động trong trò chơi | Mã phím 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 (cho các giá trị từ 0 đến 1.0) |
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 (cho các giá trị từ 0 đến 1.0) |
Đoạn mã sau đây cho thấy một lớp trợ giúp cho phép bạn kiểm tra trục mũ và giá trị mã phím từ một sự kiện đầu vào để xác định hướng 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; } } }
Bạn có thể sử dụng lớp trình trợ giúp này trong trò chơi bất cứ khi nào bạn muốn xử lý đầu vào D-pad (ví dụ: trong 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 tay đ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à các vị trí đã cập nhật của 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ó xảy ra chuyển động cần quan tâm trên cần điều khiển 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ể gộp nhiều mẫu chuyển động với nhau trong một đối tượng. Đối tượng MotionEvent
chứa vị trí hiện tại của mỗi trục cần điều khiển cũng như nhiều vị trí trước đây của mỗi trục. Khi báo cáo sự kiện chuyển động bằng mã hành động ACTION_MOVE
(chẳng hạn như chuyển động của cần điều khiển), Android sẽ phân lô các giá trị trục để đảm bảo hiệu quả. Giá trị trong quá khứ cho một trục bao gồm một tập hợp các giá trị riêng biệt cũ hơn giá trị trục hiện tại và mới hơn các giá trị được báo cáo trong bất kỳ sự kiện chuyển động nào trước đó. Hãy xem tài liệu tham khảo MotionEvent
để biết thông tin chi tiết.
Bạn có thể sử dụng thông tin trong quá khứ để kết xuất chính xác hơn chuyển động của đối tượng trò chơi dựa trên dữ liệu đầu vào của cần điều khiển. Để truy xuất giá trị hiện tại và giá trị trong quá khứ, hãy gọi getAxisValue()
hoặc getHistoricalAxisValue()
. Bạn cũng có thể tìm thấy số lượng điểm trước đây trong sự kiện cần điều khiển bằng cách gọi getHistorySize()
.
Đoạn mã sau đây cho thấy cách bạn có thể ghi đè lệnh 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 phương thức nhập bằng cần điều khiển, bạn cần xác định xem cần điều khiển có ở giữa hay không, sau đó tính toán chuyển động trục của cần điều khiển cho phù hợp. Cần điều khiển thường có một khu vực phẳng, tức là một dải giá trị gần toạ độ (0,0) mà tại đó trục được coi là ở giữa. Nếu giá trị trục do Android báo cáo nằm trong khu vực phẳng, bạn nên coi tay đ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ã dưới đây cho thấy một phương thức trợ giúp để tính toán chuyển động dọc theo mỗi trục. Bạn gọi trình trợ giúp này trong phương thức processJoystickInput()
được mô tả thêm ở bên dưới.
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óm lại, sau đây là cách bạn có thể xử lý các 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 tinh vi hơn ngoài một cần điều khiển, hãy làm theo các phương pháp hay nhất sau đây:
- 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 theo chiều ngang dưới dạng sự kiện
AXIS_X
và các chuyển động theo chiều dọc dưới dạng sự kiệnAXIS_Y
. Đối với thanh bên phải, Android báo cáo chuyển động ngang là sự kiệnAXIS_Z
và chuyển động theo chiều dọc là sự kiệnAXIS_RZ
. Hãy nhớ xử lý cả hai thẻ điều khiển trong mã của bạn. -
Xử lý các thao tác nhấn cò 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ố tay điều khiển có nút kích hoạt vai trái và phải. Khi xuất hiện, các trình kích hoạt này sẽ phát ra một sự kiệnAXIS_*TRIGGER
hoặcKEYCODE_BUTTON_*2
hoặc cả hai. Đối với nút kích hoạt bên trái, đây sẽ làAXIS_LTRIGGER
vàKEYCODE_BUTTON_L2
. Đối với nút kích hoạt bên phải, đó sẽ làAXIS_RTRIGGER
vàKEYCODE_BUTTON_R2
. Sự kiện trục chỉ xảy ra nếu trình kích hoạt phát ra một dải giá trị từ 0 đến 1 và một số tay điều khiển có đầu ra tương tự phát ra sự kiện nút ngoài sự kiện trục. Trò chơi phải hỗ trợ cả sự kiệnAXIS_
vàKEYCODE_BUTTON_
để tương thích với tất cả tay điều khiển trò chơi phổ biến, nhưng ưu tiên sự kiện phù hợp nhất với lối chơi của bạn nếu tay điều khiển báo cáo cả hai. Trên Android 4.3 (API cấp 18) trở lên, trình điều khiển tạo raAXIS_LTRIGGER
cũ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_RTRIGGER
và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 giá trị chuẩn hoá từ 0 (đã nhả) đến 1 (đã nhấn hết). -
Hành vi và khả năng hỗ trợ cụ thể có thể khác nhau trong môi trường được mô phỏng. Các nền tảng được mô phỏng, chẳng hạn như Google Play Games, có thể có hành vi hơi khác nhau dựa trên chức năng của hệ điều hành lưu trữ. Ví dụ: một số tay điều khiển phát ra cả sự kiện
AXIS_
vàKEYCODE_BUTTON_
chỉ phát ra sự kiệnAXIS_
và có thể thiếu hoàn toàn tính năng hỗ trợ cho một số tay điều khiển.