Gerenciar ações do controle

Os controles têm dois tipos de ações:

  • KeyEvent , usado para qualquer botão com um estado binário de "ativado" e "desativado".
  • MotionEvent , usado para qualquer eixo que retorne um intervalo de valores. Por exemplo, de -1 a 1 para controles analógicos ou de 0 a 1 para gatilhos analógicos.

Você pode ler essas entradas do View que tem focus.

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);
}

Se necessário, você pode ler eventos diretamente do Activity.

Verificar se um controle de jogo está conectado

Ao informar eventos de entrada, o Android reutiliza os mesmos IDs de tecla ou eixo para diferentes tipos de dispositivos de entrada. Por exemplo, uma ação em uma tela sensível ao toque gera um AXIS_X evento que representa a coordenada X da superfície sensível ao toque, mas um gamepad gera um AXIS_X evento que representa a posição X do controle analógico esquerdo. Isso significa que você precisa verificar o tipo de origem para interpretar corretamente os eventos de entrada.

Para verificar se um InputDevice conectado é um controle de jogo, use a supportsSource(int) função:

  • Um tipo de origem de SOURCE_GAMEPAD indica que o dispositivo de entrada tem botões de controle (por exemplo, KEYCODE_BUTTON_A). Observe que esse tipo de origem não indica estritamente se o controle de jogo tem botões direcionais, embora a maioria dos controles normalmente tenha controles direcionais.
  • Um tipo de origem de SOURCE_DPAD indica que o dispositivo de entrada tem botões direcionais (por exemplo, DPAD_UP).
  • Um tipo de origem de SOURCE_JOYSTICK indica que o dispositivo de entrada tem controles direcionais analógicos, por exemplo, um joystick que registra movimentos ao longo AXIS_X e AXIS_Y).

O snippet de código a seguir mostra um método auxiliar que permite verificar se os dispositivos de entrada conectados são controles de jogos. Em caso afirmativo, o método recupera os códigos de dispositivo dos controles de jogos. Em seguida, você pode associar cada código de dispositivo a um jogador e processar as ações do jogo separadamente para cada jogador conectado. Para saber mais sobre a compatibilidade com vários controles de jogos que estão conectados simultaneamente no mesmo dispositivo Android, consulte Compatibilidade com vários controles de jogos.

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;
}

Processar entradas do controle

Esta seção descreve os tipos de controles de jogos compatíveis com o Android.

Os desenvolvedores de C++ precisam usar a biblioteca Game Controller. Ela unifica todos os controles no subconjunto mais comum de recursos e fornece uma interface consistente entre eles, incluindo a capacidade de detectar o layout dos botões.

Esta figura mostra como um desenvolvedor de jogos Android pode esperar que um controle comum seja exibido no Android.

Controle de jogo genérico com entradas rotuladas, incluindo D-Pad, controles analógicos e botões
Figura 1. Perfil de um controle de jogo genérico.

A tabela lista os nomes e tipos de eventos padrão para controles de jogos. Para uma lista completa de eventos, consulte Variantes comuns. O sistema envia MotionEvent eventos por onGenericMotionEvent e KeyEvent eventos por onKeyDown e onKeyUp.

Entrada do controle Evento de tecla MotionEvent
1. Botão direcional
AXIS_HAT_X
(entrada horizontal)
AXIS_HAT_Y
(entrada vertical)
2. Controle analógico esquerdo
KEYCODE_BUTTON_THUMBL
(quando pressionado)
AXIS_X
(movimento horizontal)
AXIS_Y
(movimento vertical)
3. Controle analógico direito
KEYCODE_BUTTON_THUMBR
(quando pressionado)
AXIS_Z
(movimento horizontal)
AXIS_RZ
(movimento vertical)
4. Botão X KEYCODE_BUTTON_X
5. Botão A KEYCODE_BUTTON_A
6. Botão Y KEYCODE_BUTTON_Y
7. Botão B KEYCODE_BUTTON_B
8. Botão direito
KEYCODE_BUTTON_R1
9. Gatilho direito
AXIS_RTRIGGER
10. Gatilho esquerdo AXIS_LTRIGGER
11. Botão esquerdo KEYCODE_BUTTON_L1
12. Iniciar KEYCODE_BUTTON_START
13. Selecionar KEYCODE_BUTTON_SELECT

