瞭解如何使用 Gamepad API,讓網頁遊戲更上一層樓。
Chrome 的離線網頁彩蛋是史上最不保密的秘密之一 ([citation needed],
但為了戲劇效果,我們還是這麼說)。按下空白鍵 (行動裝置上則輕觸恐龍),離線頁面就會變成可玩的街機遊戲。您可能知道,想玩遊戲時其實不必離線:在 Chrome 中,只要前往 about://dino,或瀏覽 about://network-error/-106,就能開始玩遊戲。但你知道嗎?每個月有 2.7 億人次玩 Chrome 恐龍遊戲!
您可能不知道,在街機模式中,您可以使用遊戲手把玩遊戲。大約一年前,Reilly Grant 在提交中新增了遊戲手把支援功能。如您所見,這款遊戲與 Chromium 專案的其他部分一樣,完全開放原始碼。在這篇文章中,我將說明如何使用 Gamepad API。
使用 Gamepad API
功能偵測和瀏覽器支援
Gamepad API 在電腦和行動裝置上都支援瀏覽器,您可以使用下列程式碼片段,偵測系統是否支援 Gamepad API:
if ('getGamepads' in navigator) {
// The API is supported!
}
瀏覽器如何表示遊戲手把
瀏覽器會將遊戲手把表示為 Gamepad 物件。Gamepad 具有下列屬性:
id:遊戲手把的識別字串。這個字串會識別所連線控制器的品牌或樣式。displayId:相關聯VRDisplay的VRDisplay.displayId(如適用)。index:導覽器中遊戲手把的索引。connected:指出遊戲手把是否仍連線至系統。hand:列舉,定義控制器握持的手,或最有可能握持的手。timestamp:上次更新這款遊戲手把資料的時間。mapping:裝置使用的按鈕和軸對應,可為"standard"或"xr-standard"。pose:代表與 WebVR 控制器相關聯的姿勢資訊的GamepadPose物件。axes:遊戲手把所有軸的值陣列,線性正規化至-1.0–1.0的範圍。buttons:遊戲手把所有按鈕的按鈕狀態陣列。
請注意,按鈕可以是數位 (按下或未按下) 或類比 (例如按下 78%)。因此,按鈕會以 GamepadButton 物件的形式回報,並包含下列屬性:
pressed:按鈕的按下狀態 (按鈕按下時為true,未按下時為false)。touched:按鈕的觸控狀態。如果按鈕可以偵測觸控,則這個屬性在按鈕遭到觸控時為true,否則為false。value:如果是具有類比感應器的按鈕,這個屬性代表按鈕的按壓量,線性正規化至0.0到1.0的範圍。hapticActuators:包含GamepadHapticActuator物件的陣列,每個物件都代表控制器上可用的觸覺回饋硬體。
視瀏覽器和遊戲手把而定,您可能會遇到 vibrationActuator 屬性。可產生兩種震動效果:
- 雙重震動:由兩個偏心旋轉質量致動器產生的觸覺回饋效果,分別位於遊戲手把的握把中。
- 觸發器震動:由兩個獨立馬達產生的觸覺回饋效果,每個馬達位於遊戲手把的觸發器中。
下圖是規格中的示意圖,顯示一般遊戲手把上的按鈕和軸向對應關係和排列方式。
連線遊戲手把時收到通知
如要瞭解遊戲手把何時連線,請監聽 window 物件上觸發的 gamepadconnected 事件。使用者連線遊戲手把時 (可透過 USB 或藍牙連線),系統會觸發 GamepadEvent,並在適當命名的 gamepad 屬性中提供遊戲手把的詳細資料。以下是 Xbox 360 控制器的範例 (沒錯,我喜歡玩復古遊戲)。
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"}
*/
});
遊戲手把中斷連線時的通知
系統偵測到遊戲手把中斷連線時,會以類似的方式通知您。
這次應用程式會監聽 gamepaddisconnected 事件。請注意,在以下範例中,當我拔除 Xbox 360 控制器時,connected 現在是 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
*/
});
遊戲迴圈中的遊戲手把
如要取得遊戲手把,請先呼叫 navigator.getGamepads(),這會傳回包含 Gamepad 項目。Chrome 中的陣列一律有四個項目的固定長度。如果連線的遊戲手把數量為零或少於四個,項目可能只會顯示 null。請務必檢查陣列中的所有項目,並注意遊戲手把會「記住」自己的插槽,因此不一定會出現在第一個可用插槽。
// When no gamepads are connected:
navigator.getGamepads();
// (4) [null, null, null, null]
如果已連線一或多個遊戲手把,但 navigator.getGamepads() 仍回報 null 項目,你可能需要按下每個遊戲手把的任一按鈕「喚醒」手把。接著,您可以在遊戲迴圈中輪詢遊戲手把狀態,如下列程式碼所示。
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();
震動致動器
vibrationActuator 屬性會傳回 GamepadHapticActuator 物件,對應於馬達或其他致動器的設定,可施加力量以提供觸覺回饋。呼叫 Gamepad.vibrationActuator.playEffect() 即可播放觸覺效果。唯一有效的效果類型為 'dual-rumble' 和 'trigger-rumble'。
支援的震動效果
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.
}
雙震動
雙震動是指觸覺設定,標準遊戲手把的每個握把都配備偏心旋轉質量震動馬達。在此設定中,任一馬達都能震動整個遊戲手把。這兩個質量不相等,因此可以結合各自的效果,產生更複雜的觸覺效果。雙震動效果由四個參數定義:
duration:設定震動效果的持續時間 (以毫秒為單位)。startDelay:設定震動開始前的延遲時間長度。strongMagnitude和weakMagnitude:設定較重和較輕的偏心旋轉質量馬達的震動強度等級,並將其標準化至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,
});
};
觸發震動
觸發器震動是觸覺回饋效果,由兩個獨立馬達產生,每個馬達分別位於遊戲手把的觸發器中。
// 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,
});
};
與權限政策整合
Gamepad API 規格定義了由政策控管的功能,以字串 "gamepad" 識別。預設 allowlist 為 "self"。文件的權限政策會決定文件中的內容是否可存取 navigator.getGamepads()。如果在任何文件中停用這項功能,文件中的內容就無法使用 navigator.getGamepads(),系統也不會觸發 gamepadconnected 和 gamepaddisconnected 事件。
<iframe src="index.html" allow="gamepad"></iframe>
示範
以下範例內嵌了遊戲手把測試人員試用版。您可以在 GitHub 取得原始碼。如要試用,請使用 USB 或藍牙連線連接遊戲手把,然後按下任一按鈕或移動任一軸。
加碼:在 web.dev 上暢玩 Chrome 恐龍遊戲
你可以在這個網站上使用遊戲手把玩 Chrome 恐龍遊戲。原始碼位於 GitHub。請查看 trex-runner.js 中的遊戲手把輪詢實作項目,並注意其模擬按鍵的方式。
為了讓 Chrome 恐龍遊戲手把示範正常運作,我從核心 Chromium 專案中擷取了 Chrome 恐龍遊戲 (更新了 Arnelle Ballane 的早期成果),並將其放在獨立網站上,透過新增閃避和震動效果擴充現有的 Gamepad API 實作項目,建立全螢幕模式,Mehul Satardekar 則貢獻了深色模式實作項目。祝你玩得開心!
實用連結
特別銘謝
本文由 François Beaufort 和 Joe Medley 審查。Gamepad API 規格由 Steve Agoston、James Hollyer 和 Matt Reynolds 編輯。前規格編輯者為 Brandon Jones、Scott Graham 和 Ted Mielczarek。Gamepad 擴充功能規格由 Brandon Jones 編輯。主頁橫幅圖片由 Laura Torrent Puig 提供。