Dowiedz się, jak korzystać z interfejsu Gamepad API, aby podnieść swoje gry internetowe na wyższy poziom.
Ukryty żart na stronie offline w Chrome jest jedną z najgorzej strzeżonych tajemnic w historii ([citation needed]
), ale to stwierdzenie zostało użyte dla podkreślenia dramatyzmu. Jeśli naciśniesz klawisz spacji lub na urządzeniach mobilnych klikniesz dinozaura, strona offline zmieni się w grę zręcznościową. Warto pamiętać, że nie trzeba przechodzić w tryb offline, aby zagrać: w Chrome możesz po prostu przejść do about://dino
lub, jeśli jesteś geekiem, do about://network-error/-106
. Czy wiesz, że co miesiąc w grę z dinozaurem w Chrome gra 270 milionów użytkowników?
Inną przydatną informacją, o której możesz nie wiedzieć, jest to, że w trybie arkadowym możesz grać za pomocą kontrolera. Obsługa kontrolera została dodana około roku temu w commit autorstwa Reilly Grant. Jak widać, gra, podobnie jak reszta projektu Chromium, jest w pełni otwartym kodem źródłowym. W tym poście pokażę, jak korzystać z interfejsu Gamepad API.
Korzystanie z interfejsu Gamepad API
Wykrywanie funkcji i obsługa przeglądarek
Interfejs Gamepad API jest świetnie obsługiwany przez przeglądarki na komputerach i urządzeniach mobilnych. Aby sprawdzić, czy interfejs Gamepad API jest obsługiwany, użyj tego fragmentu kodu:
if ('getGamepads' in navigator) {
// The API is supported!
}
Jak przeglądarka przedstawia kontroler
Przeglądarka przedstawia kontrolery jako obiekty Gamepad
. Element Gamepad
ma te właściwości:
id
: identyfikator kontrolera. Ten ciąg identyfikuje markę lub styl podłączonego kontrolera.displayId
:VRDisplay.displayId
powiązanego kontaVRDisplay
(jeśli dotyczy).index
: indeks kontrolera w przewodniku.connected
: wskazuje, czy gamepad jest nadal połączony z systemem.hand
: enumeracja określająca, w której ręce trzymana jest kontrolka lub w której najprawdopodobniej jest trzymana.timestamp
: ostatni czas aktualizacji danych dotyczących tego kontrolera.mapping
: mapowanie przycisków i osi używane na tym urządzeniu:"standard"
lub"xr-standard"
.pose
: obiektGamepadPose
reprezentujący informacje o położeniu ciała powiązane z kontrolerem WebVR.axes
: tablica wartości dla wszystkich osi kontrolera, liniowo znormalizowana do zakresu-1.0
–1.0
.buttons
: tablica stanów wszystkich przycisków pada.
Pamiętaj, że przyciski mogą być cyfrowe (wciśnięte lub nie wciśnięte) lub analogowe (np. wciśnięte w 78%). Dlatego przyciski są raportowane jako obiekty GamepadButton
z tymi atrybutami:
pressed
: stan naciśnięcia przycisku (true
, jeśli jest naciśnięty, ifalse
, jeśli nie jest naciśnięty).touched
: stan przycisku po dotknięciu. Jeśli przycisk może wykrywać dotyk, ta właściwość ma wartośćtrue
, gdy jest dotykany, ifalse
w innym przypadku.value
: w przypadku przycisków z czujnikiem analogowym ta właściwość reprezentuje wartość, o jaką został naciśnięty przycisk, liniowo znormalizowana w zakresie0.0
–1.0
.hapticActuators
: tablica zawierająca obiektyGamepadHapticActuator
, z których każdy reprezentuje sprzęt dotykowy dostępny w kontrolerze.
W zależności od przeglądarki i urządzenia sterującego możesz też natknąć się na właściwość vibrationActuator
. Umożliwia 2 rodzaje efektów wibracji:
- Dual-Rumble: efekt haptyczny generowany przez dwa mimośrodowe elementy wykonawcze z rotacyjną masą, po jednym w każdym uchwycie kontrolera.
- Trigger-Rumble: efekt haptyczny generowany przez 2 niezależne silniki, z jednym silnikiem w każdym z gałek gamepada.
Ten schemat, który pochodzi bezpośrednio ze specyfikacji, przedstawia mapowanie i rozmieszczenie przycisków i osi na typowym padzie.
Powiadomienie o podłączeniu kontrolera do gier
Aby dowiedzieć się, kiedy podłączony jest kontroler, nasłuchuj zdarzenie gamepadconnected
, które uruchamia się w obiekcie window
. Gdy użytkownik podłączy kontroler, co może nastąpić przez USB lub Bluetooth, zostanie wywołana GamepadEvent
, która zawiera szczegóły kontrolera w odpowiedniej nazwie właściwości gamepad
.
Poniżej możesz zobaczyć przykład kontrolera Xbox 360, który miałem pod ręką (tak, lubię retro gry).
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 kontrolera
Powiadomienia o odłączeniu kontrolera działają analogicznie do sposobu wykrywania połączeń.
Tym razem aplikacja nasłuchuje zdarzenia gamepaddisconnected
. Zwróć uwagę, że w tym przykładzie connected
jest teraz false
, gdy odłączę kontroler Xbox 360.
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 w pętli gry
Pobieranie danych z kontrolera zaczyna się od wywołania funkcji navigator.getGamepads()
, która zwraca tablicę z Gamepad
elementami. Tablica w Chrome zawsze ma stałą długość 4 elementów. Jeśli nie ma żadnych lub mniej niż 4 podłączonych kontrolerów, element może być tylko null
. Zawsze sprawdzaj wszystkie elementy tablicy. Pamiętaj, że kontrolery „zapamiętują” swoje miejsce i nie zawsze mogą znajdować się w pierwszym dostępnym miejscu.
// When no gamepads are connected:
navigator.getGamepads();
// (4) [null, null, null, null]
Jeśli masz podłączone 1 lub więcej kontrolerów, ale navigator.getGamepads()
nadal raportuje null
elementy,
może być konieczne „obudzenie” każdego z nich przez naciśnięcie dowolnego przycisku. Następnie możesz odczytywać stany kontrolera w pętli gry, jak pokazano w poniższym 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łę w celu zapewnienia informacji zwrotnej 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ójny wibrator
Podwójne wibracje to konfiguracja haptyczna z niesymetrycznym wibrującym silnikiem wibrującym w każdej rączce standardowego kontrolera. W tej konfiguracji oba silniki mogą wibrować całym kontrolerem. Obie masy są różne, dzięki czemu można łączyć efekty, aby tworzyć bardziej złożone efekty haptyczne. Efekty wibracji podwójnych są zdefiniowane przez 4 parametry:
duration
: ustawia czas trwania efektu wibracji w milisekundach.startDelay
: określa czas opóźnienia przed rozpoczęciem wibracji.strongMagnitude
iweakMagnitude
: ustaw poziomy intensywności wibracji dla cięższych i lżejszych silników z niesymetryczną masą wirnika, znormalizowanych 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,
});
};
Włącz wibracje
Wibracje spustu to efekt haptyczny generowany przez 2 niezależne silniki, z których każdy znajduje się w pojedynczym spuscie kontrolera.
// 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 dotyczącymi uprawnień
Specyfikacja interfejsu Gamepad API definiuje funkcję kontrolowaną przez zasady, którą można zidentyfikować za pomocą ciągu znaków "gamepad"
. Domyślna wartość parametru allowlist
to "self"
. Zasady dotyczące uprawnień dokumentu określają, czy jakiekolwiek treści w tym dokumencie mogą uzyskać dostęp do navigator.getGamepads()
. Jeśli ta opcja jest wyłączona w jakimkolwiek dokumencie, żadna zawartość w tym dokumencie nie będzie mogła używać funkcji navigator.getGamepads()
. Nie będą też wywoływane zdarzenia gamepadconnected
i gamepaddisconnected
.
<iframe src="index.html" allow="gamepad"></iframe>
Prezentacja
W tym przykładzie znajduje się demo testera kontrolera. Kod źródłowy jest dostępny na Glitch. Wypróbuj wersję demonstracyjną, podłączając kontroler przez USB lub Bluetooth i naciskając dowolny przycisk lub przesuwając dowolną oś.
Bonus: zagraj w grę z dinozaurem w Chrome na stronie web.dev
W tej witrynie możesz grać w dinozaura w Chrome za pomocą kontrolera. Kod źródłowy jest dostępny na GitHubie.
Sprawdź implementację pollingu kontrolera w trex-runner.js
i zwróć uwagę, jak emuluje ona naciskanie klawiszy.
Aby demo sterowania za pomocą kontrolera w grze Dinozaur w Chrome działało, wyodrębniłem grę Dinozaur w Chrome z głównego projektu Chromium (poprawiając wcześniejsze rozwiązanie Arnelle Ballane), umieściłem ją na osobnej stronie, rozszerzyłem istniejące API sterowania, dodając efekty wyciszania i wibracji, utworzyłem tryb pełnoekranowy, a Mehul Satardekar opracował implementację trybu ciemnego. Miłego grania!
Przydatne linki
- Specyfikacja interfejsu Gamepad API
- Specyfikacja rozszerzeń interfejsu Gamepad API
- Repozytorium GitHub
Podziękowania
Ten dokument został sprawdzony przez Françoisa Beauforta i Joe Medleya. Specyfikację interfejsu Gamepad API redagują Steve Agoston, James Hollyer i Matt Reynolds. Byli to: Brandon Jones, Scott Graham i Ted Mielczarek. Specyfikację rozszerzeń dla kontrolerów do gier redaguje Brandon Jones. Obraz główny autorstwa Laury Torrent Puig.