Gérer les actions de la manette

Les manettes comportent deux types d'actions :

  • KeyEvent utilisé pour tout bouton avec un état binaire "activé" et "désactivé"
  • MotionEvent utilisé pour tout axe qui renvoie une plage de valeurs. Par exemple, de -1 à 1 pour les joysticks analogiques ou de 0 à 1 pour les gâchettes analogiques.

Vous pouvez lire ces entrées à partir de la View qui a le 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);
}

Si nécessaire, vous pouvez lire directement les événements à partir de Activity.

Vérifier qu'une manette de jeu est connectée

Lors du signalement des événements d'entrée, Android réutilise les mêmes ID de clé ou d'axe pour différents types de périphériques d'entrée. Par exemple, une action sur un écran tactile génère un AXIS_X événement qui représente la coordonnée X de la surface tactile, mais une manette de jeu génère un AXIS_X événement qui représente la position X du joystick gauche. Cela signifie que vous devez vérifier le type de source pour interpréter correctement les événements d'entrée.

Pour vérifier qu'un InputDevice connecté est une manette de jeu, utilisez la supportsSource(int) fonction :

  • Un type de source SOURCE_GAMEPAD indique que le périphérique d'entrée comporte des boutons de manette (par exemple, KEYCODE_BUTTON_A). Notez que ce type de source n'indique pas strictement si la manette de jeu comporte des boutons de pavé directionnel, bien que la plupart des manettes disposent généralement de commandes directionnelles.
  • Un type de source SOURCE_DPAD indique que le périphérique d'entrée comporte des boutons de pavé directionnel (par exemple, DPAD_UP).
  • Un type de source SOURCE_JOYSTICK indique que le périphérique d'entrée comporte des joysticks analogiques (par exemple, un joystick qui enregistre les mouvements le long de AXIS_X et AXIS_Y).

L'extrait de code suivant montre une méthode d'assistance qui vous permet de vérifier si les périphériques d'entrée connectés sont des manettes de jeu. Si c'est le cas, la méthode récupère les ID d'appareil pour les manettes de jeu. Vous pouvez ensuite associer chaque ID d'appareil à un joueur de votre jeu et traiter les actions de jeu pour chaque joueur connecté séparément. Pour en savoir plus sur la prise en charge de plusieurs manettes de jeu connectées simultanément sur le même appareil Android, consultez la section Prise en charge de plusieurs manettes de jeu.

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

Traiter les entrées de la manette

Cette section décrit les types de manettes de jeu compatibles avec Android.

Les développeurs C++ doivent utiliser la bibliothèque Game Controller. Elle unifie toutes les manettes dans le sous-ensemble de fonctionnalités le plus courant et fournit une interface cohérente entre elles, y compris la possibilité de détecter la disposition des boutons.

Cette figure montre à quoi un développeur de jeux Android peut s'attendre à ce qu'une manette courante ressemble sur Android.

Manette de jeu générique avec entrées étiquetées, y compris le pavé directionnel, les sticks analogiques et les boutons
Figure 1. Profil d'une manette de jeu générique.

Le tableau répertorie les noms et types d'événements standards pour les manettes de jeu. Pour obtenir la liste complète des événements, consultez la section Variantes courantes. Le système envoie des événements MotionEvent via onGenericMotionEvent et des événements KeyEvent via onKeyDown et onKeyUp.

