Los navegadores tienen a nuestra disposición una API para trabajar con los Gamepads o mandos USB que tengas conectados en la máquina. De esta forma, puedes utilizarlos para interactuar con la página, controlando elementos, realizando acciones o utilizando características del gamepad, como botones, joysticks o el motor de vibración.
Obtener gamepads conectados
Mediante el método getGamepads()
incluido en el objeto navigator
del navegador podemos obtener un Gamepad
conectados al USB. De esta forma, si no tenemos ninguno, nos devolverá un null
, pero si tenemos dos, nos devolverá un Gamepad
(y el resto null
).
Observa este ejemplo, en el caso de tener un gamepad conectado:
const gamepads = await navigator.getGamepads(); // [ Gamepad, null, null, null ]
Recuerda que se trata de una operación asíncrona, por lo que debes utilizar el
await
o gestionar la devolución de la promesa con un.then()
.
Eventos de Gamepad
Relacionado con este tema, tenemos a nuestra disposición dos eventos llamados gamepadconnected
y gamepaddisconnected
. Estos eventos se disparan en el momento que el navegador detecta que se ha conectado al USB un gamepad y está en funcionamiento (en algunos gamepad, debes pulsar el botón para sincronizarlo).
Evento | Descripción |
---|---|
gamepadconnected | Se dispara cada vez que se conecta un gamepad. |
gamepaddisconnected | Se dispara cada vez que se desconecta un gamepad. |
La forma de escuchar estos eventos es a través de un addEventListener() y gestionar las tareas cuando se disparan. Observa que en mi ejemplo, he desestructurado la propiedad gamepad
del objeto de evento que me devuelve, que es el mismo objeto que podemos obtener con el anteriormente mencionado getGamepads()
, con la diferencia que ese método te devuelve un gamepad
con sólo el gamepad que ha sido conectado:
globalThis.addEventListener("gamepadconnected", ({ gamepad }) => {
console.log("Gamepad conectado: ", gamepad);
});
globalThis.addEventListener("gamepaddisconnected", ({ gamepad }) => {
console.log("Gamepad desconectado: ", gamepad);
});
Siempre es conveniente escuchar estos eventos para responder o modificar nuestro juego o aplicación y que el usuario sepa que se ha conectado o desconectado el gamepad.
Objetos de Gamepad
Para trabajar con un Gamepad, básicamente tenemos tres objetos importantes: Gamepad
(el objeto principal), GamepadButton
(el objeto de cada botón) y GamepadHapticActuator
(el motor de vibración del gamepad).
Veamos los detalles de cada uno de ellos.
El objeto Gamepad
Hemos mencionado varias veces que el método anterior o los eventos anteriores nos devuelven objetos Gamepad
, pero aún no sabemos que contiene estos objetos. Cada gamepad conectado al sistema, podremos escucharlo a través de uno de estos objetos, que tienen información sobre el dispositivo:
Propiedades de Gamepad | Descripción |
---|---|
id | Identificación del controlador del gamepad. |
index | Número identificativo del gamepad. Es persistente aunque se reconecte. |
connected | Indica si el gamepad está conectado. |
timestamp | Fecha cuando se recibió datos por última vez. Es un DOMHighResTimeStamp (mejor precisión). |
mapping | Si se reconoce el layout del gamepad, devuelve standard . Sino vacío. |
axes | Array de valores -1 y 1 . |
buttons | Array de GamepadButton , uno por cada botón del gamepad. De mayor a menor importancia. |
vibrationActuator | Representa el sistema de vibración mediante un objeto GamepadHapticActuator . |
Las primeras cuatro propiedades nos sirven para obtener información sobre el gamepad. La propiedad mapping
nos puede servir para saber si el gamepad sigue el estándar oficial o no. Si se nos devuelve un standard
esto significa que el gamepad podría funcionar de forma diferente a lo esperado o devolver información donde no debería estar.
Sin embargo, las tres propiedades más importantes del mando son las siguientes:
- 1️⃣ La propiedad
axes
con uncon la dirección de los sticks direccionales. - 2️⃣ La propiedad
buttons
con unde botones del mando (gatillos, cruceta, etc...). - 3️⃣ La propiedad
vibrationActuator
que nos devuelve un objetoGamepadHapticActuator
para interactuar con el motor de vibración.
const [ gamepad ] = await navigator.getGamepads();
gamepad.buttons[0] // primer botón
gamepad.buttons[2] // tercer botón
gamepad.axes[1] // eje Y del primer pad direccional
gamepad.axes[2] // eje X del segundo pad direccional
Es importante recalcar que el objeto
gamepad
es una «instantánea» del momento en que obtienes la información del gamepad. Si guardas en una variable estos objetos no se actualizarán en tiempo real, sino que debes crear un bucle y estar continuamente accediendo agetGamepads()
para obtener la información actualizada.
El objeto GamepadButton
Hemos mencionado que la propiedad buttons
anterior es un GamepadButton
, pero no hemos mencionado que contienen. Estos objetos tienen 3 valores:
GamepadButton | Descripción |
---|---|
pressed | El botón está actualmente presionado. |
touched | El botón ha sido tocado (no necesariamente del todo). Útil para táctiles. |
value | Presión del botón analógico, entre 0.0 y 1.0 . Sólo en ciertos botones. |
La propiedad pressed
nos indica cuando un botón está siendo presionado. Por otro lado, la propiedad touched
, aunque aparezca siempre, es útil en gamepad donde tenemos elementos táctiles, como los mandos de PlayStation. Por último, el valor value
nos devuelve información sobre la presión con la que se ha pulsado el botón.
Ten en cuenta que estos valores se devuelven como
y se deben comprobar en un bucle porque en general nos puede interesar saber si el usuario lo sigue presionando o ha dejado de hacerlo y realizar una acción en determinado momento.
El objeto GamepadHapticActuator
Por último, el objeto GamepadHapticActuator
nos ofrece varios métodos interesantes para mandar efectos de vibración al gamepad, a través del motor de vibración del mismo.
GamepadHapticActuator | Descripción |
---|---|
effects | Tipos de vibración admitidos: dual-rumble (posicional) y trigger-rumble (gatillos). |
playEffects(type, params) | Envía un patrón complejo de vibración: dual-rumble y trigger-rumble . |
reset() | Resetea y anula la vibración del gamepad. |
Mediante la propiedad effects
, por ejemplo, obtendremos un dual-rumble
y trigger-rumble
si nuestro gamepad soporta estos tipos de vibración. Podemos utilizar esta propiedad para comprobar que soporta.
Por otro lado, los métodos playEffects()
y reset()
nos sirven para enviar al gamepad un efecto de vibración o detenerlo:
const [ gamepad ] = await navigator.getGamepads();
if (!gamepad.effects.includes("trigger-rumble")) {
console.log("Tu gamepad no soporta vibración de gatillos.");
return;
}
const params = {
duration: 0, /* En ms */
startDelay: 0, /* Retraso previo (en ms) */
strongMagnitude: 0.0, /* Intensidad de baja frecuencia */
weakMagnitude: 0.5, /* Intensidad de alta frecuencia */
leftTrigger: 0.0, /* Sólo para `triggle-rumble` */
rightTrigger: 0.0, /* Sólo para `triggle-rumble` */
};
gamepad.vibrationActuator.playEffects("trigger-rumble", params);
Los valores que tenemos en los params
anteriores son los valores por defecto. Podemos modificarlos para cambiar su comportamiento.