Gamepad API web: DirectInput, XInput e a confusão do mapeamento

O que a Gamepad API te dá

A Gamepad API do navegador expõe os controles conectados via navigator.getGamepads(), que retorna um array de objetos Gamepad. Cada objeto tem um array buttons — cada entrada é um objeto com pressed, touched e um value analógico de 0 a 1 — e um array axes com as posições dos analógicos, cada uma de -1 a 1. Há eventos gamepadconnected e gamepaddisconnected, mas o estado de botões e eixos nunca é empurrado para você: você tem que consultá-lo.

Na prática isso significa ler o controle dentro de um laço requestAnimationFrame, uma vez por quadro, e comparar este quadro com o anterior para detectar um botão recém-pressionado ou solto. O objeto que você obtém é um instantâneo congelado, então você chama getGamepads() de novo a cada quadro para valores frescos. Esse modelo de polling é a primeira coisa que surpreende quem vem de eventos de teclado ou ponteiro.

O 'standard mapping'

A especificação define um layout canônico chamado standard mapping. Quando um controle o cumpre, gamepad.mapping é a string standard, e os índices são fixos e previsíveis: buttons[0] é o botão frontal de baixo (A / xis), buttons[1] o da direita (B / círculo), e os bumpers, gatilhos, direcional e cliques de analógico ficam em posições definidas, com axes[0] a axes[3] carregando os analógicos esquerdo e direito. Dezessete botões e quatro eixos numa ordem conhecida.

Quando gamepad.mapping é uma string vazia, o controle não cumpriu o layout padrão e nenhum desses índices é garantido. O mesmo botão físico pode cair em outro índice, um gatilho pode aparecer como eixo, e o direcional pode ser um único eixo tipo hat em vez de quatro botões. A propriedade mapping é o campo mais importante a checar antes de confiar em qualquer índice.

DirectInput vs. XInput: a raiz da confusão

No Windows há duas formas de um jogo falar com um controle. XInput é a API moderna da Microsoft, construída em torno do controle do Xbox e do layout que todo controle compatível com Xbox segue. Como esse layout é fixo, os navegadores conseguem mapear um dispositivo XInput de forma limpa sobre o standard mapping — por isso um controle de Xbox 'simplesmente funciona' num jogo web.

DirectInput é a API mais antiga e genérica para qualquer controle HID (dispositivo de interface humana): muitos controles de terceiros, arcade sticks, joysticks de voo, volantes, e controles antigos ou sem marca. DirectInput não impõe uma ordem fixa de botões, então o navegador não tem layout canônico para mapear e reporta um mapping vazio. Essa única diferença é a origem de quase todo relato de 'meus botões estão embaralhados no navegador'.

Por que o mesmo botão reporta índices diferentes

Com um controle DirectInput, é o fabricante que decide qual controle físico é o 'botão 1', então o seu botão A pode chegar como buttons[2], os gatilhos podem aparecer dentro do array axes em vez de botões, e o direcional pode ser reportado como um hat de ponto de vista (POV) — um eixo com oito direções — em vez de quatro botões separados.

Para tornar esses dispositivos usáveis, cada navegador aplica suas próprias heurísticas de remapeamento, e elas nem sempre concordam: o mesmo controle DirectInput pode produzir índices diferentes no Chrome e no Firefox. XInput contorna todo o problema porque a Microsoft fixou o layout, e é por isso que 'use um controle no estilo Xbox' é o conselho mais confiável para jogos web.

Como lidar no código

O padrão robusto é checar gamepad.mapping primeiro. Se for igual a standard, você pode usar índices nomeados com segurança. Se estiver vazio, não fixe nada no código — ofereça uma tela de remapeamento que peça ao jogador pressionar cada ação para você aprender o layout daquele dispositivo, e salve o resultado com a chave gamepad.id para que persista por controle.

Mais dois detalhes importam independentemente do mapping. Analógicos raramente descansam exatamente em zero, então aplique uma pequena zona morta (geralmente em torno de 0.1) e trate tudo dentro dela como nenhuma entrada, ou a deriva moverá o personagem sozinho. E como a API é por polling, leia o controle fresco a cada quadro e compare com o quadro anterior para transformar estado contínuo em eventos discretos de 'recém-pressionado'. O testador de controle deste site mostra os índices de botão e valores de eixo crus que o seu dispositivo reporta — o jeito mais rápido de ver se ele tem mapeamento padrão.

Conclusões

Sempre cheque mapping antes de confiar nos índices. Standard significa o layout da especificação; vazio significa específico do dispositivo.

Prefira controles XInput / estilo Xbox para jogos web — eles mapeiam para o layout padrão, os DirectInput não.

Nunca fixe índices no código para controles não padrão. Ofereça uma UI de remapeamento e armazene o resultado por gamepad.id.

Aplique uma zona morta aos eixos para que analógicos em repouso não contem como movimento.

Faça polling a cada quadro e compare — para botões e eixos a Gamepad API te dá estado, não eventos.