Disfruta del juego del dinosaurio de Chrome con tu control de mando

Aprende a usar la API de Gamepad para llevar tus juegos web al siguiente nivel.

El huevo de pascua de la página sin conexión de Chrome es uno de los secretos peor guardados de la historia ([citation needed], sino que se realizó una declaración para el efecto dramático). Si presionas la tecla barra espaciadora o, en un dispositivo móvil dispositivos, presiona el dinosaurio, la página sin conexión se convierte en un juego de arcade. Tal vez sepas que no es necesario que te desconectes cuando quieres jugar: en Chrome, puedes navegar a about://dino, o, para el geek que llevas dentro, navega a about://network-error/-106. Pero, ¿sabías que que haya ¿Se juegan 270 millones de juegos de dinosaurios en Chrome todos los meses?

Página sin conexión de Chrome con el juego del dinosaurio de Chrome.
Presiona la tecla de espacio para jugar.

Otro hecho que, sin duda, es más útil conocer y que quizás no conozcas puedes jugarlo con un control de juegos. La compatibilidad con controles de juegos se agregó hace aproximadamente un año como de la época de este escrito en una commit por Reilly Grant. Como puedes ver, el juego proyecto de Chromium está completamente de código abierto. En quiero mostrarte cómo usar la API de Gamepad.

Cómo usar la API de Gamepad

Detección de funciones y compatibilidad con navegadores

La API de Gamepad ofrece una excelente compatibilidad con navegadores a nivel universal computadoras de escritorio y dispositivos móviles. Puedes detectar si la API de Gamepad es compatible con el siguiente fragmento:

if ('getGamepads' in navigator) {
  // The API is supported!
}

Cómo el navegador representa un control de juegos

El navegador representa los controles de juegos como Gamepad. objetos. Un Gamepad tiene las siguientes propiedades:

  • id: Es una cadena de identificación para el control de juegos. Esta cadena identifica la marca o el estilo de un control de juegos conectado.
  • displayId: El VRDisplay.displayId de una VRDisplay asociado (si es relevante).
  • index: Es el índice del control de juegos en el navegador.
  • connected: Indica si el control de mando sigue conectado al sistema.
  • hand: Es una enumeración que define en qué mano se sostiene el control o en qué es más probable que lo sostengan. en el que te etiquetaron.
  • timestamp: La última vez que se actualizaron los datos de este control de juegos.
  • mapping: Es el botón y la asignación de ejes que se usan para este dispositivo, ya sea "standard" o "xr-standard"
  • pose: Es un objeto GamepadPose. que representa la información de pose asociada con un controlador WebVR.
  • axes: Es un array de valores para todos los ejes del control de juegos, normalizados de forma lineal al rango de Del -1.0 al 1.0
  • buttons: Es un array de estados de botones para todos los botones del control de juegos.

Ten en cuenta que los botones pueden ser digitales (presionados o no presionados) o analógicos (por ejemplo, un 78% presionado). Esta Es por eso que los botones se informan como objetos GamepadButton, con los siguientes atributos:

  • pressed: Es el estado presionado del botón (true si se presiona el botón y false). si no se presiona.
  • touched: Es el estado en el que se toca el botón. Si el botón puede detectar el tacto, este La propiedad es true si se toca el botón y false de lo contrario.
  • value: para los botones que tienen un sensor analógico, esta propiedad representa la cantidad en la que el valor en el rango de 0.0 a 1.0.
  • hapticActuators: Es un array que contiene. GamepadHapticActuator y cada uno representa el hardware de respuesta táctil disponible en el control.

Según el navegador y el control de mando que tengas, puedes encontrar algo más. es una propiedad de vibrationActuator. Permite dos tipos de efectos de ruido:

  • Doble remolque: el efecto de respuesta táctil generado por dos accionadores de masa excéntricos que giran, uno en cada empuñadura del control de mando.
  • Disparador-rumble: El efecto de respuesta táctil generado por dos motores independientes, con uno ubicado en cada uno de los gatillos del control de juegos.

La siguiente descripción esquemática, tomada directamente de las especificaciones muestra la asignación y la disposición de los botones y los ejes en un control de juegos genérico.

Descripción general esquemática de las asignaciones de botones y ejes de un control de juegos común.
Representación visual de un diseño de control de juegos estándar (Fuente).

Notificación cuando se conecta un control de mando

Para saber cuándo se conecta un control de mando, escucha el evento gamepadconnected que se activa en el window. Cuando el usuario conecta un control de juegos, lo que puede hacerse mediante USB o Bluetooth, Se activa una GamepadEvent que tiene los detalles del control de juegos en una propiedad gamepad con un nombre apropiado. A continuación, puedes ver un ejemplo de un control de Xbox 360 que tenía por ahí (sí, me interesa videojuegos retro).

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"}
  */
});

