Gioca a Dino di Chrome con il tuo gamepad

Scopri come utilizzare l'API Gamepad per portare i tuoi giochi web a un livello superiore.

Il easter egg della pagina offline di Chrome è uno dei segreti più conservati della storia ([citation needed], ma per l'effetto drammatico). Se premi la barra spaziatrice o su un dispositivo mobile dispositivi, tocca il dinosauro, la pagina offline diventa un gioco arcade utilizzabile. Probabilmente sai già che non devi andare offline quando hai voglia di giocare: in Chrome puoi semplicemente navigare per about://dino oppure, per il nerd che ti circonda, passa a about://network-error/-106. Ma lo sapevi che esistono 270 milioni di giochi a tema Dino di Chrome giocati ogni mese?

Pagina offline di Chrome con il gioco Dino di Chrome.
. Premi la barra spaziatrice per giocare.

Un altro aspetto che probabilmente è più utile sapere e di cui potresti non essere a conoscenza è che arcade per giocare con un gamepad. Il supporto del gamepad è stato aggiunto circa un anno fa del momento di questa stesura, commit per Reilly Grant. Come puoi vedere, il gioco, proprio come il resto del Progetto Chromium, è completamente open source. Nella in questo post, voglio mostrarti come usare l'API Gamepad.

Utilizzare l'API Gamepad

Rilevamento delle funzionalità e supporto del browser

L'API Gamepad offre un supporto del browser universalmente eccezionale su entrambi computer desktop e dispositivi mobili. Puoi verificare se l'API Gamepad è supportata utilizzando il seguente snippet:

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

In che modo il browser rappresenta un gamepad

Il browser rappresenta i gamepad come Gamepad. di oggetti strutturati. Un Gamepad ha le seguenti proprietà:

  • id: una stringa di identificazione per il gamepad. Questa stringa identifica il brand o lo stile dispositivo gamepad connesso.
  • displayId: il VRDisplay.displayId di associati a VRDisplay (se pertinente).
  • index: l'indice del gamepad nel navigatore.
  • connected: indica se il gamepad è ancora collegato al sistema.
  • hand: un'enumerazione che definisce quale mano terrà il titolare o quale è più probabile che sia tenuto in.
  • timestamp: l'ultima volta che sono stati aggiornati i dati di questo gamepad.
  • mapping: la mappatura dei pulsanti e degli assi in uso per questo dispositivo, "standard" o "xr-standard".
  • pose: un oggetto GamepadPose che rappresentano le informazioni sulla posa associate a un controller WebVR.
  • axes: un array di valori per tutti gli assi del gamepad, normalizzati linearmente nell'intervallo di -1.0-1.0.
  • buttons: un array di stati dei pulsanti per tutti i pulsanti del gamepad.

Tieni presente che i pulsanti possono essere digitali (premuto o non premuto) o analogici (ad esempio, pressione del 78%). Questo è per questo che i pulsanti vengono segnalati come oggetti GamepadButton, con i seguenti attributi:

  • pressed: lo stato di pressione del pulsante (true se il pulsante viene premuto e false se non viene premuto.
  • touched: lo stato toccato del pulsante. Se il pulsante è in grado di rilevare il tocco, questo è true se viene toccato il pulsante e false negli altri casi.
  • value: per i pulsanti dotati di un sensore analogico, questa proprietà rappresenta il valore in base al quale è stato premuto, normalizzato linearmente per rientrare nell'intervallo 0.0-1.0.
  • hapticActuators: un array contenente GamepadHapticActuator ognuno dei quali rappresenta l'hardware di feedback aptico disponibile sul controller.

Un'altra cosa che potresti incontrare, a seconda del browser e del gamepad utilizzato: è una proprietà vibrationActuator. Consente due tipi di effetti sonori:

  • Dual-Rumble: l'effetto di feedback aptico generato da due attuatori di massa rotanti eccentrici, uno in ogni impugnatura del gamepad.
  • Trigger-Rumble: l'effetto di feedback aptico generato da due motori indipendenti, con un motore posizionato in ciascuno dei trigger del gamepad.

La seguente panoramica schematica, presa direttamente dalle specifiche, mostra la mappatura e la disposizione dei pulsanti e degli assi su un gamepad generico.

Panoramica schematica delle mappature di pulsanti e assi di un gamepad comune.
. Rappresentazione visiva del layout di un gamepad standard (Fonte).

Notifica quando si connette un gamepad

Per sapere quando è connesso un gamepad, esamina l'evento gamepadconnected che si attiva sul Oggetto window. Quando l'utente collega un gamepad, che può avvenire tramite USB o Bluetooth, Viene attivato un GamepadEvent con i dettagli del gamepad in una proprietà gamepad con nome appropriato. Di seguito, potete vedere un esempio di un controller Xbox 360 che ero in giro (sì, mi piace retrò).

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

Notifica quando un gamepad viene disconnesso

La notifica delle disconnessioni del gamepad avviene analogamente al modo in cui vengono rilevate le connessioni. Questa volta l'app rimane in ascolto dell'evento gamepaddisconnected. Nota come nell'esempio seguente Quando scollego il controller Xbox 360, connected ora è false.

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

Il gamepad nel ciclo di gioco

Il recupero di un gamepad inizia con una chiamata a navigator.getGamepads(), che restituisce un array con Gamepad elementi. L'array in Chrome ha sempre una lunghezza fissa di quattro elementi. Se pari a zero o inferiore se sono collegati più di quattro gamepad, un elemento potrebbe essere soltanto null. Assicurati sempre di controllare tutte le voci l'array e ricorda che i gamepad "ricordano" e potrebbero non essere sempre presenti il primo slot disponibile.

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

Se uno o più gamepad sono collegati, ma navigator.getGamepads() segnala comunque null elementi, potresti dover "svegliare" ciascun gamepad premendo uno dei pulsanti. Puoi quindi eseguire il polling del gamepad gli stati nel tuo ciclo di gioco, come mostrato nel seguente codice.

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

L'attuatore della vibrazione

La proprietà vibrationActuator restituisce un oggetto GamepadHapticActuator, che corrisponde a un configurazione di motori o altri attuatori che possono applicare una forza ai fini della tecnologia aptica feedback. Gli effetti aptici possono essere riprodotti chiamando Gamepad.vibrationActuator.playEffect(). L'unico i tipi di effetto validi sono 'dual-rumble' e 'trigger-rumble'.

Effetti Rumble supportati

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

Rumble doppio

Il doppio rumore descrive una configurazione aptica con una un motore a vibrazione di massa rotante eccentrico in ogni impugnatura di un gamepad standard. In questa configurazione, Entrambi i motori sono in grado di far vibrare l'intero gamepad. Le due masse sono disuguagliate, pertanto gli effetti di ciascuno possono essere combinati per creare effetti aptici più complessi. Gli effetti dual-rumble sono definiti da quattro parametri:

  • duration: imposta la durata dell'effetto di vibrazione in millisecondi.
  • startDelay: imposta la durata del ritardo prima dell'avvio della vibrazione.
  • strongMagnitude e weakMagnitude: imposta i livelli di intensità della vibrazione per: motori di massa rotanti eccentrici più leggeri, normalizzati nell'intervallo 0.0-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,
  });
};

