Uso de la API de Gamepad HTML5 para agregar soporte de controlador a los juegos de navegador

A medida que los juegos basados ​​en la web se vuelven más populares, uno de los puntos más difíciles para los jugadores es el control de entrada. Si bien mis primeros juegos FPS fueron puramente basados ​​en el mouse y el teclado, ahora me he acostumbrado mucho más a un controlador de consola adecuado que preferiría usarlo para todo, incluidos los juegos basados ​​en la web.. 

Afortunadamente, existe la API HTML5 Gamepad para permitir a los desarrolladores web acceder mediante programación a los controladores de juego. Desafortunadamente, aunque esta API ha existido durante mucho tiempo, es solo ahora, lentamente, pasando a las versiones más recientes de los navegadores de escritorio. Durante mucho tiempo languideció en una compilación de Firefox (ni una compilación más alta, nadie nocturno construir) y fue problemático en Chrome. Ahora es bueno, no es perfecto, pero un poco menos problemático y en realidad bastante fácil de usar. 

En este artículo, discutiré las diversas características de la API, cómo hacer que funcione tanto en Firefox como en Chrome, y mostraré un juego real (aunque simple) y lo fácil que es agregarle soporte para el gamepad..

Los basicos

La API de Gamepad comprende las siguientes características:

  • La capacidad de escuchar. conectar y desconectar eventos.
  • La capacidad de reconocer múltiples gamepads. (En teoría, puede conectar tantos controles de juego como puertos USB).
  • La capacidad de inspeccionar estos gamepads y reconocer cuántos ejes tienen (joysticks), cuántos botones tienen (¿has jugado una consola de juegos moderna últimamente?) Y en qué estado se encuentran cada uno de estos elementos individuales.

Comencemos discutiendo cómo puedes detectar el soporte para un gamepad a un alto nivel. 

Tanto Firefox como Chrome soportan un método en navegador, getGamepads (), que devuelve una matriz de todos los dispositivos gamepad conectados. Podemos usar esto como un método simple para detectar si la API de Gamepad está presente. Aquí hay una función simple para esa comprobación:

function canGame () return "getGamepads" en el navegador; 

Hasta ahora tan bueno. Ahora por la parte funky. La API de Gamepad tiene soporte para eventos que detectan cuando un gamepad está conectado y desconectado. Pero, ¿qué sucede si el usuario ya tiene un gamepad conectado a su computadora portátil cuando llega a su página? Normalmente, la página web esperará a que el usuario haga algo, cualquier cosa realmente, con el gamepad real. Esto significa que debemos proporcionar algún tipo de mensaje al usuario que les permita saber que necesitan "despertarse" para el gamepad si está conectado. Podrías decirles que presionen cualquier botón o muevan un palo.. 

Para hacer las cosas aún más interesantes, esta comprobación en particular no parece ser necesaria cuando recargas la página. Descubrirá que una vez que haya usado la API de Gamepad en una página y luego la haya vuelto a cargar, la página reconoce este hecho y la considera automáticamente conectada..

Pero espera, se pone mejor. Chrome no admite los eventos conectados (o desconectados) en este momento. La solución típica para esto (y la demostrada en los buenos documentos de MDN para la API) es configurar una encuesta y ver si un gamepad "aparece" en la lista de dispositivos conectados.

¿Confuso? Comencemos con un ejemplo que solo soporta Firefox:

           

En el ejemplo anterior, comenzamos a verificar si el navegador admite la API de Gamepad. Si lo hace, primero actualizamos un div con instrucciones para el usuario, y luego comenzamos a escuchar inmediatamente conectar y desconectar eventos. 

Si ejecutas esto con Firefox y conectas tu gamepad, deberías también presionar un botón, momento en el que se dispara el evento y estás listo para comenzar.. 

Una vez más, sin embargo, en mis pruebas, cuando recargo la página, el conexión El evento es inmediato. Esto crea un ligero efecto de "parpadeo" que puede ser indeseable. En realidad, podría usar un intervalo para establecer las instrucciones para algo como 250 ms después de que se haya cargado el DOM y solo avisar si no se produjo una conexión mientras tanto. Decidí mantener las cosas simples para este tutorial..

Nuestro código funciona para Firefox, pero ahora agreguemos compatibilidad con Chrome:

           

El código es un poco más complejo ahora, pero no tan terriblemente. Carga la demo en Chrome y mira que pasa..

Tenga en cuenta que tenemos una nueva variable global, hasGP, que usaremos como una bandera general para tener un gamepad conectado. Como antes, tenemos dos detectores de eventos, pero ahora tenemos un nuevo intervalo configurado para verificar si existe un gamepad. Esta es la primera vez que ves getGamepads en acción, y lo describiremos un poco más en la siguiente sección, pero por ahora sabemos que solo devuelve una matriz, y si existe el primer elemento, podemos usar eso como una forma de saber que un gamepad está conectado. 