Entrée de la manette Événement de touches MotionEvent
1. Pavé directionnel
AXIS_HAT_X
(entrée horizontale)
AXIS_HAT_Y
(entrée verticale)
2. Joystick analogique gauche
KEYCODE_BUTTON_THUMBL
(lorsqu'il est enfoncé)
AXIS_X
(mouvement horizontal)
AXIS_Y
(mouvement vertical)
3. Joystick analogique droit
KEYCODE_BUTTON_THUMBR
(lorsqu'il est enfoncé)
AXIS_Z
(mouvement horizontal)
AXIS_RZ
(mouvement vertical)
4. Bouton X KEYCODE_BUTTON_X
5. Bouton A KEYCODE_BUTTON_A
6. Bouton Y KEYCODE_BUTTON_Y
7. Bouton B KEYCODE_BUTTON_B
8. Gâchette haute droite
KEYCODE_BUTTON_R1
9. Gâchette droite
AXIS_RTRIGGER
10. Gâchette gauche AXIS_LTRIGGER
11. Gâchette haute gauche KEYCODE_BUTTON_L1
12. Démarrer KEYCODE_BUTTON_START
13. Sélectionner KEYCODE_BUTTON_SELECT

Gérer les pressions sur les boutons

Étant donné qu'Android signale les pressions sur les boutons de la manette de la même manière que les pressions sur les boutons du clavier, vous devez procéder comme suit :

  • Vérifiez que l'événement provient d'un SOURCE_GAMEPAD.
  • Assurez-vous de ne recevoir le bouton qu'une seule fois avec KeyEvent.getRepeatCount(), Android enverra des événements de touches répétées comme si vous mainteniez une touche du clavier enfoncée.
  • Indiquez qu'un événement est géré en renvoyant true.
  • Transmettez les événements non gérés à super pour vérifier que les différentes couches de compatibilité d'Android fonctionnent correctement.

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

Traiter les entrées du pavé directionnel

Le pavé directionnel à quatre directions est une commande physique courante dans de nombreuses manettes de jeu. Android signale les pressions sur les boutons HAUT et BAS du pavé directionnel comme des événements AXIS_HAT_Y, avec -1,0 indiquant le haut et 1,0 indiquant le bas. Il signale les pressions sur les boutons GAUCHE ou DROITE du pavé directionnel comme des événements AXIS_HAT_X, avec -1,0 indiquant la gauche et 1,0 indiquant la droite.

Certaines manettes signalent plutôt les pressions sur le pavé directionnel avec un code de touche. Si votre jeu se soucie des pressions sur le pavé directionnel, vous devez traiter les événements d'axe de chapeau et les codes de touche du pavé directionnel comme les mêmes événements d'entrée, comme recommandé dans le tableau 2.

Tableau 2. Actions de jeu par défaut recommandées pour les codes de touche du pavé directionnel et les valeurs d'axe de chapeau.

Action de jeu Code de touche du pavé directionnel Code d'axe de chapeau
Monter KEYCODE_DPAD_UP AXIS_HAT_Y (pour les valeurs comprises entre 0 et -1,0)
Descendre KEYCODE_DPAD_DOWN AXIS_HAT_Y (pour les valeurs comprises entre 0 et 1,0)
Déplacer vers la gauche KEYCODE_DPAD_LEFT AXIS_HAT_X (pour les valeurs comprises entre 0 et -1,0)
Déplacer vers la droite KEYCODE_DPAD_RIGHT AXIS_HAT_X (pour les valeurs comprises entre 0 et 1,0)

L'extrait de code suivant montre une classe d'assistance qui vous permet de vérifier les valeurs d'axe de chapeau et de code de touche à partir d'un événement d'entrée pour déterminer la direction du pavé directionnel.

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

Vous pouvez utiliser cette classe d'assistance dans votre jeu chaque fois que vous souhaitez traiter les entrées du pavé directionnel (par exemple, dans les onGenericMotionEvent() ou onKeyDown() rappels).

Exemple :

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

Traiter les mouvements du joystick

Lorsque les joueurs déplacent un joystick sur leur manette de jeu, Android signale un MotionEvent qui contient le ACTION_MOVE code d'action et les positions mises à jour des axes du joystick. Votre jeu peut utiliser les données fournies par MotionEvent pour déterminer si un mouvement de joystick qui l'intéresse s'est produit.

Notez que les événements de mouvement du joystick peuvent regrouper plusieurs exemples de mouvement dans un seul objet. L' MotionEvent objet contient la position actuelle de chaque axe du joystick, ainsi que plusieurs positions historiques pour chaque axe. Lors du signalement des événements de mouvement avec le code d'action ACTION_MOVE (par exemple, les mouvements du joystick), Android regroupe les valeurs d'axe pour plus d'efficacité. Les valeurs historiques d'un axe sont constituées de l'ensemble des valeurs distinctes antérieures à la valeur d'axe actuelle et plus récentes que les valeurs signalées dans les événements de mouvement précédents. Pour en savoir plus, consultez la MotionEvent référence.

Pour afficher avec précision le mouvement d'un objet de jeu en fonction de l'entrée du joystick, vous pouvez utiliser les informations historiques fournies par les objets MotionEvent.

Vous pouvez récupérer les valeurs actuelles et historiques à l'aide des méthodes suivantes :

L'extrait suivant montre comment remplacer le onGenericMotionEvent() rappel pour traiter l'entrée du joystick. Vous devez d'abord traiter les valeurs historiques d'un axe, puis sa position actuelle.

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

Avant d'utiliser l'entrée du joystick, vous devez déterminer si le joystick est centré, puis calculer ses mouvements d'axe en conséquence. Les joysticks ont généralement une zone plate, c'est-à-dire une plage de valeurs proche de la coordonnée (0,0) à laquelle l'axe est considéré comme centré. Si la valeur d'axe signalée par Android se situe dans la zone plate, vous devez considérer que la manette est au repos (c'est-à-dire immobile sur les deux axes).

L'extrait montre une méthode d'assistance qui calcule le mouvement le long de chaque axe. Vous appelez cette assistance dans la méthode processJoystickInput() décrite plus loin dans l'exemple suivant :

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

Pour résumer, voici comment traiter les mouvements du joystick dans votre jeu :

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
}

Pour prendre en charge les manettes de jeu dotées de fonctionnalités plus sophistiquées qu'un seul joystick, suivez ces bonnes pratiques :

  • Gérez les deux joysticks de la manette. De nombreuses manettes de jeu comportent un joystick gauche et un joystick droit. Pour le joystick gauche, Android signale les mouvements horizontaux comme des AXIS_X événements et les mouvements verticaux comme AXIS_Y événements. Pour le joystick droit, Android signale les mouvements horizontaux comme des AXIS_Z événements et les mouvements verticaux comme des AXIS_RZ événements. Assurez-vous de gérer les deux joysticks de la manette dans votre code.
  • Gérez les pressions sur les gâchettes d'épaule (et assurez-vous que votre jeu fonctionne avec les événements AXIS_ et KEYCODE_BUTTON_). Certaines manettes comportent des gâchettes gauche et droite. Lorsque ces gâchettes sont présentes, elles émettent un événement AXIS_*TRIGGER ou KEYCODE_BUTTON_*2, ou les deux. Pour la gâchette gauche, il s'agit de AXIS_LTRIGGER et KEYCODE_BUTTON_L2. Pour la gâchette droite, il s'agit de AXIS_RTRIGGER et KEYCODE_BUTTON_R2. Les événements d'axe ne se produisent que si la gâchette émet une plage de valeurs comprise entre 0 et 1, et certaines manettes avec sortie analogique émettent des événements de bouton en plus des événements d'axe. Les jeux doivent prendre en charge les événements AXIS_ et KEYCODE_BUTTON_ pour rester compatibles avec toutes les manettes de jeu courantes, mais privilégiez l'événement le plus pertinent pour votre gameplay si une manette signale les deux. Sur Android 4.3 (niveau d'API 18) ou version ultérieure, une manette qui produit un AXIS_LTRIGGER signale également une valeur identique pour l'axe AXIS_BRAKE. Il en va de même pour AXIS_RTRIGGER et AXIS_GAS. Android signale toutes les pressions sur les gâchettes analogiques avec une valeur normalisée comprise entre 0,0 (relâchée) et 1,0 (complètement enfoncée).
  • Les comportements et la compatibilité spécifiques peuvent différer dans les environnements émulés. Les plates-formes émulées, telles que Google Play Games, peuvent légèrement différer en termes de comportement en fonction des fonctionnalités du système d'exploitation hôte. Par exemple, certaines manettes qui émettent à la fois des événements AXIS_ et KEYCODE_BUTTON_ n'émettent que des événements AXIS_, et la compatibilité avec certaines manettes peut être totalement absente.

Variantes courantes

Compte tenu de la grande variété de manettes compatibles avec Android, il peut être difficile de créer et de tester votre jeu pour vérifier qu'il fonctionne sans bug pour votre base de joueurs. Nous constatons que, malgré cette variété apparente, les fabricants de manettes du monde entier ont tendance à respecter trois styles de manettes différents. Certains proposent des commutateurs matériels entre deux ou plusieurs de ces styles.

Cela signifie que vous pouvez tester votre jeu avec seulement trois manettes au sein de votre équipe de développement et être sûr qu'il est jouable sans avoir recours à des listes d'autorisation et de refus.

Types de manettes courants

Le style de manettes le plus courant a tendance à imiter la disposition des consoles de jeu populaires. Cela est à la fois esthétique dans les libellés et la disposition des boutons, et fonctionnel par les événements déclenchés. Les manettes avec commutateurs matériels entre différents types de consoles modifient les événements qu'elles envoient et souvent même leur disposition logique des boutons.

Lors des tests, nous vous recommandons de vérifier que votre jeu fonctionne avec une manette dans chacune des catégories. Vous pouvez choisir de tester avec des manettes propriétaires ou des fabricants tiers populaires. En général, nous mappons les manettes les plus populaires sur la définition ci-dessus au mieux de nos capacités.

Type de manette Différences de comportement Variations de libellés
Manettes de style Xbox

Il s'agit généralement de manettes conçues pour la plate-forme Microsoft Xbox et Windows*

Ces manettes correspondent à l'ensemble de caractéristiques décrit dans la section Traiter les entrées de la manette. Les boutons L2/R2 de ces manettes sont libellés LT/RT.
Manettes de style Switch

Ces manettes sont généralement conçues pour la famille de consoles Nintendo Switch*

Ces manettes envoient les KeyEvent KEYCODE_BUTTON_R2 KEYCODE_BUTTON_L2 MotionEvents. Les boutons L2/R2 de ces manettes sont libellés ZL/ZR.

Ces manettes inversent également les boutons A et B ainsi que les boutons X et Y. Ainsi, KEYCODE_BUTTON_A est le bouton libellé B et inversement.

Manettes de style PlayStation

Ces manettes sont généralement conçues pour la famille de consoles Sony PlayStation* .

Ces manettes envoient des MotionEvents comme les manettes de style Xbox, mais aussi des KeyEvents comme les manettes de style Switch lorsqu'elles sont complètement enfoncées. Ces manettes utilisent un ensemble de glyphes différent pour les boutons de la face avant.

* Microsoft, Xbox et Windows sont des marques déposées de Microsoft ; Nintendo Switch est une marque déposée de Nintendo of America Inc.; PlayStation est une marque déposée de Sony Interactive Entertainment Inc.

Distinguer les boutons de déclenchement

Certaines manettes envoient AXIS_LTRIGGER et AXIS_RTRIGGER, d'autres envoient KEYCODE_BUTTON_L2 et KEYCODE_BUTTON_R2, et d'autres encore envoient tous ces événements en fonction de leurs capacités matérielles. Optimisez la compatibilité en prenant en charge tous ces événements.

Toutes les manettes qui envoient AXIS_LTRIGGER envoient également AXIS_BRAKE, de même que AXIS_RTRIGGER et AXIS_GAS, afin d'optimiser la compatibilité entre les volants de course et les manettes de jeu classiques. En général, cela ne pose pas de problème, mais soyez attentif aux fonctionnalités telles que les écrans de remappage des touches.

Déclencheur MotionEvent KeyEvent
Gâchette gauche AXIS_LTRIGGER
AXIS_BRAKE
KEYCODE_BUTTON_L2
Gâchette droite AXIS_RTRIGGER
AXIS_GAS
KEYCODE_BUTTON_R2

Vous devez veiller à ce que votre jeu puisse gérer à la fois KeyEvent et MotionEvent pour maintenir la compatibilité avec le plus grand nombre de manettes possible et que les événements soient dédupliqués.

Manettes compatibles

Lors des tests, nous vous recommandons de vérifier que votre jeu fonctionne avec une manette dans chacune des catégories.

  • Style Xbox
  • Style Nintendo Switch
  • Style PlayStation

Vous pouvez tester avec des manettes propriétaires ou des fabricants tiers populaires. En général, nous mappons les manettes les plus populaires sur la définition aussi précisément que possible.