Attiva rumore

Il rumble del trigger è l'effetto di feedback aptico generato da due motori indipendenti, con un motore situato in ciascuno dei trigger del gamepad.

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

Integrazione con il criterio relativo alle autorizzazioni

La specifica dell'API Gamepad definisce funzionalità controllata dalle norme identificata dalla la stringa "gamepad". Il valore predefinito di allowlist è "self". I criteri di autorizzazione di un documento determinano se i contenuti del documento sono autorizzati ad accedere a navigator.getGamepads(). Se disattivato in qualsiasi documento, nessun contenuto del documento potrà utilizzare navigator.getGamepads(), né l'attivazione degli eventi gamepadconnected e gamepaddisconnected.

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

Demo

Nel seguente esempio è incorporata una demo dei tester dei gamepad. Il codice sorgente è disponibile su Glitch. Prova la demo collegando un Gamepad che utilizza USB o Bluetooth e premi un pulsante o si sposta un asse.

Bonus: gioca a Dino di Chrome su web.dev

Puoi giocare a Dino di Chrome con il tuo gamepad su questo molto sito. Il codice sorgente è disponibile su GitHub. Controlla l'implementazione del polling del gamepad in trex-runner.js e nota come emula le pressioni dei tasti.

Affinché la demo del dino gamepad di Chrome funzioni, ho ha estrapolato il gioco Dino di Chrome dal progetto principale di Chromium (aggiornando impegno precedente Arnelle Ballane), che lo ha inserito in un sito indipendente, ha esteso i l'implementazione dell'API gamepad esistente con l'aggiunta di effetti per attenuazione automatica e vibrazione, creazione di una visualizzazione a schermo intero e Mehul Satardekar ha contribuito alla modalità Buio implementazione. Buon divertimento!

Ringraziamenti

Questo documento è stato esaminato da François Beaufort e Mario Rossi. Le specifiche dell'API Gamepad vengono modificate Steve Agoston James Hollyer e Matt Reynolds. I precedenti editor delle specifiche Brandon Jones, Scott Graham e Ted Mielczarek. La specifica delle estensioni per Gamepad è stata modificata Brandon Jones. Immagine hero di Laura Torrent Puig.