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 để nâng trò chơi trên web lên một tầm cao mới.

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

Trang ngoại tuyến của Chrome với trò chơi khủng long trên Chrome.
Nhấn phím cách để chơi!

Một thực tế khác hữu ích hơn mà có thể bạn chưa biết là ở chế độ trò chơi, bạn có thể chơi trò chơi bằng tay điều khiển trò chơi. Tính năng hỗ trợ tay điều khiển trò chơi đã được Reilly Grant đưa vào sử dụng khoảng một năm trước kể từ thời điểm viết bài này trong một cam kết. Như bạn có thể thấy, trò chơi này cũng giống như phần còn lại của dự án Chromium, hoàn toàn là nguồn mở. 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ể dùng đoạn mã sau để biết Gamepad API có được hỗ trợ hay không:

if ('getGamepads' in navigator) {
  // The API is supported!
}

Cách trình duyệt thể hiện tay điều khiển trò chơi

Trình duyệt biểu thị tay điều khiển trò chơi ở dạng đối tượng Gamepad. Gamepad có các thuộc tính sau:

  • id: Chuỗi nhận dạng cho tay điều khiển trò chơi. Chuỗi này nhận diện thương hiệu hoặc kiểu của thiết bị tay điều khiển trò chơi đã kết nối.
  • displayId: VRDisplay.displayId của một VRDisplay được liên kết (nếu có liên quan).
  • index: Chỉ mục của tay điều hướng trong trình điều hướng.
  • connected: Cho biết tay điều khiển trò chơi có còn kết nối với hệ thống hay không.
  • hand: Một giá trị enum xác định tay điều khiển đang được giữ hoặc tay có nhiều khả năng được giữ nhất.
  • timestamp: Lần gần đây nhất dữ liệu của tay điều khiển trò chơi này được cập nhật.
  • mapping: Ánh xạ nút và trục đang dùng cho thiết bị này, "standard" hoặc "xr-standard".
  • pose: Một đối tượng GamepadPose đại diện cho thông tin về tư thế liên kết với 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 điều khiển trò chơi, được chuẩn hoá tuyến tính theo phạm vi từ -1.01.0.
  • buttons: Một dãy trạng thái nút cho tất cả các nút trên tay điều khiển trò chơi.

Lưu ý rằng các nút có thể ở dạng số (nhấn hoặc không nhấn) hoặc analog (ví dụ: nhấn 78%). Đây là lý do 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 không nhấn nút và false nếu không nhấn nút).
  • touched: Trạng thái chạm của nút. Nếu nút có khả năng phát hiện thao tác chạm, thuộc tính này sẽ là true nếu người dùng nhấn vào nút và là false nếu người dùng nhấn vào nút.
  • value: Đối với các nút có cảm biến analog, thuộc tính này thể hiện số lần 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 tay điều khiển.

Ngoài ra, tuỳ thuộc vào trình duyệt và tay điều khiển trò chơi mà bạn có, bạn có thể gặp phải thuộc tính vibrationActuator. Nó cho phép hai loại hiệu ứng rumble:

  • Hai-Rumble: Hiệu ứng phản hồi xúc giác được tạo ra bởi hai bộ truyền động khối xoay lệch tâm, mỗi tay nắm một tay điều khiển trò chơi.
  • Kích hoạt-Rumble: Hiệu ứng phản hồi xúc giác được tạo ra bởi hai động cơ độc lập, với một động cơ nằm trong mỗi bộ khởi động của tay điều khiển trò chơi.

Thông tin tổng quan về giản đồ sau đây, trực tiếp từ phần thông số kỹ thuật, cho thấy cách liên kết cũng như cách sắp xếp các nút và trục trên một tay điều khiển trò chơi chung.

Sơ đồ tổng quan về sơ đồ ánh xạ nút và các trục của một tay điều khiển trò chơi phổ biến.
Hình ảnh minh hoạ một bố cục tay điều khiển trò chơi tiêu chuẩn (Nguồn).

Thông báo khi tay điều khiển trò chơi được kết nối

Để tìm hiểu thời điểm kết nối tay điều khiển trò chơ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 tay điều khiển trò chơi (có thể diễn ra qua USB hoặc Bluetooth), GamepadEvent sẽ được kích hoạt để có thông tin chi tiết về tay điều khiển trò chơi trong một thuộc tính gamepad được đặt tên khéo léo. Sau đây, bạn có thể thấy ví dụ về bộ điều khiển Xbox 360 mà tôi thường thấy (vâng, tôi thích chơi trò chơi kiểu cũ).

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 điều khiển trò chơi bị ngắt kết nối

