เล่นเกมไดโนเสาร์ Chrome ด้วยเกมแพด

ดูวิธีใช้ Gamepad API เพื่อยกระดับเกมในเว็บของคุณขึ้นไปอีกขั้น

ไข่อีสเตอร์ของหน้าออฟไลน์ของ Chrome เป็นหนึ่งในความลับที่เลวร้ายที่สุดในประวัติศาสตร์ ([citation needed], แต่กล่าวอ้างว่าใช้ผลลัพธ์ดราม่า) หากคุณกดแป้น Space หรือแป้นบนอุปกรณ์เคลื่อนที่ อุปกรณ์ แตะไดโนเสาร์ หน้าออฟไลน์จะกลายเป็นเกมอาร์เคดที่เล่นได้ คุณอาจทราบว่า คุณไม่จำเป็นต้องออฟไลน์เมื่อรู้สึกอยากเล่นเกม เพราะใน Chrome คุณไปที่ตำแหน่งต่างๆ ไปที่ about://dino หรือถ้าอยากรู้จักคุณ ก็ไปที่ about://network-error/-106 แต่คุณทราบไหมว่า ว่ามี มีเกมไดโนเสาร์ Chrome ที่มีผู้เล่นมากถึง 270 ล้านเกมในแต่ละเดือน

วันที่ หน้าออฟไลน์ของ Chrome ที่มีเกมไดโนเสาร์ Chrome
กดปุ่ม Space เพื่อเล่น

อีกข้อเท็จจริงหนึ่งที่ใช้โต้แย้งได้มีประโยชน์กว่าและที่คุณไม่รู้มาก่อนคือ โหมดอาร์เคดที่คุณสามารถเล่นเกมด้วยเกมแพด เพิ่มการรองรับเกมแพดเมื่อประมาณ 1 ปีที่ผ่านมาเป็น ของการเขียนนี้ใน คอมมิตโดย Reilly Grant ดังที่คุณเห็น เกมจะเหมือนกับเกมอื่นๆ โปรเจ็กต์ Chromium เสร็จสมบูรณ์ โอเพนซอร์ส ใน โพสต์นี้ ผมต้องการแสดงวิธีใช้ API เกมแพด

ใช้ Gamepad API

การตรวจหาฟีเจอร์และการสนับสนุนเบราว์เซอร์

Gamepad API มีการรองรับเบราว์เซอร์ที่ยอดเยี่ยมในทั้ง 2 แพลตฟอร์ม ทั้งเดสก์ท็อปและอุปกรณ์เคลื่อนที่ คุณตรวจสอบได้ว่ามีการรองรับ Gamepad API หรือไม่โดยใช้ข้อมูลโค้ดต่อไปนี้

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

เบราว์เซอร์แสดงถึงเกมแพดอย่างไร

เบราว์เซอร์แสดงเกมแพดเป็น Gamepad ออบเจ็กต์ Gamepad มีพร็อพเพอร์ตี้ต่อไปนี้

  • id: สตริงการระบุของเกมแพด สตริงนี้ระบุแบรนด์หรือรูปแบบของ อุปกรณ์เกมแพดที่เชื่อมต่ออยู่
  • displayId: VRDisplay.displayId จาก VRDisplay ที่เกี่ยวข้อง (หากเกี่ยวข้อง)
  • index: ดัชนีของเกมแพดในเครื่องมือนำทาง
  • connected: ระบุว่าเกมแพดยังเชื่อมต่อกับระบบอยู่ไหม
  • hand: Enum เป็นตัวกำหนดมือที่ตัวควบคุมถืออยู่หรือที่มีแนวโน้มที่จะถือมากที่สุด นิ้ว
  • timestamp: ครั้งล่าสุดที่อัปเดตข้อมูลสำหรับเกมแพดนี้
  • mapping: การแมปปุ่มและแกนที่ใช้สำหรับอุปกรณ์นี้ ได้แก่ "standard" หรือ "xr-standard"
  • pose: ออบเจ็กต์ GamepadPose ซึ่งแสดงข้อมูลท่าทางที่เชื่อมโยงกับตัวควบคุม WebVR
  • axes: อาร์เรย์ของค่าสำหรับแกนเกมแพดทุกแกน ซึ่งแปลงเป็นรูปแบบมาตรฐานเชิงเส้นไว้ที่ช่วง -1.0-1.0
  • buttons: อาร์เรย์สถานะของปุ่มทุกปุ่มของเกมแพด

