Hier erfahren Sie, wie Sie mit der Gamepad API Ihre Webspiele auf ein neues Level heben.
Das Easter Egg auf der Offlineseite von Chrome ist eines der schlechtesten gehüteten Geheimnisse der Geschichte ([citation needed]
,
dieser Anspruch wird jedoch nur aus dramatischen Gründen erhoben). Wenn Sie die Leertaste drücken oder auf Mobilgeräten auf den Dinosaurier tippen, wird die Offlineseite zu einem Arcade-Spiel. Sie wissen vielleicht, dass Sie nicht unbedingt offline gehen müssen, wenn Sie Lust auf ein Spiel haben: In Chrome können Sie einfach zu about://dino
oder, für die Geeks unter Ihnen, zu about://network-error/-106
gehen. Wussten Sie aber, dass jeden Monat 270 Millionen Chrome-Dino-Spiele gespielt werden?
Noch ein nützlicher Tipp, den du vielleicht noch nicht kennst: Im Arcade-Modus kannst du das Spiel mit einem Gamepad spielen. Die Unterstützung von Gamepads wurde vor etwa einem Jahr in einem commit von Reilly Grant hinzugefügt. Wie Sie sehen, ist das Spiel, genau wie der Rest des Chromium-Projekts, vollständig Open Source. In diesem Beitrag möchte ich Ihnen zeigen, wie Sie die Gamepad API verwenden.
Gamepad API verwenden
Funktionserkennung und Browserunterstützung
Die Gamepad API bietet eine hervorragende Browserunterstützung sowohl auf Computern als auch auf Mobilgeräten. Mit dem folgenden Snippet können Sie prüfen, ob die Gamepad API unterstützt wird:
if ('getGamepads' in navigator) {
// The API is supported!
}
Darstellung eines Gamepads im Browser
Der Browser stellt Gamepads als Gamepad
-Objekte dar. Ein Gamepad
hat die folgenden Eigenschaften:
id
: Ein Identifikationsstring für das Gamepad. Mit diesem String wird die Marke oder der Stil des verbundenen Gamepads angegeben.displayId
: DieVRDisplay.displayId
eines zugehörigenVRDisplay
(falls zutreffend).index
: Der Index des Gamepads im Navigationsbereich.connected
: Gibt an, ob das Gamepad noch mit dem System verbunden ist.hand
: Ein Enum, das definiert, in welcher Hand der Controller gehalten wird oder am wahrscheinlichsten gehalten wird.timestamp
: Das Datum, an dem die Daten für dieses Gamepad zuletzt aktualisiert wurden.mapping
: Die für dieses Gerät verwendete Tasten- und Achsenzuordnung, entweder"standard"
oder"xr-standard"
.pose
: EinGamepadPose
-Objekt, das die Informationen zur Haltung darstellt, die mit einem WebVR-Controller verknüpft sind.axes
: Ein Array von Werten für alle Achsen des Gamepads, linear normalisiert auf den Bereich-1.0
–1.0
.buttons
: Ein Array mit den Tastenstatus aller Tasten des Gamepads.
Schaltflächen können digital (gedrückt oder nicht gedrückt) oder analog (z. B. zu 78% gedrückt) sein. Deshalb werden Schaltflächen als GamepadButton
-Objekte mit den folgenden Attributen erfasst:
pressed
: Der gedrückte Status der Taste (true
, wenn die Taste gedrückt ist, undfalse
, wenn sie nicht gedrückt ist).touched
: Der Status der Schaltfläche, wenn sie berührt wird. Wenn die Schaltfläche Berührungen erkennen kann, hat diese Eigenschaft den Werttrue
, wenn die Schaltfläche berührt wird, andernfallsfalse
.value
: Bei Tasten mit einem analogen Sensor gibt diese Property an, wie stark die Taste gedrückt wurde. Der Wert ist linear auf den Bereich0.0
–1.0
normalisiert.hapticActuators
: Ein Array mitGamepadHapticActuator
-Objekten, von denen jedes die auf dem Controller verfügbare Hardware für haptisches Feedback darstellt.
Je nach Browser und Gamepad kann es vorkommen, dass du auf eine vibrationActuator
-Eigenschaft stößt. Es gibt zwei Arten von Rumble-Effekten:
- Dual-Rumble: Der haptische Feedbackeffekt, der von zwei exzentrischen rotierenden Massenaktoren erzeugt wird, jeweils einer in jedem Griff des Gamepads.
- Trigger-Rumble: Der haptische Feedbackeffekt, der von zwei unabhängigen Motoren erzeugt wird, wobei sich jeweils ein Motor in den Triggern des Gamepads befindet.
Die folgende schematische Übersicht, die direkt aus der Spezifikation stammt, zeigt die Zuordnung und Anordnung der Tasten und Achsen auf einem generischen Gamepad.
Benachrichtigung, wenn ein Gamepad verbunden wird
Wenn Sie wissen möchten, wann ein Gamepad verbunden ist, können Sie das gamepadconnected
-Ereignis abhören, das auf dem window
-Objekt ausgelöst wird. Wenn der Nutzer ein Gamepad verbindet, was entweder über USB oder Bluetooth erfolgen kann, wird ein GamepadEvent
ausgelöst, der die Details des Gamepads in einer passenden gamepad
-Eigenschaft enthält.
Im Folgenden sehen Sie ein Beispiel von einem Xbox 360-Controller, den ich herumliegen hatte (ja, ich mag Retro-Gaming).
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"}
*/
});
Benachrichtigung, wenn ein Gamepad getrennt wird
Die Benachrichtigung über die Trennung des Gamepads erfolgt analog zur Erkennung von Verbindungen.
Dieses Mal wartet die App auf das Ereignis gamepaddisconnected
. Im folgenden Beispiel ist zu sehen, dass connected
jetzt false
ist, wenn ich den Xbox 360-Controller vom Stromnetz trenne.
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
*/
});
Das Gamepad in Ihrem Game Loop
Um ein Gamepad zu erhalten, müssen Sie zuerst navigator.getGamepads()
aufrufen. Dadurch wird ein Array mit Gamepad
Elementen zurückgegeben. Das Array in Chrome hat immer eine feste Länge von vier Elementen. Wenn keine oder weniger als vier Gamepads verbunden sind, kann ein Element nur null
sein. Prüfen Sie immer alle Elemente des Arrays. Gamepads „merken“ sich ihren Anschluss und sind möglicherweise nicht immer am ersten verfügbaren Anschluss vorhanden.
// When no gamepads are connected:
navigator.getGamepads();
// (4) [null, null, null, null]
Wenn ein oder mehrere Gamepads verbunden sind, navigator.getGamepads()
aber weiterhin null
Elemente meldet, müssen Sie möglicherweise jedes Gamepad durch Drücken einer beliebigen Taste aktivieren. Sie können dann die Gamepad-Status in Ihrem Gameloop abfragen, wie im folgenden Code gezeigt.
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();
Der Vibrationsaktuator
Die Eigenschaft vibrationActuator
gibt ein GamepadHapticActuator
-Objekt zurück, das einer Konfiguration von Motoren oder anderen Aktoren entspricht, die eine Kraft für haptisches Feedback ausüben können. Sie können durch Drücken von Gamepad.vibrationActuator.playEffect()
haptische Effekte auslösen. Die einzigen gültigen Effekttypen sind 'dual-rumble'
und 'trigger-rumble'
.
Unterstützte Rumble-Effekte
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.
}
Dualer Rumble
„Dual-Rumble“ beschreibt eine haptische Konfiguration mit einem exzentrischen rotierenden Massenvibrationsmotor in jedem Griff eines Standard-Gamepads. Bei dieser Konfiguration kann jeder Motor das gesamte Gamepad zum Vibrieren bringen. Die beiden Massen sind unterschiedlich, damit sich die Auswirkungen der beiden kombinieren lassen, um komplexere haptische Effekte zu erzielen. Dual-Basseffekte werden durch vier Parameter definiert:
duration
: Hiermit wird die Dauer des Vibrationseffekts in Millisekunden festgelegt.startDelay
: Hiermit wird die Dauer der Verzögerung bis zum Beginn der Vibration festgelegt.strongMagnitude
undweakMagnitude
: Legen Sie die Vibrationsintensitätsstufen für die schwereren und leichteren Asynchronmotoren mit exzentrischer rotierendem Massenausgleich fest, normalisiert auf den Bereich0.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,
});
};
Vibrationen auslösen
Trigger-Vibration ist der haptische Feedbackeffekt, der von zwei unabhängigen Motoren erzeugt wird, wobei sich jeweils ein Motor in den Triggern des Gamepads befindet.
// 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,
});
};
Integration mit der Richtlinie für Berechtigungen
Die Gamepad API-Spezifikation definiert eine richtliniengesteuerte Funktion, die durch den String "gamepad"
gekennzeichnet ist. Der Standardwert von allowlist
ist "self"
. Die Berechtigungsrichtlinie eines Dokuments legt fest, ob Inhalte in diesem Dokument auf navigator.getGamepads()
zugreifen dürfen. Wenn die Funktion in einem Dokument deaktiviert ist, kann navigator.getGamepads()
in keinem Inhalt des Dokuments verwendet werden. Außerdem werden die Ereignisse gamepadconnected
und gamepaddisconnected
nicht ausgelöst.
<iframe src="index.html" allow="gamepad"></iframe>
Demo
Im folgenden Beispiel ist eine Demo des Gamepad-Testers eingebettet. Der Quellcode ist auf Glitch verfügbar. Verbinden Sie ein Gamepad über USB oder Bluetooth und drücken Sie eine beliebige Taste oder bewegen Sie eine beliebige Achse, um die Demo zu testen.
Bonus: Chrome-Dino auf web.dev spielen
Auf dieser Website können Sie Chrome Dino mit Ihrem Gamepad spielen. Der Quellcode ist auf GitHub verfügbar.
Sehen Sie sich die Implementierung der Gamepad-Abfrage in trex-runner.js
an und achten Sie darauf, wie Tastendrücke emuliert werden.
Damit die Demo für das Chrome-Dino-Gamepad funktioniert, habe ich das Chrome-Dino-Spiel aus dem Chromium-Hauptprojekt entfernt (eine frühere Version von Arnelle Ballane aktualisiert), es auf einer eigenständigen Website platziert, die vorhandene Gamepad-API-Implementierung um Ducking- und Vibrationseffekte erweitert, einen Vollbildmodus erstellt und Mehul Satardekar hat eine Implementierung des dunklen Modus beigesteuert. Viel Spaß beim Gaming!
Nützliche Links
Danksagungen
Dieses Dokument wurde von François Beaufort und Joe Medley geprüft. Die Gamepad API-Spezifikation wird von Steve Agoston, James Hollyer und Matt Reynolds bearbeitet. Die bisherigen Spezialisten für Spezifikationen sind Brandon Jones, Scott Graham und Ted Mielczarek. Die Spezifikation für Gamepad-Erweiterungen wird von Brandon Jones bearbeitet. Hero-Image von Laura Torrent Puig.