Việc nhận thông báo về việc ngắt kết nối tay điều khiển trò chơi diễn ra tương tự như cách phát hiện các kết nối. Lần này, ứng dụng theo dõi sự kiện gamepaddisconnected. Lưu ý trong ví dụ sau connected hiện là false khi tôi rút tay điều khiển Xbox 360 ra sao.

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

Việc nắm giữ tay điều khiển trò chơi sẽ bắt đầu bằng lệnh gọi đến 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 không kết nối hoặc có ít hơn 4 tay điều khiển trò chơ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 các tay điều khiển trò chơi "nhớ" vị trí tương ứng và không phải lúc nào cũng xuất hiện ở vị trí có sẵn đầu tiên.

// When no gamepads are connected:
navigator.getGamepads();
// (4) [null, null, null, null]

Nếu một hoặc nhiều tay điều khiển trò chơi đã được kết nối, nhưng navigator.getGamepads() vẫn báo cáo các mục null, thì bạn có thể cần phải "đánh thức" từng tay điều khiển trò chơi bằng cách nhấn vào nút bất kỳ trên tay điều khiển đó. Sau đó, bạn có thể thăm dò các trạng thái của tay điều khiển trò chơi 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 động cơ hoặc bộ truyền động khác có thể tác động một lực để 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(). Loại hiệu ứng duy nhất hợp lệ là 'dual-rumble'. Dual-rumble mô tả cấu hình xúc giác với một động cơ rung khối xoay lệch tâm trên mỗi tay cầm của tay điều khiển trò chơi tiêu chuẩn. Trong cấu hình này, một trong hai động cơ có khả năng rung toàn bộ tay điều khiển trò chơi. Hai khối lượng không bằng nhau nên có thể kết hợp hiệu ứng của nhau nhằm tạo ra các hiệu ứng xúc giác phức tạp hơn. Hiệu ứng kép rumble được xác định bởi bốn 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 chế độ rung bắt đầu.
  • strongMagnitudeweakMagnitude: Đặt mức cường độ rung cho các động cơ khối lượng xoay lệch tâm nặng hơn và nhẹ hơn, được chuẩn hoá trong phạm vi 0.0 đến 1.0.

Hiệu ứng rumble đượ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.
}

Rumble kép

// 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,
  });
};

Tiếng gầm rú

// 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

Thông số kỹ thuật của Gamepad API xác định tính năng được kiểm soát bằng chính sách được xác định bằng chuỗi "gamepad". allowlist mặc định của chế độ này là "self". Chính sách về quyền của tài liệu xác định liệu có 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ị tắt trong bất kỳ tài liệu nào, thì sẽ không có nội dung nào trong tài liệu được phép sử dụng navigator.getGamepads(), đồng thời các sự kiện gamepadconnectedgamepaddisconnected cũng sẽ không kích hoạt.

<iframe src="index.html" allow="gamepad"></iframe>

Bản minh hoạ

Bản minh hoạ trình kiểm thử gamepad đượ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 tay điều khiển trò chơi qua USB hoặc Bluetooth, sau đó nhấn nút bất kỳ hoặc di chuyển trục bất kỳ của thiết bị.

Bật mí thêm cho bạn: chơi khủng long trên Chrome trên web.dev

Bạn có thể chơi trò chơi khủng long trên Chrome bằng tay điều khiển trò chơi trên chính trang web này. Mã nguồn có trên GitHub. Xem quy trình triển khai thăm dò tay điều khiển trò chơi trong trex-runner.js và lưu ý cách mô phỏng thao tác nhấn phím.

Để bản minh hoạ trò chơi khủng long trên Chrome hoạt động, tôi đã tách trò chơi khủng long trên Chrome khỏi dự án Chromium cốt lõi (cập nhật nỗ lực trước đây 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 điều khiển trò chơi hiện có bằng cách thêm hiệu ứng giảm âm thanh và rung, tạo chế độ toàn màn hình và Mehul Satardekar góp phần triển khai chế độ tối. Chúc bạn chơi vui vẻ!

Xác nhận

Tài liệu này đã được François BeaufortJoe Medley xem xét. Thông số kỹ thuật của API Gamepad do Steve Agoston, James HollyerMatt Reynolds chỉnh sửa. Các cựu biên tập viên thông số kỹ thuật là Brandon Jones, Scott GrahamTed Mielczarek. Brandon Jones chỉnh sửa thông số kỹ thuật của Tiện ích tay điều khiển trò chơi. Hình ảnh chính của Laura Torrent Puig.