Gamepad API web : DirectInput, XInput et la confusion du mapping

Ce que t'offre la Gamepad API

La Gamepad API du navigateur expose les manettes connectées via navigator.getGamepads(), qui renvoie un tableau d'objets Gamepad. Chaque objet a un tableau buttons — chaque entrée est un objet avec pressed, touched et une value analogique de 0 à 1 — et un tableau axes contenant les positions des sticks analogiques, chacune de -1 à 1. Il y a des événements gamepadconnected et gamepaddisconnected, mais l'état des boutons et des axes ne t'est jamais poussé : tu dois l'interroger.

En pratique cela veut dire lire la manette dans une boucle requestAnimationFrame, une fois par image, et comparer cette image à la précédente pour détecter un bouton tout juste pressé ou relâché. L'objet que tu obtiens est un instantané figé, donc tu rappelles getGamepads() à chaque image pour des valeurs fraîches. Ce modèle d'interrogation est la première chose qui surprend ceux qui viennent des événements clavier ou pointeur.

Le « standard mapping »

La spécification définit une disposition canonique appelée standard mapping. Quand une manette y correspond, gamepad.mapping vaut la chaîne standard, et les index sont fixes et prévisibles : buttons[0] est le bouton frontal du bas (A / croix), buttons[1] celui de droite (B / rond), et les gâchettes hautes, gâchettes, croix directionnelle et clics de stick sont à des positions définies, axes[0] à axes[3] portant les sticks gauche et droit. Dix-sept boutons et quatre axes dans un ordre connu.

Quand gamepad.mapping est une chaîne vide, la manette ne correspond pas à la disposition standard et aucun de ces index n'est garanti. Le même bouton physique peut tomber à un autre index, une gâchette peut apparaître comme un axe, et la croix directionnelle peut être un unique axe de type hat au lieu de quatre boutons. La propriété mapping est le champ le plus important à vérifier avant de te fier à un index.

DirectInput contre XInput : la racine de la confusion

Sous Windows, un jeu parle à une manette de deux façons. XInput est l'API moderne de Microsoft, bâtie autour de la manette Xbox et de la disposition que suit toute manette compatible Xbox. Comme cette disposition est fixe, les navigateurs peuvent mapper proprement un appareil XInput sur le standard mapping — c'est pourquoi une manette Xbox « fonctionne tout de suite » dans un jeu web.

DirectInput est l'API plus ancienne et générique pour toute manette HID (périphérique d'interface humaine) : beaucoup de manettes tierces, sticks d'arcade, joysticks de vol, volants, et manettes anciennes ou sans marque. DirectInput n'impose aucun ordre de boutons fixe, donc le navigateur n'a pas de disposition canonique à mapper et signale un mapping vide. Cette seule différence est à l'origine de presque chaque rapport « mes boutons sont mélangés dans le navigateur ».

Pourquoi le même bouton signale des index différents

Avec une manette DirectInput, c'est le fabricant qui décide quel contrôle physique est le « bouton 1 », donc ton bouton A peut arriver en buttons[2], les gâchettes peuvent apparaître dans le tableau axes plutôt que comme boutons, et la croix directionnelle peut être signalée comme un hat de point de vue (POV) — un axe à huit directions — au lieu de quatre boutons distincts.

Pour rendre ces appareils utilisables, chaque navigateur applique ses propres heuristiques de remappage, et elles ne s'accordent pas toujours : la même manette DirectInput peut produire des index différents dans Chrome et Firefox. XInput contourne tout le problème car Microsoft a figé la disposition, c'est pourquoi « utilise une manette de type Xbox » est le conseil le plus fiable pour les jeux web.

Le gérer dans le code

Le schéma robuste est de vérifier d'abord gamepad.mapping. S'il vaut standard, tu peux utiliser des index nommés sans risque. S'il est vide, ne code rien en dur — propose un écran de remappage qui demande au joueur d'appuyer sur chaque action pour apprendre la disposition de cet appareil, et enregistre le résultat sous la clé gamepad.id pour qu'il persiste par manette.

Deux autres détails comptent quel que soit le mapping. Les sticks analogiques se reposent rarement exactement à zéro, alors applique une petite zone morte (souvent autour de 0.1) et traite tout ce qui est dedans comme aucune entrée, sinon la dérive déplacera le personnage tout seul. Et comme l'API est par interrogation, lis la manette à neuf chaque image et compare à l'image précédente pour transformer l'état continu en événements discrets « tout juste pressé ». Le testeur de manette de ce site montre les index de boutons et valeurs d'axes bruts que signale ton appareil — le moyen le plus rapide de voir s'il a le mapping standard.

À retenir

Vérifie toujours mapping avant de te fier aux index. Standard signifie la disposition de la spec ; vide signifie spécifique à l'appareil.

Préfère les manettes XInput / de type Xbox pour les jeux web — elles mappent vers la disposition standard, pas les DirectInput.

Ne code jamais d'index en dur pour les manettes non standard. Propose une UI de remappage et stocke le résultat par gamepad.id.

Applique une zone morte aux axes pour que les sticks au repos ne comptent pas comme un mouvement.

Interroge chaque image et compare — pour les boutons et axes, la Gamepad API te donne un état, pas des événements.