Dowiedz się, jak używać interfejsu Gamepad API, aby przenieść gry przeglądarkowe na wyższy poziom.
Ukryty żart na stronie offline w Chrome to jedna z najgorzej strzeżonych tajemnic w historii ([citation needed], ale twierdzenie to ma na celu wywołanie efektu dramatycznego). Jeśli naciśniesz spację lub klikniesz dinozaura na urządzeniu mobilnym, strona offline zmieni się w grę zręcznościową. Być może wiesz, że nie musisz przechodzić w tryb offline, gdy chcesz zagrać. W Chrome możesz po prostu wejść na stronę about://dino lub, jeśli jesteś geekiem, about://network-error/-106. Czy wiesz, że co miesiąc w grę z dinozaurem w Chrome gra się 270 milionów razy?
Innym faktem, który może być bardziej przydatny i o którym możesz nie wiedzieć, jest to, że w trybie zręcznościowym możesz grać za pomocą pada do gier. Obsługa pada do gier została dodana około roku temu (w momencie pisania tego artykułu) w zatwierdzeniu przez Reilly'ego Granta. Jak widać, gra, podobnie jak reszta projektu Chromium, jest w pełni open source. W tym poście chcę pokazać, jak korzystać z interfejsu Gamepad API.
Korzystanie z interfejsu Gamepad API
Wykrywanie funkcji i obsługa przeglądarek
Interfejs Gamepad API jest powszechnie obsługiwany przez przeglądarki zarówno na komputerach, jak i urządzeniach mobilnych. Możesz sprawdzić, czy interfejs Gamepad API jest obsługiwany, używając tego fragmentu kodu:
if ('getGamepads' in navigator) {
// The API is supported!
}
Jak przeglądarka reprezentuje pad do gier
Przeglądarka reprezentuje pady do gier jako obiekty Gamepad. Obiekt Gamepad ma te właściwości:
id: ciąg znaków identyfikujący pada do gier. Ten ciąg znaków identyfikuje markę lub styl połączonego pada do gier.displayId:VRDisplay.displayIdpowiązanego elementuVRDisplay(jeśli dotyczy).index: indeks pada do gier w nawigatorze.connected: wskazuje, czy pad do gier jest nadal połączony z systemem.hand: typ wyliczeniowy określający, w której dłoni kontroler jest trzymany lub najprawdopodobniej będzie trzymany.timestamp: ostatnia aktualizacja danych dotyczących tego pada do gier.mapping: mapowanie przycisków i osi używane na tym urządzeniu,"standard"lub"xr-standard".pose: obiektGamepadPosereprezentujący informacje o pozie powiązane z kontrolerem WebVR.axes: tablica wartości dla wszystkich osi pada do gier, znormalizowana liniowo do zakresu-1.0–1.0.buttons: tablica stanów wszystkich przycisków pada do gier.
Pamiętaj, że przyciski mogą być cyfrowe (naciśnięte lub nie) lub analogowe (np. naciśnięte w 78%). Dlatego przyciski są raportowane jako obiekty GamepadButton z tymi atrybutami:
pressed: stan naciśnięcia przycisku (true, jeśli przycisk jest naciśnięty, ifalse, jeśli nie jest naciśnięty).touched: stan przycisku po dotknięciu. Jeśli przycisk wykrywa dotyk, ta właściwość ma wartośćtrue, gdy przycisk jest dotykany, a w przeciwnym razie –false.value: w przypadku przycisków z czujnikiem analogowym ta właściwość reprezentuje stopień naciśnięcia przycisku, znormalizowany liniowo do zakresu0.0–1.0.hapticActuators: tablica zawierająca obiektyGamepadHapticActuator, z których każdy reprezentuje sprzęt do reakcji haptycznej dostępny na kontrolerze.
W zależności od przeglądarki i pada do gier możesz też napotkać właściwość vibrationActuator. Umożliwia on 2 rodzaje efektów wibracji:
- Dual-Rumble: efekt reakcji haptycznej generowany przez 2 siłowniki z masą obrotową, po jednym w każdym uchwycie pada do gier.
- Trigger-Rumble: efekt reakcji haptycznej generowany przez 2 niezależne silniki, po jednym w każdym ze spustów pada do gier.
Poniższy schemat, zaczerpnięty bezpośrednio ze specyfikacji, pokazuje mapowanie i układ przycisków oraz osi na ogólnym padzie do gier.
Powiadomienie o podłączeniu pada do gier
Aby dowiedzieć się, kiedy pad do gier jest połączony, nasłuchuj zdarzenia gamepadconnected, które jest wywoływane w obiekcie window. Gdy użytkownik podłączy pada do gier przez USB lub Bluetooth, wywoływane jest zdarzenie GamepadEvent, które zawiera szczegóły dotyczące pada do gier we właściwości o odpowiedniej nazwie gamepad.
Poniżej możesz zobaczyć przykład z kontrolera Xbox 360, który miałem pod ręką (tak, lubię gry 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"}
*/
});
Powiadomienie o odłączeniu pada do gier
Powiadomienia o odłączeniu pada do gier są wysyłane w taki sam sposób jak powiadomienia o jego podłączeniu.
Tym razem aplikacja nasłuchuje zdarzenia gamepaddisconnected. Zwróć uwagę, że w tym przykładzie po odłączeniu kontrolera Xbox 360 wartość connected zmieniła się na 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
*/
});
Pad do gier w pętli gry
Aby uzyskać dostęp do pada do gier, należy wywołać funkcję navigator.getGamepads(), która zwraca tablicę z Gamepad elementami. Tablica w Chrome zawsze ma stałą długość 4 elementów. Jeśli nie jest podłączony żaden pad do gier lub jest ich mniej niż 4, element może mieć wartość null. Zawsze sprawdzaj wszystkie elementy tablicy i pamiętaj, że pady do gier „pamiętają” swoje gniazdo i nie zawsze są obecne w pierwszym dostępnym gnieździe.
// When no gamepads are connected:
navigator.getGamepads();
// (4) [null, null, null, null]
Jeśli podłączono co najmniej 1 pad do gier, ale navigator.getGamepads() nadal zgłasza null elementy, może być konieczne „wybudzenie” każdego pada przez naciśnięcie dowolnego przycisku. Następnie możesz sprawdzać stany pada do gier w pętli gry, jak pokazano w tym kodzie.
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();
silnik wibracyjny,
Właściwość vibrationActuator zwraca obiekt GamepadHapticActuator, który odpowiada konfiguracji silników lub innych elementów wykonawczych, które mogą wywierać siłę na potrzeby reakcji haptycznej. Efekty haptyczne można odtwarzać, wywołując Gamepad.vibrationActuator.playEffect(). Jedynymi prawidłowymi typami efektów są 'dual-rumble' i 'trigger-rumble'.
Obsługiwane efekty wibracji
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.
}
Podwójne wibracje
Dual-rumble to konfiguracja haptyczna z silnikiem wibracyjnym z wirującą masą mimośrodową w każdym uchwycie standardowego pada do gier. W tej konfiguracji każdy silnik może wprawić w drgania cały pad do gier. Obie masy są różne, dzięki czemu ich efekty można łączyć, aby tworzyć bardziej złożone efekty haptyczne. Efekty podwójnych wibracji są definiowane przez 4 parametry:
duration: ustawia czas trwania efektu wibracji w milisekundach.startDelay: ustawia czas opóźnienia przed rozpoczęciem wibracji.strongMagnitudeiweakMagnitude: ustaw poziomy intensywności wibracji dla cięższych i lżejszych silników z wirującą masą mimośrodową, znormalizowane do zakresu0.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,
});
};
Wibracje przycisków spustowych
Wibracje spustów to efekt reakcji haptycznej generowany przez 2 niezależne silniki, po jednym w każdym ze spustów pada do gier.
// 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,
});
};
Integracja z zasadami uprawnień
Specyfikacja interfejsu Gamepad API definiuje funkcję kontrolowaną przez zasady, która jest identyfikowana przez ciąg znaków "gamepad". Domyślna wartość allowlist to "self". Zasady uprawnień dokumentu określają, czy treści w tym dokumencie mogą mieć dostęp do navigator.getGamepads(). Jeśli ta funkcja jest wyłączona w dowolnym dokumencie, żadna zawartość dokumentu nie będzie mogła korzystać z navigator.getGamepads(), a zdarzenia gamepadconnected i gamepaddisconnected nie będą wywoływane.
<iframe src="index.html" allow="gamepad"></iframe>
Prezentacja
W przykładzie poniżej umieszczono wersję demonstracyjną testera pada do gier. Kod źródłowy jest dostępny na GitHubie. Wypróbuj wersję demonstracyjną, podłączając pada do gier przez USB lub Bluetooth i naciskając dowolny przycisk lub poruszając dowolną osią.
Bonus: zagraj w grę z dinozaurem w Chrome na web.dev
Możesz grać w dinozaura w Chrome za pomocą pada do gier na tej stronie. Kod źródłowy jest dostępny na GitHubie.
Sprawdź implementację odpytywania pada do gier w trex-runner.js i zwróć uwagę, jak emuluje naciśnięcia klawiszy.
Aby wersja demonstracyjna pada do gier z dinozaurem w Chrome działała, wyodrębniliśmy grę z dinozaurem w Chrome z głównego projektu Chromium (aktualizując wcześniejszą wersję Arnelle Ballane), umieściliśmy ją w samodzielnej witrynie, rozszerzyliśmy istniejącą implementację interfejsu Gamepad API, dodając efekty kucania i wibracji, oraz utworzyliśmy tryb pełnoekranowy. Mehul Satardekar dodał też tryb ciemny. Miłego grania!
Przydatne linki
- Specyfikacja interfejsu Gamepad API
- Specyfikacja rozszerzeń interfejsu Gamepad API
- Repozytorium GitHub
Podziękowania
Ten dokument został sprawdzony przez François Beaufort i Joe Medley. Specyfikację interfejsu Gamepad API opracowali Steve Agoston, James Hollyer i Matt Reynolds. Poprzedni redaktorzy specyfikacji to Brandon Jones, Scott Graham i Ted Mielczarek. Specyfikację Gamepad Extensions opracował Brandon Jones.