Tìm hiểu cách sử dụng Gamepad API để đưa trò chơi trên web của bạn lên một tầm cao mới.
Điều thú vị ẩn giấu trên trang khi không có mạng của Chrome là một trong những bí mật bị lộ nhiều nhất trong lịch sử ([citation needed]
, nhưng tuyên bố này chỉ mang tính chất kịch tính). Nếu bạn nhấn phím cách hoặc nhấn vào khủng long trên thiết bị di động, trang không có mạng sẽ trở thành một trò chơi điện tử mà bạn có thể chơi. Có thể bạn đã biết rằng bạn không thực sự phải chuyển sang chế độ ngoại tuyến khi muốn chơi: trong Chrome, bạn chỉ cần chuyển đến about://dino
hoặc nếu là người đam mê công nghệ, bạn có thể duyệt đến about://network-error/-106
. Nhưng bạn có biết rằng có 270 triệu lượt chơi trò chơi khủng long trên Chrome mỗi tháng không?

Một sự thật khác có thể hữu ích hơn mà bạn có thể chưa biết là trong chế độ arcade, bạn có thể chơi trò chơi bằng tay cầm chơi game. Khoảng một năm trước thời điểm viết bài này, chế độ hỗ trợ tay cầm chơi game đã được thêm vào một cam kết của Reilly Grant. Như bạn thấy, trò chơi này cũng hoàn toàn mã nguồn mở giống như phần còn lại của dự án Chromium. Trong bài đăng này, tôi muốn hướng dẫn bạn cách sử dụng Gamepad API.
Sử dụng Gamepad API
Phát hiện tính năng và hỗ trợ trình duyệt
Gamepad API có khả năng hỗ trợ trình duyệt tuyệt vời trên cả máy tính và thiết bị di động. Bạn có thể phát hiện xem Gamepad API có được hỗ trợ hay không bằng cách sử dụng đoạn mã sau:
if ('getGamepads' in navigator) {
// The API is supported!
}
Cách trình duyệt biểu thị tay điều khiển trò chơi
Trình duyệt biểu thị tay cầm chơi game dưới dạng đối tượng Gamepad
. Gamepad
có các thuộc tính sau:
id
: Một chuỗi nhận dạng cho tay cầm chơi game. Chuỗi này xác định thương hiệu hoặc kiểu dáng của thiết bị tay cầm chơi game được kết nối.displayId
:VRDisplay.displayId
của mộtVRDisplay
được liên kết (nếu có).index
: Chỉ mục của tay cầm chơi game trong trình điều hướng.connected
: Cho biết liệu tay cầm chơi game có còn kết nối với hệ thống hay không.hand
: Một enum xác định tay đang cầm bộ điều khiển hoặc có nhiều khả năng sẽ cầm bộ điều khiển.timestamp
: Lần gần nhất dữ liệu của tay cầm chơi game này được cập nhật.mapping
: Nút và ánh xạ trục đang dùng cho thiết bị này, có thể là"standard"
hoặc"xr-standard"
.pose
: Một đối tượngGamepadPose
đại diện cho thông tin tư thế liên kết với một bộ điều khiển WebVR.axes
: Một mảng các giá trị cho tất cả các trục của tay cầm chơi game, được chuẩn hoá tuyến tính theo phạm vi-1.0
–1.0
.buttons
: Một mảng trạng thái nút cho tất cả các nút của tay cầm chơi game.
Xin lưu ý rằng các nút có thể là nút kỹ thuật số (được nhấn hoặc không được nhấn) hoặc nút tương tự (ví dụ: được nhấn 78%). Đây là lý do khiến các nút được báo cáo dưới dạng đối tượng GamepadButton
, với các thuộc tính sau:
pressed
: Trạng thái nhấn của nút (true
nếu nút được nhấn vàfalse
nếu nút không được nhấn.touched
: Trạng thái được chạm của nút. Nếu nút có khả năng phát hiện thao tác chạm, thì thuộc tính này làtrue
nếu nút đang được chạm vào vàfalse
nếu không.value
: Đối với các nút có cảm biến tương tự, thuộc tính này biểu thị mức độ nhấn nút, được chuẩn hoá tuyến tính theo phạm vi0.0
–1.0
.hapticActuators
: Một mảng chứa các đối tượngGamepadHapticActuator
, mỗi đối tượng đại diện cho phần cứng phản hồi xúc giác có trên bộ điều khiển.
Một điều nữa mà bạn có thể gặp phải, tuỳ thuộc vào trình duyệt và tay cầm chơi game bạn có, đó là thuộc tính vibrationActuator
. API này cho phép tạo 2 loại hiệu ứng rung:
- Dual-Rumble: Hiệu ứng phản hồi xúc giác do 2 bộ truyền động khối lượng quay lệch tâm tạo ra, mỗi bộ truyền động nằm ở một tay cầm của tay cầm chơi game.
- Trigger-Rumble: Hiệu ứng phản hồi xúc giác do 2 mô-tơ độc lập tạo ra, mỗi mô-tơ nằm ở một cò của tay cầm chơi game.
Sơ đồ tổng quan sau đây (lấy trực tiếp từ thông số kỹ thuật) cho thấy cách ánh xạ và bố trí các nút cũng như trục trên một tay cầm chơi game chung.
Thông báo khi tay điều khiển trò chơi được kết nối
Để biết thời điểm tay cầm chơi game được kết nối, hãy theo dõi sự kiện gamepadconnected
kích hoạt trên đối tượng window
. Khi người dùng kết nối một tay cầm chơi game (có thể kết nối bằng USB hoặc Bluetooth), một GamepadEvent
sẽ được kích hoạt và có thông tin chi tiết về tay cầm chơi game trong một thuộc tính gamepad
có tên phù hợp.
Sau đây là ví dụ về bộ điều khiển Xbox 360 mà tôi có (vâng, tôi thích chơi game cổ điển).
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"}
*/
});
Thông báo khi tay cầm chơi game bị ngắt kết nối
Việc nhận thông báo về tình trạng ngắt kết nối tay cầm chơi game cũng tương tự như cách phát hiện kết nối.
Lần này, ứng dụng sẽ theo dõi sự kiện gamepaddisconnected
. Lưu ý cách connected
trong ví dụ sau đây hiện là false
khi tôi rút phích cắm của bộ điều khiển 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
*/
});
Tay điều khiển trò chơi trong vòng lặp trò chơi
Để lấy tay cầm chơi game, bạn cần bắt đầu bằng cách gọi navigator.getGamepads()
. Lệnh gọi này sẽ trả về một mảng có Gamepad
mục. Mảng trong Chrome luôn có độ dài cố định là 4 mục. Nếu có 0 hoặc ít hơn 4 tay cầm chơi game được kết nối, thì một mục có thể chỉ là null
. Luôn nhớ kiểm tra tất cả các mục trong mảng và lưu ý rằng tay cầm chơi game "ghi nhớ" khe cắm của chúng và không phải lúc nào cũng có mặt ở khe cắm có sẵn đầu tiên.
// When no gamepads are connected:
navigator.getGamepads();
// (4) [null, null, null, null]
Nếu đã kết nối một hoặc nhiều tay cầm chơi game nhưng navigator.getGamepads()
vẫn báo cáo null
mục, bạn có thể cần "đánh thức" từng tay cầm chơi game bằng cách nhấn vào bất kỳ nút nào trên tay cầm. Sau đó, bạn có thể thăm dò trạng thái của tay cầm chơi game trong vòng lặp trò chơi như minh hoạ trong mã sau.
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();
Bộ truyền động rung
Thuộc tính vibrationActuator
trả về một đối tượng GamepadHapticActuator
, tương ứng với cấu hình của các động cơ hoặc bộ truyền động khác có thể tác dụng lực cho mục đích phản hồi xúc giác. Bạn có thể phát hiệu ứng xúc giác bằng cách gọi Gamepad.vibrationActuator.playEffect()
. Các loại hiệu ứng hợp lệ duy nhất là 'dual-rumble'
và 'trigger-rumble'
.
Hiệu ứng rung được hỗ trợ
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.
}
Rung kép
Dual-rumble mô tả một cấu hình xúc giác có động cơ rung khối lượng quay lệch tâm trong mỗi tay cầm của một tay cầm chơi game tiêu chuẩn. Trong cấu hình này, cả hai động cơ đều có khả năng rung toàn bộ tay cầm chơi game. Hai khối lượng này không bằng nhau để có thể kết hợp hiệu ứng của từng khối lượng nhằm tạo ra các hiệu ứng xúc giác phức tạp hơn. Hiệu ứng rung kép được xác định bằng 4 tham số:
duration
: Đặt thời lượng của hiệu ứng rung tính bằng mili giây.startDelay
: Đặt khoảng thời gian trễ cho đến khi bắt đầu rung.strongMagnitude
vàweakMagnitude
: Đặt mức cường độ rung cho các động cơ có khối lượng quay lệch tâm nặng và nhẹ hơn, được chuẩn hoá theo dải0.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,
});
};
Kích hoạt chế độ rung
Tính năng rung phản hồi xúc giác được tạo ra bởi 2 động cơ độc lập, mỗi động cơ nằm ở một cò của tay cầm chơi game.
// 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,
});
};
Tích hợp với Chính sách về quyền
Quy cách Gamepad API xác định một tính năng do chính sách kiểm soát được xác định bằng chuỗi "gamepad"
. Giá trị mặc định allowlist
là "self"
. Chính sách về quyền của tài liệu sẽ xác định xem nội dung nào trong tài liệu đó được phép truy cập vào navigator.getGamepads()
hay không. Nếu bị vô hiệu hoá trong bất kỳ tài liệu nào, thì không có nội dung nào trong tài liệu đó được phép sử dụng navigator.getGamepads()
, cũng như các sự kiện gamepadconnected
và gamepaddisconnected
sẽ không kích hoạt.
<iframe src="index.html" allow="gamepad"></iframe>
Bản minh hoạ
Một bản minh hoạ trình kiểm thử tay cầm chơi game được nhúng trong ví dụ sau. Mã nguồn có sẵn trên Glitch. Hãy thử bản minh hoạ bằng cách kết nối một tay cầm chơi game qua USB hoặc Bluetooth, rồi nhấn nút bất kỳ hoặc di chuyển trục bất kỳ trên tay cầm đó.
Phần thưởng: chơi trò chơi khủng long trên Chrome trên web.dev
Bạn có thể chơi trò khủng long trên Chrome bằng tay cầm chơi game ngay trên trang web này. Mã nguồn có trên GitHub.
Hãy xem cách triển khai tính năng thăm dò tay cầm chơi game trong trex-runner.js
và lưu ý cách tính năng này mô phỏng thao tác nhấn phím.
Để bản minh hoạ tay cầm chơi game khủng long Chrome hoạt động, tôi đã tách trò chơi khủng long Chrome ra khỏi dự án Chromium cốt lõi (cập nhật nỗ lực trước đó của Arnelle Ballane), đặt trò chơi này trên một trang web độc lập, mở rộng việc triển khai API tay cầm chơi game hiện có bằng cách thêm hiệu ứng né tránh và rung, tạo chế độ toàn màn hình và Mehul Satardekar đã đóng góp một cách triển khai chế độ tối. Chúc bạn chơi game vui vẻ!
Đường liên kết hữu ích
Lời cảm ơn
Tài liệu này được François Beaufort và Joe Medley xem xét. Quy cách Gamepad API do Steve Agoston, James Hollyer và Matt Reynolds chỉnh sửa. Các biên tập viên đặc tả trước đây là Brandon Jones, Scott Graham và Ted Mielczarek. Quy cách Tiện ích tay điều khiển trò chơi do Brandon Jones chỉnh sửa. Hình ảnh chính của Laura Torrent Puig.