Processar o pressionamento de botão

Como o Android informa o pressionamento de botões do controle de forma idêntica ao pressionamento de botões do teclado, você precisa:

  • Validar se o evento está vindo de um SOURCE_GAMEPAD.
  • Verifique se você só recebe o botão uma vez com KeyEvent.getRepeatCount(), O Android envia eventos de tecla repetidos da mesma forma que se você mantivesse uma tecla do teclado pressionada.
  • Indique que um evento é processado retornando true.
  • Transmita eventos não processados para super para verificar se as várias camadas de compatibilidade do Android funcionam corretamente.

    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);
      }
    }
    

Processar entradas pelo botão direcional

O botão direcional de quatro vias é um controle físico comum em muitos controles de jogos. O Android informa o pressionamento dos botões direcionais PARA CIMA e PARA BAIXO como eventos AXIS_HAT_Y, com -1,0 indicando para cima e 1,0 indicando para baixo. Ele informa o pressionamento dos botões ESQUERDO ou DIREITO do botão direcional como eventos AXIS_HAT_X, com -1,0 indicando para a esquerda e 1,0 indicando para a direita.

Alguns controles informam o pressionamento do botão direcional com um código de tecla. Se seu jogo usar os pressionamentos do botão direcional, trate os eventos do botão do ângulo de visão e os códigos do botão direcional como os mesmos eventos de entrada, conforme recomendado na tabela 2.

Tabela 2. Ações recomendadas do jogo padrão para códigos de botão direcional e valores do botão do ângulo de visão.

Ação do jogo Código do botão direcional Código do botão do ângulo de visão
Mover para cima KEYCODE_DPAD_UP AXIS_HAT_Y (para valores de 0 a -1,0)
Mover para baixo KEYCODE_DPAD_DOWN AXIS_HAT_Y (para valores de 0 a 1,0)
Mover para a esquerda KEYCODE_DPAD_LEFT AXIS_HAT_X (para valores de 0 a -1,0)
Mover para a direita KEYCODE_DPAD_RIGHT AXIS_HAT_X (para valores de 0 a 1,0)

O snippet de código a seguir mostra uma classe auxiliar que permite verificar os valores do botão do ângulo de visão e do código de tecla de um evento de entrada para determinar a direção do botão direcional.

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);
     }
}

Você pode usar esta classe auxiliar no seu jogo sempre que quiser processar a entrada do botão direcional (por exemplo, nos onGenericMotionEvent() ou onKeyDown() callbacks).

Exemplo:

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.
    ...
}

Processar movimentos do joystick

Quando os jogadores movem um joystick nos controles de jogos, o Android informa um MotionEvent que contém o ACTION_MOVE código de ação e as posições atualizadas dos eixos do joystick. Seu jogo pode usar os dados fornecidos por MotionEvent para determinar se houve um movimento no joystick.

Observe que os eventos de movimento do joystick podem agrupar várias amostras de movimento em um único objeto. O MotionEvent objeto contém a posição atual para cada eixo do joystick, bem como várias posições históricas para cada eixo. Ao relatar eventos de movimento com o código de ação ACTION_MOVE (por exemplo, movimentos do joystick), o Android agrupa os valores dos eixos para aumentar a eficiência. Os valores históricos de um eixo consistem no conjunto de valores distintos mais antigos do que o valor do eixo atual e mais recentes do que os valores informados em qualquer evento de movimento anterior. Consulte a MotionEvent referência para mais detalhes.