Notificación cuando se desconecta un control de juegos

Las notificaciones de desconexiones del control de mando sucede de forma análoga a la forma en que se detectan las conexiones. Esta vez, la app estará a la espera del evento gamepaddisconnected. Observa cómo, en el siguiente ejemplo, connected ahora está en false cuando desenchufa el control de Xbox 360.

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
  */
});

El control de mando en tu bucle de juego

La obtención de un control de juegos comienza con una llamada a navigator.getGamepads(), que muestra un array. con Gamepad elementos. El array de Chrome siempre tiene una longitud fija de cuatro elementos. Si es cero o menos hay cuatro controles de juegos conectados, un elemento puede ser solo null. Asegúrate siempre de revisar todos los elementos de y ten en cuenta que los controles de mando su espacio y es posible que no siempre esté presente primer espacio disponible.

// When no gamepads are connected:
navigator.getGamepads();
// (4) [null, null, null, null]

Si uno o varios controles de juegos están conectados, pero navigator.getGamepads() aún informa null elementos, quizás debas "activar" cada control de mando presionando cualquiera de los botones. Luego, puedes sondear el control de mando estados en el bucle de juego, como se muestra en el siguiente código.

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();

El accionador de vibración

La propiedad vibrationActuator muestra un objeto GamepadHapticActuator, que corresponde a un configuración de motores u otros accionadores que pueden aplicar una fuerza para la tecnología táctil comentarios. Se pueden reproducir los efectos táctiles llamando a Gamepad.vibrationActuator.playEffect(). El único Los tipos de efectos válidos son 'dual-rumble' y 'trigger-rumble'.

Efectos de ruido compatibles

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.
}

Doble ruido

El doble ruido describe una configuración táctil con un motor de vibración de masa rotativa excéntrico en cada manija de un control de juegos estándar. En esta configuración, cualquiera de los dos motores puede hacer vibrar todo el control de mando. Las dos masas son desiguales, de modo que la sus efectos se pueden combinar para crear efectos táctiles más complejos. Los efectos de doble ruido definido por cuatro parámetros:

  • duration: Establece la duración del efecto de vibración en milisegundos.
  • startDelay: Establece la duración del retraso hasta que se inicia la vibración.
  • strongMagnitude y weakMagnitude: Establece los niveles de intensidad de vibración para las aplicaciones motores de masa rotativa excéntrica más ligeras, normalizados en el rango de 0.0 a 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,
  });
};

Ruido de activador

El ruido de gatillo es el efecto de respuesta táctil que generan dos motores independientes, con uno ubicado en cada uno de los gatillos del control de mando.

// 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,
  });
};

Integración con la Política de Permisos

La especificación de la API de Gamepad define función controlada por políticas identificada por el la cadena "gamepad". Su allowlist predeterminado es "self". La política de permisos de un documento determina si algún contenido de ese documento puede acceder a navigator.getGamepads(). Si se inhabilita en cualquier documento, su contenido no podrá usar navigator.getGamepads() ni se activan los eventos gamepadconnected y gamepaddisconnected.

<iframe src="index.html" allow="gamepad"></iframe>

Demostración

En el siguiente ejemplo, se incorpora una demostración de verificador del control de juegos. El código fuente está disponible en Glitch. Prueba la demostración conectando un control de mando a través de USB o Bluetooth y presionar cualquiera de los botones o mover cualquiera de sus eje.

Contenido adicional: Juega al dinosaurio de Chrome en web.dev

Puedes jugar al dinosaurio de Chrome con tu control de juegos en este en el mismo sitio. El código fuente está disponible en GitHub. Consulta la implementación del sondeo del control de mando en trex-runner.js y observa cómo emula la pulsación de teclas.

Para que la demostración del control de juegos del dinosaurio de Chrome funcione, saqué el juego del dinosaurio de Chrome del proyecto principal de Chromium (actualizar un esfuerzo anterior por Arnelle Ballane), la colocó en un sitio independiente, extendió implementación de la API de mando para juegos existente agregando efectos de atenuación y vibración, creó una pantalla completa y Mehul Satardekar contribuyó con un modo oscuro para implementarlos. ¡Que disfrutes de los videojuegos!

Agradecimientos

Este documento fue revisado por François Beaufort y Joe Medley. El usuario edita la especificación de la API de Gamepad Steve Agoston: James Hollyer y Matt Reynolds. Los antiguos editores de especificaciones Brandon Jones, Scott Graham y Ted Mielczarek. La especificación de extensiones de Gamepad es editada por Brandon González. Hero image de Laura Torrent Puig.