Chơi trò chơi khủng long trên Chrome bằng tay điều khiển trò chơi

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?

Trang khi không có mạng của Chrome có trò chơi khủng long trên Chrome.
Nhấn phím cách để chơi!

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ột VRDisplay đượ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ượng GamepadPose đạ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.01.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 vi 0.01.0.
  • hapticActuators: Một mảng chứa các đối tượng GamepadHapticActuator, 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.

Sơ đồ tổng quan về các nút và trục của một tay cầm chơi game thông thường.
Hình ảnh minh hoạ bố cục tiêu chuẩn của tay cầm chơi game (Nguồn).

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''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.
  • strongMagnitudeweakMagnitude: Đặ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ải 0.01.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"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 gamepadconnectedgamepaddisconnected 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ẻ!

Lời cảm ơn

Tài liệu này được François BeaufortJoe Medley xem xét. Quy cách Gamepad API do Steve Agoston, James HollyerMatt Reynolds chỉnh sửa. Các biên tập viên đặc tả trước đây là Brandon Jones, Scott GrahamTed 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.