Para renderizar com precisão o movimento de um objeto do jogo com base na entrada do joystick, você pode usar as informações históricas fornecidas por objetos MotionEvent.

É possível recuperar valores atuais e históricos usando os seguintes métodos:

O snippet a seguir mostra como você pode substituir o onGenericMotionEvent() callback para processar a entrada do joystick. Primeiro, você precisa processar os valores históricos de um eixo e, em seguida, a posição atual.

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);
    }
}

Antes de usar a entrada do joystick, é necessário determinar se ele está centralizado e, em seguida, calcular os movimentos do eixo de forma adequada. Os joysticks normalmente têm uma área plana, ou seja, um intervalo de valores próximo à coordenada (0,0) no qual o eixo é considerado centralizado. Se o valor do eixo informado pelo Android estiver dentro da área plana, você precisará fazer com que o controle fique em repouso, isto é, imóvel ao longo dos dois eixos.

O snippet mostra um método auxiliar que calcula o movimento ao longo de cada eixo. Você invoca esse auxiliar no método processJoystickInput() descrito mais adiante no exemplo a seguir:

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;
}

Juntando tudo, veja como você pode processar os movimentos do joystick no seu jogo:

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
}

Para oferecer compatibilidade com controles de jogos que têm recursos mais sofisticados além de um único joystick, siga estas práticas recomendadas:

  • Gerencie dois controles direcionais analógicos. Muitos controles de jogos têm um joystick esquerdo e um direito. Para o direcional analógico esquerdo, o Android relata movimentos horizontais como AXIS_X eventos e movimentos verticais como AXIS_Y eventos. Para o direcional analógico direito, o Android relata movimentos horizontais como AXIS_Z eventos e movimentos verticais como AXIS_RZ eventos. Verifique se os dois controles direcionais analógicos estão sendo processados no seu código.
  • Gerencie o pressionamento dos gatilhos superiores (e verifique se o jogo funciona com eventos AXIS_ e KEYCODE_BUTTON_). Alguns controles têm gatilhos superiores nas extremidades esquerda e direita. Quando esses gatilhos estão presentes, eles emitem um evento AXIS_*TRIGGER ou KEYCODE_BUTTON_*2, ou ambos. Para o gatilho esquerdo, seria AXIS_LTRIGGER e KEYCODE_BUTTON_L2. Para o gatilho direito, seria AXIS_RTRIGGER e KEYCODE_BUTTON_R2. Os eventos de eixo só ocorrem se o gatilho emitir um intervalo de valores entre 0 e 1, e alguns controles com saída analógica emitem eventos de botão além de eventos de eixo. Os jogos precisam oferecer suporte a eventos AXIS_ e KEYCODE_BUTTON_ para permanecerem compatíveis com todos os controles de jogos comuns, mas preferem o evento que faz mais sentido para a jogabilidade se um controle informar os dois. No Android 4.3 (nível 18 da API) e mais recentes, um controle que produz um AXIS_LTRIGGER também informa um valor idêntico para o AXIS_BRAKE eixo. O mesmo vale para AXIS_RTRIGGER e AXIS_GAS. O Android informa todos os pressionamentos de gatilhos analógicos com um valor normalizado de 0,0 (liberado) a 1,0 (totalmente pressionado).
  • Comportamentos e suporte específicos podem ser diferentes em ambientes emulados. Plataformas emuladas, como o Google Play Games, podem ter um comportamento ligeiramente diferente com base nos recursos do sistema operacional host. Por exemplo, alguns controles que emitem eventos AXIS_ e KEYCODE_BUTTON_ só emitem eventos AXIS_, e o suporte para alguns controles pode estar ausente.

Variantes comuns