โปรดทราบว่าปุ่มต่างๆ อาจเป็นปุ่มดิจิทัล (กดหรือไม่กด) หรือแอนะล็อก (เช่น เมื่อกด 78%) ช่วงเวลานี้ คือสาเหตุที่ปุ่มมีการรายงานเป็นออบเจ็กต์ GamepadButton โดยมีแอตทริบิวต์ต่อไปนี้

  • pressed: สถานะการกดของปุ่ม (true หากกดปุ่ม และ false หากไม่ได้กด
  • touched: สถานะการแตะของปุ่ม หากปุ่มสามารถตรวจจับการแตะได้ พร็อพเพอร์ตี้คือ true หากมีการแตะปุ่ม และ false หากไม่ใช่
  • value: สำหรับปุ่มที่มีเซ็นเซอร์แอนะล็อก คุณสมบัตินี้จะแสดงปริมาณ มีการกดปุ่มแล้ว ปรับให้เป็นเส้นตรงให้เป็นช่วงของ 0.01.0 แล้ว
  • hapticActuators: อาร์เรย์ที่มี GamepadHapticActuator โดยแต่ละออบเจ็กต์จะแสดงฮาร์ดแวร์การตอบสนองแบบรู้สึกได้ที่มีอยู่ในตัวควบคุม

สิ่งหนึ่งที่คุณอาจพบขึ้นอยู่กับเบราว์เซอร์และเกมแพดที่มี เป็นพร็อพเพอร์ตี้ vibrationActuator โดยทำให้เกิดเอฟเฟกต์กระตุก 2 แบบ ดังนี้

  • การตอบสนองแบบรู้สึกได้แบบ 2 จังหวะ: เอฟเฟกต์การตอบสนองแบบรู้สึกได้ที่เกิดจากตัวขับเคลื่อนมวลชนแบบหมุนศูนย์กลาง 2 ตัว โดย 1 ตัวในด้ามจับแต่ละข้างของเกมแพด
  • ทริกเกอร์-รูมเบิล: เอฟเฟกต์การตอบสนองแบบรู้สึกได้ที่เกิดจากมอเตอร์อิสระ 2 ตัว โดยมีมอเตอร์ 1 ตัวอยู่ในทริกเกอร์ของเกมแพดแต่ละตัว

ภาพรวมสคีมาต่อไปนี้ถ่ายไว้ ตรงกับข้อกำหนด แสดงการแมปและการจัดเรียงปุ่มและแกนบนเกมแพดทั่วไป

วันที่ สคีมาภาพรวมในการแมปปุ่มและแกนของเกมแพดทั่วไป
การแสดงภาพเลย์เอาต์เกมแพดมาตรฐาน (แหล่งที่มา)

การแจ้งเตือนเมื่อเชื่อมต่อเกมแพด

หากต้องการเรียนรู้เมื่อมีการเชื่อมต่อเกมแพด ให้ฟังเหตุการณ์ gamepadconnected ที่ทริกเกอร์บน window ออบเจ็กต์ เมื่อผู้ใช้เชื่อมต่อเกมแพด ซึ่งอาจเป็นการใช้ 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 โปรดสังเกตวิธีในตัวอย่างต่อไปนี้ ตอนนี้ connected คือ false เมื่อฉันถอดปลั๊กคอนโทรลเลอร์ 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
  */
});

เกมแพดใน Game Loop ของคุณ

การถือเกมแพดจะเริ่มต้นด้วยการเรียก navigator.getGamepads() ซึ่งแสดงผลอาร์เรย์ ที่มี Gamepad รายการ อาร์เรย์ใน Chrome มีความยาวคงที่ที่ 4 รายการเสมอ หากเป็น 0 หรือน้อยกว่า เกมแพดเชื่อมต่ออยู่มากกว่า 4 เครื่อง รายการหนึ่งอาจจะเป็น null อย่าลืมตรวจสอบทุกรายการ อาร์เรย์และระมัดระวังว่าเกมแพดจะ "จำ" ช่องของครีเอเตอร์รายดังกล่าวและอาจไม่ได้แสดงที่ ที่ว่างแรก

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

