Hier erfahren Sie, wie Sie die Gamepad API verwenden, um Ihre Onlinespiele auf die nächste Stufe zu bringen.
Das Easter Egg auf der Offline-Seite von Chrome ist eines der am schlechtesten gehüteten Geheimnisse der Geschichte ([citation needed],
aber die Behauptung wurde aus dramatischen Gründen aufgestellt). Wenn Sie die Leertaste drücken oder auf Mobilgeräten auf den Dinosaurier tippen, wird die Offlineseite zu einem spielbaren Arcade-Spiel. Vielleicht wissen Sie, dass Sie nicht offline gehen müssen, wenn Sie spielen möchten: In Chrome können Sie einfach about://dino aufrufen oder, wenn Sie ein Geek sind, about://network-error/-106. Aber wussten Sie, dass jeden Monat 270 Millionen Chrome-Dino-Spiele gespielt werden?
Ein weiterer Fakt, der wohl nützlicher ist und den Sie vielleicht noch nicht kennen, ist, dass Sie das Spiel im Arcade-Modus mit einem Gamepad spielen können. Die Gamepad-Unterstützung wurde zum Zeitpunkt der Erstellung dieses Artikels 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 wird von den meisten Browsern auf Computern und Mobilgeräten unterstützt. Mit dem folgenden Snippet können Sie prüfen, ob die Gamepad API unterstützt wird:
if ('getGamepads' in navigator) {
// The API is supported!
}
Wie der Browser ein Gamepad darstellt
Der Browser stellt Gamepads als Gamepad-Objekte dar. Ein Gamepad hat die folgenden Eigenschaften:
id: Ein Identifikationsstring für das Gamepad. Dieser String gibt die Marke oder den Stil des verbundenen Gamepads an.displayId: DieVRDisplay.displayIdeiner zugehörigenVRDisplay(falls relevant).index: Der Index des Gamepads im Navigator.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: Der Zeitpunkt, zu dem die Daten für dieses Gamepad zuletzt aktualisiert wurden.mapping: Die für dieses Gerät verwendete Tasten- und Achsenbelegung, entweder"standard"oder"xr-standard".pose: EinGamepadPose-Objekt, das die mit einem WebVR-Controller verknüpften Positionsinformationen darstellt.axes: Ein Array von Werten für alle Achsen des Gamepads, linear normalisiert auf den Bereich von-1.0bis1.0.buttons: Ein Array von Schaltflächenstatus für alle Schaltflächen des Gamepads.
Tasten können digital (gedrückt oder nicht gedrückt) oder analog (z. B. zu 78 % gedrückt) sein. Daher werden Schaltflächen als GamepadButton-Objekte mit den folgenden Attributen gemeldet:
pressed: Der gedrückte Zustand der Taste (true, wenn die Taste gedrückt ist, undfalse, wenn sie nicht gedrückt ist).touched: Der Berührungsstatus der Schaltfläche. Wenn die Taste Berührungen erkennen kann, ist diese Propertytrue, wenn die Taste berührt wird, und andernfallsfalse.value: Bei Tasten mit einem analogen Sensor gibt diese Eigenschaft an, wie stark die Taste gedrückt wurde. Der Wert wird linear auf den Bereich von0.0bis1.0normalisiert.hapticActuators: Ein Array mitGamepadHapticActuator-Objekten, die jeweils die auf dem Controller verfügbare Hardware für haptisches Feedback darstellen.
Je nach Browser und Gamepad kann es sein, dass Sie auf ein vibrationActuator-Attribut stoßen. Es sind zwei Arten von Rumble-Effekten möglich:
- Dual-Rumble: Der haptische Feedback-Effekt, der von zwei exzentrischen rotierenden Massenaktuatoren erzeugt wird, einer in jedem Griff des Gamepads.
- Trigger-Rumble: Der haptische Feedbackeffekt, der von zwei unabhängigen Motoren erzeugt wird, wobei sich ein Motor in jedem der Trigger 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 du wissen möchtest, wann ein Gamepad verbunden ist, kannst du auf das Ereignis gamepadconnected warten, das für das Objekt window ausgelöst wird. Wenn der Nutzer ein Gamepad über USB oder Bluetooth verbindet, wird ein GamepadEvent ausgelöst, das die Details des Gamepads in einer entsprechend benannten gamepad-Eigenschaft enthält.
Im Folgenden sehen Sie ein Beispiel für einen Xbox 360-Controller, den ich noch 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 die Trennung von Gamepads erfolgt analog zur Erkennung von Verbindungen.
Dieses Mal wartet die Anwendung auf das Ereignis gamepaddisconnected. Beachten Sie, dass im folgenden Beispiel connected jetzt false ist, wenn ich den Xbox 360-Controller 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 Ihrer Spielschleife
Um ein Gamepad zu erhalten, rufen Sie navigator.getGamepads() auf. Dadurch wird ein Array mit Gamepad Elementen zurückgegeben. Das Array in Chrome hat immer eine feste Länge von vier Elementen. Wenn kein Gamepad oder weniger als vier Gamepads verbunden sind, kann ein Element auch einfach null sein. Prüfe immer alle Elemente des Arrays und beachte, dass sich Gamepads ihren Slot „merken“ und 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, aber navigator.getGamepads() weiterhin null Elemente meldet, musst du die Gamepads möglicherweise „aktivieren“, indem du eine beliebige Taste drückst. Anschließend können Sie die Gamepad-Zustände 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();
Vibrationsmotor
Das Attribut vibrationActuator gibt ein GamepadHapticActuator-Objekt zurück, das einer Konfiguration von Motoren oder anderen Aktuatoren entspricht, die eine Kraft für haptisches Feedback ausüben können. Haptische Effekte können durch Aufrufen von Gamepad.vibrationActuator.playEffect() wiedergegeben werden. Die einzigen gültigen Effekttypen sind 'dual-rumble' und 'trigger-rumble'.
Unterstützte Vibrationseffekte
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.
}
Dual-Rumble-Funktion
Dual-Rumble beschreibt eine haptische Konfiguration mit einem Vibrationsmotor mit exzentrischer rotierender Masse in jedem Griff eines Standard-Gamepads. In dieser Konfiguration kann jeder Motor das gesamte Gamepad vibrieren lassen. Die beiden Massen sind ungleich, sodass die Effekte der beiden kombiniert werden können, um komplexere haptische Effekte zu erzeugen. Dual-Rumble-Effekte werden durch vier Parameter definiert:
duration: Legt die Dauer des Vibrationseffekts in Millisekunden fest.startDelay: Legt die Dauer der Verzögerung fest, bis die Vibration gestartet wird.strongMagnitudeundweakMagnitude: Legen die Vibrationsintensität für die schwereren und leichteren ERM-Motoren 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,
});
};
Trigger-Vibration
Trigger-Rumble ist der haptische Feedbackeffekt, der von zwei unabhängigen Motoren erzeugt wird, wobei sich ein Motor in jedem der Trigger 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 in die Berechtigungsrichtlinie
In der Gamepad API-Spezifikation wird ein richtliniengesteuertes Feature definiert, das durch den String "gamepad" identifiziert wird. Der Standardwert für allowlist ist "self". Die Berechtigungsrichtlinie eines Dokuments bestimmt, ob Inhalte in diesem Dokument auf navigator.getGamepads() zugreifen dürfen. Wenn die Funktion in einem Dokument deaktiviert ist, darf kein Inhalt im Dokument navigator.getGamepads() verwenden und die Ereignisse gamepadconnected und gamepaddisconnected werden nicht ausgelöst.
<iframe src="index.html" allow="gamepad"></iframe>
Demo
Im folgenden Beispiel ist eine Gamepad-Tester-Demo eingebettet. Der Quellcode ist auf GitHub 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
Sie können Chrome Dino mit Ihrem Gamepad auf dieser Website spielen. Der Quellcode ist auf GitHub verfügbar.
Sehen Sie sich die Implementierung der Gamepad-Abfrage in trex-runner.js an und beachten Sie, wie Tastendrücke emuliert werden.
Damit die Demo Chrome Dino Gamepad funktioniert, habe ich das Chrome Dino-Spiel aus dem Chromium-Kernprojekt herausgenommen (und damit eine frühere Version von Arnelle Ballane aktualisiert), es auf einer eigenständigen Website platziert, die bestehende Gamepad API-Implementierung um Ducking- und Vibrationseffekte erweitert, einen Vollbildmodus erstellt und Mehul Satardekar hat eine Implementierung für den dunklen Modus beigesteuert. Viel Spaß beim Spielen!
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 früheren Spezifikations-Editoren sind Brandon Jones, Scott Graham und Ted Mielczarek. Die Spezifikation für Gamepad-Erweiterungen wird von Brandon Jones bearbeitet.