Com a grande variedade de suporte que o Android oferece para controles, pode não ficar claro como criar e testar para verificar se o jogo funciona sem bugs entre a base de jogadores. Descobrimos que, apesar dessa variedade aparente, os fabricantes de controles em todo o mundo tendem a aderir de forma consistente a três estilos diferentes de controle. Alguns oferecem alternâncias de hardware entre dois ou mais deles.

Isso significa que você pode testar com apenas três controles entre sua equipe de desenvolvimento e continuar confiante de que o jogo pode ser jogado sem recorrer a listas de permissão e negação.

Tipos comuns de controle

O estilo mais comum de controles tende a imitar os layouts de consoles de jogos populares. Isso é estético nos rótulos e no layout dos botões e funcional pelos eventos gerados. Os controles com alternâncias de hardware entre diferentes tipos de console mudam os eventos enviados e, muitas vezes, até mesmo o layout lógico dos botões.

Ao testar, recomendamos validar se o jogo funciona com um controle em cada uma das categorias. Você pode testar com controles próprios ou fabricantes terceirizados populares. Geralmente, mapeamos os controles mais populares para a definição acima da melhor maneira possível.

Tipo de controle Diferenças de comportamento Variações de rótulos
Controles de estilo Xbox

Esses controles são normalmente feitos para a plataforma Microsoft Xbox e Windows* .

Esses controladores correspondem ao conjunto de atributos descrito em Processar entradas do controlador Os botões L2/R2 nesses controles são rotulados como LT/RT.
Controles de estilo Switch

Esses controles são normalmente projetados para a família de consoles Nintendo Switch* .

Esses controles enviam os KeyEvent KEYCODE_BUTTON_R2 KEYCODE_BUTTON_L2 MotionEvents. Os botões L2/R2 nesses controles são rotulados como ZL/ZR.

Esses controles também trocam os botões A e B e os botões X e Y, de modo que KEYCODE_BUTTON_A é o botão rotulado como B e vice-versa.

Controles de estilo PlayStation

Esses controles são normalmente projetados para a família de consoles Sony PlayStation* .

Esses controles enviam MotionEvents como os controles de estilo Xbox, mas também enviam KeyEvents como os controles de estilo Switch quando totalmente pressionados. Esses controles usam um conjunto diferente de glifos para botões frontais.

* Microsoft, Xbox e Windows são marcas registradas da Microsoft; Nintendo Switch é uma marca registrada da Nintendo of America Inc.; PlayStation é uma marca registrada da Sony Interactive Entertainment Inc.

Desambiguar botões de gatilho

Alguns controles enviam AXIS_LTRIGGER e AXIS_RTRIGGER, alguns enviam KEYCODE_BUTTON_L2 e KEYCODE_BUTTON_R2, e outros enviam todos esses eventos com base nos recursos de hardware. Maximize a compatibilidade oferecendo suporte a todos esses eventos.

Todos os controles que enviam AXIS_LTRIGGER também enviam AXIS_BRAKE, da mesma forma que AXIS_RTRIGGER e AXIS_GAS, para ajudar a maximizar a compatibilidade entre volantes de corrida e controles de jogos típicos. Geralmente, isso não causa problemas, mas esteja ciente de recursos como telas de remapeamento de teclas.

Gatilho MotionEvent KeyEvent
Gatilho esquerdo AXIS_LTRIGGER
AXIS_BRAKE
KEYCODE_BUTTON_L2
Gatilho direito AXIS_RTRIGGER
AXIS_GAS
KEYCODE_BUTTON_R2

É necessário verificar se o jogo pode processar KeyEvent e MotionEvent para manter a compatibilidade com o maior número possível de controles e se os eventos são desduplicados.

Controles compatíveis

Ao testar, recomendamos validar se o jogo funciona com um controle em cada uma das categorias.

  • Estilo Xbox
  • Estilo Nintendo Switch
  • Estilo PlayStation

Você pode testar com controles próprios ou fabricantes terceirizados populares. Geralmente, mapeamos os controles mais populares para a definição da forma mais próxima possível.