Gérer les actions de la manette

Les contrôleurs 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 View qui comporte 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 les événements directement à partir de Activity.

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

Lorsqu'il signale 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 événement AXIS_X qui représente la coordonnée X de la surface tactile, tandis qu'une manette de jeu génère un événement AXIS_X qui représente la position X du stick 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 fonction supportsSource(int) :

  • Un type de source SOURCE_GAMEPAD indique que le périphérique d'entrée comporte des boutons de contrôle (par exemple, KEYCODE_BUTTON_A). Notez que ce type de source n'indique pas précisément 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 directionnels (par exemple, DPAD_UP).
  • Un type de source SOURCE_JOYSTICK indique que le périphérique d'entrée est équipé de joysticks analogiques (par exemple, un joystick qui enregistre les mouvements le long des axes 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 tel est le cas, la méthode récupère les ID d'appareil des 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 Assurer la compatibilité avec 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 du contrôleur

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

Les développeurs C++ doivent utiliser la bibliothèque Game Controller. Il unifie tous les contrôleurs au sous-ensemble de fonctionnalités le plus courant et fournit une interface cohérente entre eux, 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 pavé directionnel, sticks analogiques et boutons
Figure 1 : Profil pour une manette de jeu générique.

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

Saisie à la manette KeyEvent 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. Stick 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 appuis sur les boutons de la manette de la même manière que les appuis sur les touches du clavier, vous devez :

  • 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 touche répétés 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 HAUT et 1.0 indiquant BAS. Il signale les appuis sur la croix directionnelle GAUCHE ou DROITE comme des événements AXIS_HAT_X, avec -1.0 indiquant la gauche et 1.0 indiquant la droite.

Certains contrôleurs signalent les pressions sur le pavé directionnel avec un code clé. Si votre jeu se soucie des pressions sur le pavé directionnel, vous devez traiter les événements de l'axe du 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 touches du pavé directionnel et les valeurs des axes du chapeau.

Action de jeu Code de touche du pavé directionnel Code Hat Axis
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 à gauche KEYCODE_DPAD_LEFT AXIS_HAT_X (pour les valeurs comprises entre 0 et -1,0)
Déplacer à 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 de l'axe du chapeau et du code clé 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 rappels onGenericMotionEvent() ou onKeyDown()).

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 leurs manettes de jeu, Android signale un MotionEvent qui contient le code d'action ACTION_MOVE 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 échantillons de mouvement dans un seul objet. L'objet MotionEvent contient la position actuelle de chaque axe du joystick, ainsi que plusieurs positions historiques pour chacun d'eux. Lorsque vous signalez des événements de mouvement avec le code d'action ACTION_MOVE (comme les mouvements du joystick), Android regroupe les valeurs des axes pour plus d'efficacité. Les valeurs historiques d'un axe se composent de l'ensemble des valeurs distinctes antérieures à la valeur actuelle de l'axe et plus récentes que les valeurs signalées dans les événements de mouvement précédents. Pour en savoir plus, consultez la documentation de référence sur MotionEvent.

Pour afficher précisément le mouvement d'un objet de jeu en fonction des commandes 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 rappel onGenericMotionEvent() pour traiter les entrées 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 les mouvements de son 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 de l'axe signalée par Android se situe dans la zone plate, vous devez considérer que le contrôleur est au repos (c'est-à-dire immobile le long des deux axes).

L'extrait montre une méthode d'assistance qui calcule le mouvement le long de chaque axe. Vous appelez cet utilitaire 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;
}

