Tôi đã xây dựng hỗ trợ Gamepad bằng JavaScript
Tôi đã dành cả một cuối tuần để nghiên cứu ngược (reverse-engineering) đầu vào của tay cầm cho một trò chơi trên trình duyệt. Tôi ngồi đó lúc 2 giờ sáng, nhìn chằm chằm vào một mảng trống rỗng. Chẳng có gì hiển thị cả.
Trải nghiệm đó đã dạy tôi nhiều điều về browser API và hardware polling hơn bất kỳ bài hướng dẫn nào.
Web gaming đang phát triển. Với WebGL và WebGPU, các trò chơi trên trình duyệt hiện đã đạt đến mức chất lượng cao. Người chơi mong đợi rằng chỉ cần cắm tay cầm Xbox hoặc PS5 vào là có thể sử dụng ngay lập tức.
Nếu bạn xây dựng trò chơi, các công cụ hỗ trợ tiếp cận (accessibility tools), hoặc các dashboard tùy chỉnh, bạn sẽ cần đến HTML5 Gamepad API. Nó hoạt động trên Chrome, Firefox và Edge, và không yêu cầu bất kỳ thư viện nào.
Đây là những gì tôi đã học được:
- API này hoạt động theo hướng sự kiện (event-driven) đối với các kết nối nhưng lại dựa trên cơ chế polling đối với đầu vào (input).
- Bạn phải sử dụng requestAnimationFrame để đọc đầu vào một cách liên tục.
- Trình duyệt sẽ không hiển thị tay cầm cho đến khi người dùng nhấn một nút nào đó. Đây là một tính năng bảo mật nhằm ngăn chặn việc nhận dạng phần cứng (hardware fingerprinting).
Quản lý dữ liệu:
- Sử dụng gamepad.mapping để kiểm tra bố cục "standard". Bố cục chuẩn đảm bảo nút 0 luôn là nút mặt dưới.
- Kiểm tra gamepad.id để lấy Vendor ID và Product ID. Điều này giúp bạn xác định các phần cứng cụ thể như Sony DualSense.
- Luôn triển khai một bộ lọc deadzone. Các cần analog không bao giờ nằm chính xác ở giữa. Nếu không có deadzone, nhân vật hoặc camera của bạn sẽ bị trôi liên tục.
Một cách đơn giản để xử lý deadzone: Nếu Math.abs(axis) > 0.1, sau đó hãy sử dụng giá trị đó.
Phản hồi xúc giác (Haptic feedback): Bạn có thể sử dụng gamepad.vibrationActuator.playEffect để kích hoạt rung (rumble). Tính năng này hoạt động tốt trên Chrome và Edge. Firefox hỗ trợ một phần, còn Safari thì không hỗ trợ.
Những điểm chính cần lưu ý cho dự án của bạn:
- Lắng nghe các sự kiện gamepadconnected và gamepaddisconnected.
- Theo dõi trạng thái nút trước đó để phân biệt giữa "press" và "hold".
- Sử dụng một ngưỡng deadzone để bỏ qua các chuyển động nhỏ của cần analog.
- Kiểm tra trên nhiều trình duyệt khác nhau.
- Hãy nhớ rằng navigator.getGamepads() trả về một snapshot. Bạn phải gọi nó bên trong vòng lặp để lấy dữ liệu mới nhất.
Nếu bạn muốn kiểm tra phần cứng của mình trước khi viết mã, hãy sử dụng GamepadTester.pro. Nó sẽ hiển thị dữ liệu API thô và các giá trị trục (axis) theo thời gian thực.
