Chrome-Dino-Spiel mit dem Gamepad spielen

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 about://dino oder about://network-error/-106 aufrufen. Wussten Sie aber, dass jeden Monat 270 Millionen Chrome-Dino-Spiele gespielt werden?

Die Offlineseite von Chrome mit dem Chrome-Dino-Spiel
Drücke die Leertaste, um zu spielen.

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. 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: Die VRDisplay.displayId eines zugehörigen VRDisplay (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: Ein GamepadPose-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.01.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, und false, 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 Wert true, wenn die Schaltfläche berührt wird, andernfalls false.
  • value: Bei Tasten mit einem analogen Sensor gibt diese Property an, wie stark die Taste gedrückt wurde. Der Wert ist linear auf den Bereich 0.01.0 normalisiert.
  • hapticActuators: Ein Array mit GamepadHapticActuator-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 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.

Schematische Übersicht der Tasten- und Achsenzuordnungen eines gängigen Gamepads.
Visuelle Darstellung eines Standard-Gamepad-Layouts (Quelle).

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 ein 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

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 und weakMagnitude: Legen Sie die Vibrationsintensitätsstufen für die schwereren und leichteren Asynchronmotoren mit exzentrischer rotierendem Massenausgleich fest, normalisiert auf den Bereich 0.01.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!

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.