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 am schlechtesten gehüteten Geheimnisse der Geschichte ([citation needed]
, wird aber aufgrund des dramatischen Effekts behauptet). Wenn Sie die Leertaste drücken oder auf Mobilgeräten auf den Dinosaurier tippen, wird die Offlineseite zu einem spielbaren 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 about://dino
oder about://network-error/-106
aufrufen. Aber wussten Sie auch, dass jeden Monat 270 Millionen Chrome-Dino-Spiele gespielt werden?
Noch ein nützlicher Tipp, den du vielleicht 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. Anhand des folgenden Snippets können Sie feststellen, 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 Identifizierungsstring 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 angibt, 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 berührte Status der Schaltfläche. Wenn die Schaltfläche Berührungen erkennen kann, lautet diese Eigenschafttrue
beim Berühren der Schaltfläche, andernfallsfalse
.value
: Bei Schaltflächen mit analogem Sensor gibt diese Eigenschaft den Wert an, um den die Schaltfläche gedrückt wurde. Sie wird linear auf den Bereich von0.0
–1.0
normalisiert.hapticActuators
: Ein Array mit Objekten vom TypGamepadHapticActuator
, die jeweils die Hardware für haptisches Feedback darstellen, die auf dem Controller verfügbar ist.
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 wird von zwei unabhängigen Motoren erzeugt, 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 für das 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 treffend benannten 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 die Verbindung zu einem Gamepad getrennt wird
Die Benachrichtigung über getrennte Gamepads funktioniert 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
Das Abrufen eines Gamepads beginnt mit einem Aufruf von navigator.getGamepads()
, der ein Array mit Gamepad
-Elementen zurückgibt. 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. Überprüfen Sie immer alle Elemente des Arrays und achten Sie darauf, dass sich Gamepads an ihren Slot „erinnern“ und möglicherweise nicht immer im ersten verfügbaren Slot vorhanden sind.
// 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. Anschließend können Sie die Gamepad-Statuswerte in Ihrer Spielschleife 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();
Vibrationsbetätiger
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“ bezeichnet 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-Rumble-Effekte werden durch vier Parameter definiert:
duration
: Hiermit wird die Dauer des Vibrationseffekts in Millisekunden festgelegt.startDelay
: Legt die Dauer der Verzögerung bis zum Einsetzen der Vibration fest.strongMagnitude
undweakMagnitude
: Legen Sie die Vibrationsintensitätsstufen für die schwereren und leichteren Motoren mit exzentrischer rotierendem Massenträgheitsmoment 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 ein richtliniengesteuertes Feature, das durch den String "gamepad"
identifiziert wird. 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 Gamepad-Tester-Demo eingebettet. Der Quellcode ist auf Glitch verfügbar. Probieren Sie die Demo aus, indem Sie ein Gamepad über USB oder Bluetooth verbinden und eine beliebige Taste drücken oder eine beliebige Achse bewegen.
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 Chrome-Dino-Gamepad-Demo 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.