Découvrez comment utiliser l'API Gamepad pour faire passer vos jeux Web au niveau supérieur.
L'œuf de Pâques de la page hors connexion de Chrome est l'un des secrets les moins bien gardés de l'histoire ([citation needed]
, mais cette affirmation est faite pour l'effet dramatique). Si vous appuyez sur la touche Espace ou, sur les appareils mobiles, appuyez sur le dinosaure, la page hors connexion devient un jeu d'arcade jouable. Vous savez peut-être que vous n'avez pas besoin de passer en mode hors connexion lorsque vous avez envie de jouer: dans Chrome, vous pouvez simplement accéder à about://dino
ou, pour les geeks, à about://network-error/-106
. Mais saviez-vous que 270 millions de parties du jeu du dino Chrome sont jouées chaque mois ?
Un autre fait qui est peut-être plus utile à connaître et que vous ne connaissez peut-être pas est que, en mode arcade, vous pouvez jouer au jeu avec une manette de jeu. La prise en charge des manettes de jeu a été ajoutée il y a environ un an, au moment de la rédaction de cet article, dans un commit de Reilly Grant. Comme vous pouvez le constater, le jeu, tout comme le reste du projet Chromium, est entièrement Open Source. Dans cet article, je vais vous montrer comment utiliser l'API Gamepad.
Utiliser l'API Gamepad
Détection des fonctionnalités et compatibilité avec les navigateurs
L'API Gamepad est universellement compatible avec les navigateurs, que ce soit sur ordinateur ou sur mobile. Vous pouvez détecter si l'API Gamepad est compatible à l'aide de l'extrait de code suivant:
if ('getGamepads' in navigator) {
// The API is supported!
}
Comment le navigateur représente une manette de jeu
Le navigateur représente les manettes de jeu sous la forme d'objets Gamepad
. Un Gamepad
a les propriétés suivantes:
id
: chaîne d'identification de la manette. Cette chaîne identifie la marque ou le style de la manette de jeu connectée.displayId
:VRDisplay.displayId
d'unVRDisplay
associé (le cas échéant).index
: index de la manette dans le navigateur.connected
: indique si la manette de jeu est toujours connectée au système.hand
: énumération définissant la main dans laquelle le contrôleur est tenu ou est le plus susceptible d'être tenu.timestamp
: date de la dernière mise à jour des données de cette manettemapping
: mappage des boutons et des axes utilisé pour cet appareil ("standard"
ou"xr-standard"
).pose
: objetGamepadPose
représentant les informations sur la position associées à un contrôleur WebVR.axes
: tableau de valeurs pour tous les axes de la manette, normalisé linéairement dans la plage-1.0
à1.0
.buttons
: tableau d'états des boutons pour tous les boutons de la manette.
Notez que les boutons peuvent être numériques (poussés ou non) ou analogiques (par exemple, 78% de pression). C'est pourquoi les boutons sont signalés en tant qu'objets GamepadButton
, avec les attributs suivants:
pressed
: état de pression du bouton (true
si le bouton est enfoncé etfalse
s'il ne l'est pas).touched
: état du bouton lorsqu'il est touché. Si le bouton est capable de détecter le toucher, cette propriété esttrue
si le bouton est touché, etfalse
dans le cas contraire.value
: pour les boutons dotés d'un capteur analogique, cette propriété représente la quantité de pression sur le bouton, normalisée linéairement dans la plage0.0
à1.0
.hapticActuators
: tableau contenant des objetsGamepadHapticActuator
, chacun représentant du matériel de retour haptique disponible sur le contrôleur.
En fonction de votre navigateur et de la manette de jeu que vous possédez, vous pouvez également rencontrer une propriété vibrationActuator
. Il permet deux types d'effets de vibration:
- Dual-Rumble: effet de retour haptique généré par deux actionneurs à masse rotative excentrique, un dans chaque poignée de la manette de jeu.
- Trigger-Rumble: effet de retour haptique généré par deux moteurs indépendants, un dans chacun des déclencheurs de la manette de jeu.
La vue schématique suivante, tirée directement de la spécification, montre la mise en correspondance et la disposition des boutons et des axes sur une manette de jeu générique.
Notification lorsqu'une manette de jeu est connectée
Pour savoir quand une manette de jeu est connectée, écoutez l'événement gamepadconnected
qui se déclenche sur l'objet window
. Lorsque l'utilisateur connecte une manette, ce qui peut se faire via USB ou Bluetooth, un GamepadEvent
est déclenché, qui contient les détails de la manette dans une propriété gamepad
bien nommée.
Vous trouverez ci-dessous un exemple d'une manette Xbox 360 que j'avais sous la main (oui, je suis un fan de jeux rétro).
window.addEventListener('gamepadconnected', (event) => {
console.log('✅ 🎮 A gamepad was connected:', event.gamepad);
/*
gamepad: Gamepad
axes: (4) [0, 0, 0, 0]
buttons: (17) [GamepadButton, GamepadButton, GamepadButton, GamepadButton, GamepadButton, GamepadButton, GamepadButton, GamepadButton, GamepadButton, GamepadButton, GamepadButton, GamepadButton, GamepadButton, GamepadButton, GamepadButton, GamepadButton, GamepadButton]
connected: true
id: "Xbox 360 Controller (STANDARD GAMEPAD Vendor: 045e Product: 028e)"
index: 0
mapping: "standard"
timestamp: 6563054.284999998
vibrationActuator: GamepadHapticActuator {type: "dual-rumble"}
*/
});
Notification lorsqu'une manette de jeu est déconnectée
La notification de déconnexion d'une manette de jeu se produit de la même manière que la détection des connexions.
Cette fois, l'application écoute l'événement gamepaddisconnected
. Notez que dans l'exemple suivant, connected
est maintenant false
lorsque je débranche la manette Xbox 360.
window.addEventListener('gamepaddisconnected', (event) => {
console.log('❌ 🎮 A gamepad was disconnected:', event.gamepad);
/*
gamepad: Gamepad
axes: (4) [0, 0, 0, 0]
buttons: (17) [GamepadButton, GamepadButton, GamepadButton, GamepadButton, GamepadButton, GamepadButton, GamepadButton, GamepadButton, GamepadButton, GamepadButton, GamepadButton, GamepadButton, GamepadButton, GamepadButton, GamepadButton, GamepadButton, GamepadButton]
connected: false
id: "Xbox 360 Controller (STANDARD GAMEPAD Vendor: 045e Product: 028e)"
index: 0
mapping: "standard"
timestamp: 6563054.284999998
vibrationActuator: null
*/
});
La manette de jeu dans votre boucle de jeu
L'accès à une manette de jeu commence par un appel à navigator.getGamepads()
, qui renvoie un tableau d'éléments Gamepad
. Dans Chrome, le tableau a toujours une longueur fixe de quatre éléments. Si aucun ou moins de quatre manettes de jeu ne sont connectées, un élément peut simplement être null
. Veillez toujours à vérifier tous les éléments du tableau et gardez à l'esprit que les manettes de jeu "se souviennent" de leur emplacement et qu'elles ne sont pas toujours présentes dans le premier emplacement disponible.
// When no gamepads are connected:
navigator.getGamepads();
// (4) [null, null, null, null]
Si un ou plusieurs manettes de jeu sont connectés, mais que navigator.getGamepads()
continue de signaler des éléments null
, vous devrez peut-être "réveiller" chaque manette de jeu en appuyant sur l'un de ses boutons. Vous pouvez ensuite interroger les états de la manette dans votre boucle de jeu, comme indiqué dans le code suivant.
const pollGamepads = () => {
// Always call `navigator.getGamepads()` inside of
// the game loop, not outside.
const gamepads = navigator.getGamepads();
for (const gamepad of gamepads) {
// Disregard empty slots.
if (!gamepad) {
continue;
}
// Process the gamepad state.
console.log(gamepad);
}
// Call yourself upon the next animation frame.
// (Typically this happens every 60 times per second.)
window.requestAnimationFrame(pollGamepads);
};
// Kick off the initial game loop iteration.
pollGamepads();
L'actionneur de vibration
La propriété vibrationActuator
renvoie un objet GamepadHapticActuator
, qui correspond à une configuration de moteurs ou d'autres actionneurs pouvant appliquer une force à des fins de retour haptique. Vous pouvez lire des effets haptiques en appelant Gamepad.vibrationActuator.playEffect()
. Les seuls types d'effets valides sont 'dual-rumble'
et 'trigger-rumble'
.
Effets de vibration compatibles
if (gamepad.vibrationActuator.effects.includes('trigger-rumble')) {
// Trigger rumble supported.
} else if (gamepad.vibrationActuator.effects.includes('dual-rumble')) {
// Dual rumble supported.
} else {
// Rumble effects aren't supported.
}
Double retour haptique
La double vibration décrit une configuration haptique avec un moteur de vibration à masse rotative excentrique dans chaque poignée d'une manette de jeu standard. Dans cette configuration, l'un des moteurs peut faire vibrer l'ensemble de la manette. Les deux masses sont inégales afin que les effets de chacune puissent être combinés pour créer des effets haptiques plus complexes. Les effets de double vibreur sont définis par quatre paramètres:
duration
: définit la durée de l'effet de vibration en millisecondes.startDelay
: définit la durée du délai avant le début de la vibration.strongMagnitude
etweakMagnitude
: définissez les niveaux d'intensité des vibrations pour les moteurs à masse excentrique rotative plus lourds et plus légers, normalisés à la plage0.0
–1.0
.
// This assumes a `Gamepad` as the value of the `gamepad` variable.
const dualRumble = (gamepad, delay = 0, duration = 100, weak = 1.0, strong = 1.0) => {
if (!('vibrationActuator' in gamepad)) {
return;
}
gamepad.vibrationActuator.playEffect('dual-rumble', {
// Start delay in ms.
startDelay: delay,
// Duration in ms.
duration: duration,
// The magnitude of the weak actuator (between 0 and 1).
weakMagnitude: weak,
// The magnitude of the strong actuator (between 0 and 1).
strongMagnitude: strong,
});
};
Déclencher le retour haptique
Le retour haptique des déclencheurs est l'effet généré par deux moteurs indépendants, un dans chacun des déclencheurs de la manette.
// This assumes a `Gamepad` as the value of the `gamepad` variable.
const triggerRumble = (gamepad, delay = 0, duration = 100, weak = 1.0, strong = 1.0) => {
if (!('vibrationActuator' in gamepad)) {
return;
}
// Feature detection.
if (!('effects' in gamepad.vibrationActuator) || !gamepad.vibrationActuator.effects.includes('trigger-rumble')) {
return;
}
gamepad.vibrationActuator.playEffect('trigger-rumble', {
// Duration in ms.
duration: duration,
// The left trigger (between 0 and 1).
leftTrigger: leftTrigger,
// The right trigger (between 0 and 1).
rightTrigger: rightTrigger,
});
};
Intégration avec les règles sur les autorisations
La spécification de l'API Gamepad définit une fonctionnalité contrôlée par les règles identifiée par la chaîne "gamepad"
. La valeur allowlist
par défaut est "self"
. La stratégie d'autorisations d'un document détermine si un contenu de ce document est autorisé à accéder à navigator.getGamepads()
. Si cette option est désactivée dans un document, aucun contenu du document ne sera autorisé à utiliser navigator.getGamepads()
, et les événements gamepadconnected
et gamepaddisconnected
ne seront pas déclenchés.
<iframe src="index.html" allow="gamepad"></iframe>
Démo
Une démo de testeur de manette de jeu est intégrée à l'exemple suivant. Le code source est disponible sur Glitch. Essayez la démonstration en connectant une manette à l'aide d'un câble USB ou du Bluetooth, puis en appuyant sur l'un de ses boutons ou en déplaçant l'une de ses axes.
Bonus: jouer au dino Chrome sur web.dev
Vous pouvez jouer au dinosaure Chrome avec votre manette de jeu sur ce site. Le code source est disponible sur GitHub.
Consultez l'implémentation du sondage de la manette de jeu dans trex-runner.js
et notez comment elle émule les pressions sur les touches.
Pour que la démonstration de la manette de jeu Chrome Dino fonctionne, j'ai extrait le jeu du dinosaure Chrome du projet Chromium principal (en mettant à jour un effort antérieur de Arnelle Ballane), l'ai placé sur un site autonome, étendu l'implémentation de l'API de manette de jeu existante en ajoutant des effets de masquage et de vibration, créé un mode plein écran, et Mehul Satardekar a contribué à l'implémentation du mode sombre. Bon jeu !
Liens utiles
Remerciements
Ce document a été examiné par François Beaufort et Joe Medley. La spécification de l'API Gamepad est éditée par Steve Agoston, James Hollyer et Matt Reynolds. Les anciens éditeurs de spécifications sont Brandon Jones, Scott Graham et Ted Mielczarek. La spécification des extensions de manette est éditée par Brandon Jones. Image principale par Laura Torrent Puig.