На системном уровне Android сообщает входные коды событий с игровых контроллеров в виде кодов клавиш Android и значений осей. В своей игре вы можете получать эти коды и значения и конвертировать их в определенные внутриигровые действия.
Когда игроки физически подключают или подключают по беспроводной сети игровой контроллер к своим устройствам на базе Android, система автоматически определяет контроллер как устройство ввода и начинает сообщать о событиях ввода. Ваша игра может получать эти входные события, реализуя следующие методы обратного вызова в активном Activity
или сфокусированном View
(вы должны реализовать обратные вызовы либо для Activity
, либо View
, но не для обоих):
- Из
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
, с которым взаимодействует пользователь. Проверьте следующие объекты, предоставленные обратными вызовами, чтобы получить информацию о типе полученного события ввода:
-
KeyEvent
- Объект, описывающий события крестовины и кнопок геймпада. Ключевые события сопровождаются кодом клавиши , который указывает на срабатывание конкретной кнопки, например
DPAD_DOWN
илиBUTTON_A
. Вы можете получить код ключа, вызвавgetKeyCode()
или обратные вызовы ключевых событий, такие какonKeyDown()
. -
MotionEvent
- Объект, описывающий ввод данных от движений джойстика и триггера плеча. События движения сопровождаются кодом действия и набором значений оси . Код действия указывает произошедшее изменение состояния, например перемещение джойстика. Значения оси описывают положение и другие свойства движения для конкретного физического элемента управления, например
AXIS_X
илиAXIS_RTRIGGER
. Вы можете получить код действия, вызвавgetAction()
, а значение оси, вызвавgetAxisValue()
.
В этом уроке основное внимание уделяется тому, как можно обрабатывать ввод от наиболее распространенных типов физических элементов управления (кнопок геймпада, кнопок управления и джойстиков) на игровом экране, реализуя вышеупомянутые методы обратного вызова View
и обрабатывая объекты KeyEvent
и MotionEvent
.
Убедитесь, что игровой контроллер подключен
Сообщая о событиях ввода, 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, см. в разделе Поддержка нескольких игровых контроллеров .
Котлин
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 }
Ява
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()
. Затем для каждого возвращенного объектаInputDevice.MotionRange
вызовитеgetAxis()
чтобы получить идентификатор его оси.
Обрабатывать нажатия кнопок геймпада
На рис. 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
с игровым действием.
Котлин
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 }
Ява
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 Back . Если ваше приложение поддерживает эти версии Android, обязательно рассматривайте BUTTON_A
как основное игровое действие. Чтобы определить текущую версию Android SDK на устройстве, обратитесь к значению Build.VERSION.SDK_INT
.
Обработка ввода с помощью навигационной панели
4-позиционная навигационная панель (D-pad) является обычным физическим элементом управления во многих игровых контроллерах. Android сообщает о нажатиях крестовины ВВЕРХ и ВНИЗ как о событиях AXIS_HAT_Y
с диапазоном от -1,0 (вверх) до 1,0 (вниз), а о нажатиях крестовины ВЛЕВО или ВПРАВО как о событиях AXIS_HAT_X
с диапазоном от -1,0 (влево) до 1,0 ( верно).
Некоторые контроллеры вместо этого сообщают о нажатиях 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) |
В следующем фрагменте кода показан вспомогательный класс, который позволяет проверять ось шляпы и значения кода клавиши из события ввода, чтобы определить направление крестовины.
Котлин
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 } }
Ява
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()
).
Например:
Котлин
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. ... }
Ява
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()
.
В следующем фрагменте показано, как можно переопределить обратный вызов onGenericMotionEvent()
для обработки ввода с джойстика. Сначала следует обработать исторические значения оси, а затем обработать ее текущее положение.
Котлин
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) } } }
Ява
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, попадает в плоскую область, вам следует считать контроллер неподвижным (то есть неподвижным по обеим осям).
Во фрагменте ниже показан вспомогательный метод, который вычисляет движение вдоль каждой оси. Вы вызываете этот помощник в методеprocessJoystickInput processJoystickInput()
описанном ниже.
Котлин
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 }
Ява
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; }
Собрав все это вместе, вот как вы можете обрабатывать движения джойстика в своей игре:
Котлин
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 }
Ява
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
. Обязательно обрабатывайте оба контроллера в своем коде. - Обрабатывайте нажатия триггера на плечо (и убедитесь, что ваша игра работает с событиями
AXIS_
иKEYCODE_BUTTON_
). Некоторые контроллеры имеют триггеры для левого и правого плеча. Когда эти триггеры присутствуют, они выдают событиеAXIS_*TRIGGER
илиKEYCODE_BUTTON_*2
или оба. Для левого триггера это будутAXIS_LTRIGGER
иKEYCODE_BUTTON_L2
. Для правильного триггера это будутAXIS_RTRIGGER
иKEYCODE_BUTTON_R2
. События оси происходят только в том случае, если триггер выдает диапазон значений от 0 до 1, а некоторые контроллеры с аналоговым выходом выдают события кнопки в дополнение к событиям оси. Игры должны поддерживать событияAXIS_
иKEYCODE_BUTTON_
, чтобы оставаться совместимыми со всеми распространенными игровыми контроллерами, но отдавайте предпочтение событию, которое имеет наибольший смысл для вашего игрового процесса, если контроллер сообщает об обоих. В 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_
, а поддержка некоторых контроллеров может полностью отсутствовать.