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

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. 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: 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 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: 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 berührte Status der Schaltfläche. Wenn die Schaltfläche Berührungen erkennen kann, lautet diese Eigenschaft true beim Berühren der Schaltfläche, andernfalls false.
  • 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 von 0.01.0 normalisiert.
  • hapticActuators: Ein Array mit Objekten vom Typ GamepadHapticActuator, 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.

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 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 und weakMagnitude: Legen Sie die Vibrationsintensitätsstufen für die schwereren und leichteren Motoren mit exzentrischer rotierendem Massenträgheitsmoment 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 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!

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.