หากมีการเชื่อมต่อเกมแพดอย่างน้อย 1 รายการ แต่ navigator.getGamepads() ยังคงรายงาน null รายการ คุณอาจต้อง "ปลุก" แต่ละเกมแพดด้วยการกดปุ่มใดๆ ของเกมแพด จากนั้นคุณจะสำรวจเกมแพดได้ สถานะใน Game Loop ของคุณดังที่แสดงในโค้ดต่อไปนี้

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.
}

Dual Rumble

การกดปุ่ม 2 เส้นจะอธิบายการกำหนดค่าแบบรู้สึกได้ มอเตอร์สั่นแบบมวลหมุนประหลาดในด้ามจับแต่ละด้านของเกมแพดมาตรฐาน ในการกำหนดค่าแบบนี้ มอเตอร์ตัวใดตัวหนึ่งก็สามารถสั่นทั้งเกมแพดได้ มวลสองมวลไม่เท่ากัน ดังนั้นค่า สามารถรวมกันเพื่อสร้างผลกระทบแบบรู้สึกได้ที่ซับซ้อนยิ่งขึ้น เอฟเฟกต์แบบ 2 จังหวะ กำหนดโดยพารามิเตอร์ 4 ตัว ได้แก่

  • duration: ตั้งค่าระยะเวลาของเอฟเฟ็กต์การสั่นเป็นมิลลิวินาที
  • startDelay: ตั้งค่าระยะหน่วงเวลาจนกว่าจะเริ่มการสั่น
  • strongMagnitude และ weakMagnitude: ตั้งค่าระดับความเข้มของการสั่นสำหรับหนักขึ้นและ มอเตอร์มวลการหมุนรอบศูนย์กลางที่ใช้ไฟน้อยกว่า ซึ่งปรับให้เป็นช่วงมาตรฐานที่ช่วง 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,
  });
};

ทริกเกอร์ตะลุมบอน

ทริกเกอร์ตะลุมบอนคือเอฟเฟกต์การตอบสนองแบบรู้สึกได้ที่เกิดจากมอเตอร์อิสระ 2 ตัว โดยมีมอเตอร์ 1 ตัวอยู่ในทริกเกอร์ของเกมแพดแต่ละตัว

// 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 หรือบลูทูธ และการกดปุ่มใดๆ ของเกมแพดหรือขยับแกนใดก็ได้

โบนัส: เล่นไดโนเสาร์ Chrome ใน web.dev

คุณสามารถเล่นไดโนเสาร์ Chrome ด้วยเกมแพดใน ที่ยิ่งใหญ่มาก ดูซอร์สโค้ดได้ใน GitHub ลองดูการใช้งานการสำรวจเกมแพดใน trex-runner.js และสังเกตวิธีจำลองการกดแป้นต่างๆ

สำหรับการสาธิตการใช้งานเกมแพดไดโนเสาร์ Chrome เกมไดโนเสาร์ Chrome จากโครงการ Chromium หลัก (การอัปเดต ความพยายามก่อนหน้านี้โดย Arnelle Ballane) มาวางบนเว็บไซต์แบบสแตนด์อโลน ขยาย การใช้ Gamepad API ที่มีอยู่ด้วยการเพิ่มเอฟเฟกต์การลดเสียงและการสั่น เป็นการสร้างแบบเต็มหน้าจอ และ Mehul Satardekar มีส่วนร่วมในโหมดมืด การใช้งานของคุณ ขอให้สนุกกับการเล่นเกม

กิตติกรรมประกาศ

เอกสารนี้ได้รับการตรวจสอบโดย François Beaufort และ Joe Medley ข้อกำหนดของ Gamepad API ได้รับการแก้ไขโดย Steve Agoston James Hollyer และ Matt Reynolds ข้อกำหนดเดิมของผู้แก้ไขสเปค Brandon Jones, Scott Graham และ Ted Mielczarek ข้อกำหนดส่วนขยายเกมแพดแก้ไขโดย Brandon Jones รูปภาพหลักโดย Laura Torrent Puig