Usamos jQuery para desencadenar el mismo evento que Firefox habría recibido, y luego borrar el intervalo. Tenga en cuenta que este mismo intervalo también se activará una vez en Firefox, lo cual es un desperdicio, pero honestamente pensé que era una pérdida de tiempo agregar soporte adicional para detectar Chrome frente a Firefox. Una pequeña llamada como esta desperdiciada en Firefox no debería importar en absoluto.

Ahora que tenemos un gamepad conectado, trabajemos con él!

El objeto de gamepad

Para darte una idea de la edad que tengo, aquí está el joystick de última generación que utilicé para mi primer sistema de juego..


Imagen de Wikimedia Commons.

Agradable, simple, y me dolió muchísimo después de una hora de juego. Las consolas modernas tienen gamepads mucho más complejos. Considere el controlador de PS4:

Imagen de Wikimedia Commons.

Este controlador tiene dos sticks, un pad direccional, cuatro botones principales, cuatro más en la parte posterior, un Compartir y Opciones botón, un PD botón, algo funky de control táctil, un altavoz y una luz. Probablemente también tiene un capacitador de flujo y un fregadero de cocina.. 

Afortunadamente, tenemos acceso a esta bestia a través del objeto Gamepad. Las propiedades incluyen:

  • carné de identidad: Este es el nombre del controlador. No esperes algo amistoso de esto. Mi DualShock 4 fue reportado como 54c-5c4-controlador inalámbrico en Firefox, mientras que Chrome llama el mismo controlador Controlador inalámbrico (Proveedor de JUEGO ESTÁNDAR: 054c Producto: 05c4).
  • índice: Dado que la API de Gamepad es compatible con varios controladores, este le permite determinar qué controlador numerado es. Podría usarse para identificar el jugador uno, dos, etc..
  • cartografía: La asignación no es algo que vamos a cubrir aquí, pero básicamente esto es algo que el navegador puede hacer para ayudar a asignar su controlador particular a una configuración de controlador "estándar". Si ha jugado varias consolas, sabe que tienen algunas similitudes en términos de control, y la API intenta "combinar" su controlador en un estándar. No tiene que preocuparse por esto por ahora, pero si desea más detalles, consulte la sección de asignación de los documentos de la API.
  • conectado: Un valor booleano que indica si el controlador aún está conectado.
  • botones: Una matriz de valores de botón. Cada botón es una instancia de GamepadButton. Tenga en cuenta que el GamepadButton objeto soporta tanto una propiedad booleana simple (presionado) así como un valor propiedad para botones analógicos.
  • hachas: Una matriz de valores que representan los diferentes sticks en el gamepad. Dado un gamepad con tres sticks, tendrás una matriz de seis elementos, donde cada stick está representado por dos valores de array. El primero en el par representa X, o movimiento a la izquierda / derecha, mientras que el segundo representa a Y, movimiento arriba / abajo. En todos los casos el valor irá desde -1 a 1: para valores izquierda / derecha, -1 se deja y 1 es correcto; para valores arriba / abajo, -1 está arriba y 1 esta abajo. De acuerdo con la API, la matriz está ordenada de acuerdo con la "importancia", por lo que en teoría, puede centrarse en ejes [0] y ejes [1] para la mayoría de las necesidades de juego. Para hacer las cosas más interesantes, usando mi DualShock 4, Firefox informó tres ejes (lo que tiene sentido, vea la imagen de arriba), pero Chrome informó dos. Parece como si el d-pad stick se reportara en Firefox como un eje, pero no parece que salgan datos de él. En Chrome, el d-pad se mostró como botones adicionales y se leyó correctamente.
  • marca de tiempo: Finalmente, este valor es una marca de tiempo que representa la última vez que se verificó el hardware. En teoría, esto probablemente no es algo que usarías.

Bien, eso es mucho para digerir. En el siguiente ejemplo, simplemente hemos agregado un intervalo para obtener e inspeccionar el primer gamepad, e imprimir la ID y luego los botones y los ejes:

           

Puedes probar la demo en Chrome o Firefox.

Supongo que todo esto es bastante autoexplicativo; La única parte realmente difícil era manejar los ejes. Recorro la matriz y cuento por dos para representar los valores de izquierda / derecha, arriba / abajo a la vez. Si abres esto en Firefox y conectas un DualShock, puedes ver algo como esto.

Como puedes ver, el botón 2 fue presionado cuando tomé mi captura de pantalla. (En caso de que tenga curiosidad, esa fue la X botón.) Tenga en cuenta los palos; mi gamepad estaba sentado en mi computadora portátil y esos valores fluctuaban constantemente. No de una manera que implicara que los valores fueran malo, per se: si tomaba el teclado del juego y lo empujaba en una dirección, veía el valor correcto. Pero creo que lo que estaba viendo era cuán sensible es el controlador al entorno. O tal vez gremlins.

Aquí hay un ejemplo de cómo Chrome lo muestra:

Yo estaba, otra vez, sosteniendo el X botón, pero observe cómo el índice de botón es diferente aquí. Como puede ver, tendrá que hacer un poco de ... masaje si desea utilizar esta API para un juego. Me imagino que podría marcar los botones 1 y 2 para "disparar" y seguir con una buena cantidad de pruebas.

