瞭解如何使用 Gamepad API,讓您的網路遊戲更上一層樓。
Chrome 的離線頁面彩蛋是歷史上最容易洩漏的秘密之一 ([citation needed]
,但這只是為了營造戲劇效果而說的話)。如果您按下 空格鍵,或是在行動裝置上輕觸恐龍圖示,離線頁面就會變成可玩的街機遊戲。您可能知道,想玩遊戲時不一定要離線:在 Chrome 中,您可以前往 about://dino
,或是前往 about://network-error/-106
。不過,你知道每個月有 2 億人次玩 Chrome 恐龍遊戲嗎?
另一個您可能不知道,但相當實用的事實是,在街機模式中,您可以使用遊戲搖桿玩遊戲。在撰寫本文時,遊戲控制器支援功能大約在一年多前加入,當時是 Reilly Grant 進行commit時所做的修改。如您所見,這款遊戲與其他 Chromium 專案一樣,完全是開放原始碼。在本篇文章中,我將說明如何使用 Gamepad API。
使用 Gamepad API
功能偵測和瀏覽器支援
在電腦和行動裝置上,Gamepad API 都普遍支援瀏覽器。您可以使用以下程式碼片段,偵測是否支援 Gamepad API:
if ('getGamepads' in navigator) {
// The API is supported!
}
瀏覽器如何呈現遊戲控制器
瀏覽器會將遊戲控制器表示為 Gamepad
物件。Gamepad
包含下列屬性:
id
:遊戲控制器的 ID 字串。這個字串會識別已連結的遊戲控制器裝置品牌或樣式。displayId
:相關聯VRDisplay
的VRDisplay.displayId
(如有)。index
:導覽器中的遊戲控制器索引。connected
:指出遊戲控制器是否仍連線至系統。hand
:定義控制器所持或最有可能持握的手部。timestamp
:上次更新此遊戲控制器的資料時間。mapping
:此裝置使用的按鈕和軸對應項目,可為"standard"
或"xr-standard"
。pose
:GamepadPose
物件,代表與 WebVR 控制器相關聯的姿勢資訊。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>
示範
以下範例內嵌了遊戲控制器測試器示範。原始碼可在 Glitch 上取得。請使用 USB 或藍牙連接遊戲控制器,然後按下任何按鈕或移動任何軸,試試這個示範。
額外獎勵:在 web.dev 上玩 Chrome 恐龍遊戲
您可以在這個網站上使用遊戲手把玩 Chrome 恐龍遊戲。原始碼可在 GitHub 取得。請查看 trex-runner.js
中的遊戲控制器輪詢實作項目,並注意它如何模擬按鍵按下動作。
為了讓 Chrome 恐龍遊戲搖桿示範能正常運作,我從核心 Chromium 專案中擷取 Chrome 恐龍遊戲 (更新 Arnelle Ballane 先前的努力),將其放置在獨立網站上,並透過新增靜音和震動效果來擴充現有的遊戲搖桿 API 實作,建立全螢幕模式,而 Mehul Satardekar 則提供了深色模式實作。祝您遊戲愉快!
實用連結
特別銘謝
本文件由 François Beaufort 和 Joe Medley 審查。Steve Agoston、James Hollyer 和 Matt Reynolds 編輯了 Gamepad API 規格。前規格編輯為 Brandon Jones、Scott Graham 和 Ted Mielczarek。Brandon Jones 編輯了 Gamepad Extensions 規格。主頁橫幅圖片由 Laura Torrent Puig 提供。