En combinant tous ces éléments, voici comment vous pouvez 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 simple joystick, suivez ces bonnes pratiques :

  • Gérer les deux joysticks de la manette. De nombreuses manettes de jeu sont équipées d'un joystick gauche et d'un joystick droit. Pour le joystick gauche, Android signale les mouvements horizontaux comme des événements AXIS_X et les mouvements verticaux comme des événements AXIS_Y. Pour le joystick droit, Android signale les mouvements horizontaux comme des événements AXIS_Z et les mouvements verticaux comme des événements AXIS_RZ. Assurez-vous de gérer les deux joysticks de la manette dans votre code.
  • Gérez les pressions sur les gâchettes (et assurez-vous que votre jeu fonctionne avec les événements AXIS_ et KEYCODE_BUTTON_). Certains contrôleurs sont dotés de boutons de tranche gauche et droite. Lorsque ces déclencheurs sont présents, ils émettent un événement AXIS_*TRIGGER ou KEYCODE_BUTTON_*2, ou les deux. Pour le déclencheur gauche, il s'agit de AXIS_LTRIGGER et KEYCODE_BUTTON_L2. Pour le bouton de tranche droit, il s'agit de AXIS_RTRIGGER et KEYCODE_BUTTON_R2. Les événements d'axe ne se produisent que si le déclencheur émet une plage de valeurs comprise entre 0 et 1. Certains contrôleurs 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. Toutefois, si une manette signale les deux, privilégiez l'événement qui a le plus de sens pour votre gameplay. Sur Android 4.3 (niveau d'API 18) et versions ultérieures, un contrôleur 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 boutons de déclenchement analogiques avec une valeur normalisée comprise entre 0.0 (relâché) et 1.0 (complètement enfoncé).
  • Les comportements et l'assistance spécifiques peuvent varier dans les environnements émulés. Le comportement des plates-formes émulées, telles que Google Play Jeux, peut varier légèrement en fonction des capacités du système d'exploitation hôte. Par exemple, certains contrôleurs qui émettent des événements AXIS_ et KEYCODE_BUTTON_ n'émettent que des événements AXIS_, et la prise en charge de certains contrôleurs peut être totalement manquante.

Variantes courantes

Étant donné la grande variété de manettes compatibles avec Android, il peut être difficile de savoir comment créer et tester votre jeu pour vérifier qu'il fonctionne sans bug pour votre base de joueurs. Nous avons constaté que, malgré cette apparente variété, les fabricants de manettes du monde entier ont tendance à s'en tenir à trois styles de manettes différents. Certains appareils proposent des boutons matériels pour basculer entre deux ou plusieurs de ces modes.

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

Types de contrôleurs courants

Le style de contrôleurs le plus courant tend à imiter la mise en page des consoles de jeu populaires. Cela concerne à la fois l'esthétique des libellés et de la mise en page des boutons, et la fonctionnalité des événements déclenchés. Les manettes avec des boutons matériels permettant de basculer entre différents types de consoles modifient les événements qu'elles envoient et souvent même la disposition logique de leurs boutons.

Lors des tests, nous vous recommandons de vérifier que votre jeu fonctionne avec un contrôleur de chaque catégorie. Vous pouvez choisir de tester avec des contrôleurs propriétaires ou des fabricants tiers populaires. En général, nous mappons les manettes les plus populaires à la définition ci-dessus au mieux de nos capacités.

Type de contrôleur Différences de comportement Variantes de libellés
Manettes de style Xbox

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

Ces contrôleurs correspondent à l'ensemble de fonctionnalités décrit dans Traiter les entrées du contrôleur. Les boutons L2/R2 de ces manettes sont appelés LT/RT.
Manettes de style Switch

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

Ces contrôleurs envoient les KeyEvent KEYCODE_BUTTON_R2 KEYCODE_BUTTON_L2 MotionEvent Les boutons L2/R2 de ces manettes sont appelés ZL/ZR.

Ces manettes inversent également les boutons A et B, ainsi que les boutons X et Y. Par conséquent, KEYCODE_BUTTON_A correspond au bouton 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 MotionEvent, comme les manettes de style Xbox, mais aussi des KeyEvent, 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 du dessus.

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

Désambiguïser les boutons de déclenchement

Certains contrôleurs 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. Maximisez la compatibilité en prenant en charge tous ces événements.

Toutes les manettes qui envoient AXIS_LTRIGGER envoient également AXIS_BRAKE, de même pour AXIS_RTRIGGER et AXIS_GAS, afin de maximiser 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 vigilant pour les fonctionnalités telles que les écrans de remappage des touches.

Trigger MotionEvent KeyEvent
Gâchette gauche AXIS_LTRIGGER
AXIS_BRAKE
KEYCODE_BUTTON_L2
Gâchette de droite AXIS_RTRIGGER
AXIS_GAS
KEYCODE_BUTTON_R2

Veillez à vérifier que votre jeu peut gérer à la fois KeyEvent et MotionEvent pour maintenir la compatibilité avec le plus grand nombre de manettes possible et que les événements ne sont pas dupliqués.

Manettes compatibles

Lors des tests, nous vous recommandons de vérifier que votre jeu fonctionne avec un contrôleur de chaque catégorie.

  • Style Xbox
  • Style Nintendo Switch
  • Style PlayStation

Vous pouvez effectuer des tests avec des manettes first party ou des fabricants tiers populaires. Nous mappons généralement les manettes les plus populaires à la définition aussi précisément que possible.