Poniendolo todo junto

Entonces, ¿qué tal una demo real? Como la mayoría de los programadores que comenzaron su vida jugando videojuegos, soñé con ser un creador de videojuegos cuando crecí. Resulta que las matemáticas se vuelven realmente difíciles después del cálculo, y al parecer estas cosas de la "red" tienen un futuro, por lo que si bien ese futuro no me salió bien, todavía me gustaría imaginar que algún día podría cumplir estos estándares web habilidades en un juego jugable. Hasta ese día, lo que tengo hoy es una versión bastante floja basada en lienzo de pong. Pong para un jugador. Como dije, cojo.

El juego simplemente presenta una paleta y una pelota, y le da control del teclado sobre la pelota. Cada vez que pierdes el balón, la puntuación aumenta. Lo que tiene sentido para el golf en lugar del pong, supongo, pero no nos preocupemos demasiado por ello. El código se puede encontrar en game1.html y puede reproducir la demostración en su navegador. 

No revisaré todo el código aquí, pero veamos algunos fragmentos. Primero, aquí está la función de bucle principal que maneja todos los detalles de la animación:

función loop () draw.clear (); ball.move (); ball.draw (); paddle.draw (); paddle.move (); draw.text ("Score:" + score, 10, 20, 20); 

La paleta es manejada por el teclado usando dos simples controladores de eventos:

$ (window) .keydown (function (e) switch (e.keyCode) case 37: input.left = true; break; case 39: input.right = true; break;); $ (window) .keyup (function (e) switch (e.keyCode) case 37: input.left = false; break; case 39: input.right = false; break;); 

los entrada variable es una variable global que es recogida por un objeto de paleta movimiento método:

this.move = function () if (input.left) this.x - = this.speed; si (esto.x < 0) this.x=0;  if(input.right)  this.x += this.speed; if((this.x+this.w) > canvas.width) this.x = canvas.width-this.w;  

Una vez más, nada demasiado complejo aquí. Aquí hay una captura de pantalla del juego en acción. (Lo sé, no debería renunciar a mi trabajo diario).

Entonces, ¿cómo añadimos soporte para gamepad? Por suerte, ya tenemos el código hecho para nosotros. En la demostración anterior, hicimos todo lo necesario para verificar y observar las actualizaciones del código. Podemos tomar ese código y simplemente agregarlo al código existente del juego. 

Ya que (virtualmente) es lo mismo, no lo repetiré (aunque la lista completa está disponible si lo desea), pero compartiré el código modificado cada 100 ms una vez que se detecte un gamepad:

función checkGamepad () var gp = navigator.getGamepads () [0]; var axeLF = gp.axes [0]; si (axeLF) < -0.5)  input.left = true; input.right = false;  else if(axeLF > 0.5) input.left = false; input.right = true;  else input.left = false; input.right = falso;  

De nuevo, puedes probar la demo en cualquiera de los navegadores..

Al igual que en el ejemplo anterior, hemos asumido que solo nos importa un gamepad. Dado que nuestro juego solo tiene una paleta y solo se mueve horizontalmente, podemos hacerlo solo con el primer eje. Recuerda, de acuerdo con la API, este debería ser el "más importante", y en mi prueba fue el stick izquierdo, que es bastante estándar para los juegos.. 

Dado que nuestro juego utiliza una variable global, entrada, Para representar el movimiento hacia la izquierda y hacia la derecha, todo lo que tengo que hacer es modificar ese valor en función del valor del eje. Ahora, note que no simplemente busqué "menos de cero" y "más que cero". ¿Por qué? Si recuerdas la demo anterior, el gamepad era muy sensible, y con frecuencia reportaba valores incluso cuando no creía que realmente había movido el stick. Usando un valor límite de .5 Le da al control un poco más de estabilidad. (Y, obviamente, este es el tipo de cosa que necesitarías modificar para ver qué "se siente" bien). 

En total, agregué aproximadamente 25 líneas de código a mi juego para agregar compatibilidad con el gamepad. Que rocas.

Juego encendido!

Es de esperar que haya visto eso, aunque definitivamente hay algunas idiosincrasias, la API de Gamepad ahora tiene soporte en dos navegadores principales, y es algo que creo que los desarrolladores realmente deberían comenzar a considerar para sus juegos.

Recursos

Aquí hay algunos recursos adicionales para ayudarlo a aprender más sobre la API de Gamepad.

  • Usando la API de Gamepad
  • Especificación de gamepad
  • Gamepad.js: una biblioteca de Javascript para habilitar el uso de gamepads y joysticks en el navegador.
  • Controles del gamepad para juegos HTML5
  • La versión de Wii U (totalmente diferente de la especificación - ¡buen trabajo, Nintendo!)

Referencias

  • Crédito de la imagen de vista previa: controlador de videojuegos diseñado por Uriel Sosa del Proyecto Noun