เรียนรู้วิธีใช้ Gamepad API เพื่อยกระดับเกมบนเว็บของคุณไปอีกขั้น
ไข่อีสเตอร์ในหน้าออฟไลน์ของ Chrome เป็นหนึ่งในความลับที่เลวร้ายที่สุดในประวัติศาสตร์ ([citation needed]
แต่การกล่าวอ้างว่ามีผลอย่างมาก) หากคุณกดแป้น Space หรือบนอุปกรณ์เคลื่อนที่ ให้แตะไดโนเสาร์ หน้าออฟไลน์จะกลายเป็นเกมอาร์เคดที่เล่นได้ คุณอาจรู้ว่าจริงๆ แล้วคุณไม่ต้องออฟไลน์เมื่ออยากเล่นเกม เพราะใน Chrome คุณสามารถไปที่ about://dino
หรือไปที่ about://network-error/-106
เพื่อแนะนำคุณก็ได้ แต่คุณทราบหรือไม่ว่า
มีเกมไดโนเสาร์ Chrome
ที่มีการเล่นถึง 270 ล้านเกมทุกเดือน
![หน้าออฟไลน์ของ Chrome ที่มีเกมไดโนเสาร์ Chrome](https://web.dev/static/articles/gamepad/image/chromes-offline-page-th-3d966d4a2f411.png?authuser=5&hl=th)
อีกเรื่องหนึ่งที่พิสูจน์ได้ว่ามีประโยชน์มากกว่าที่ควรรู้และคุณอาจไม่รู้ก็คือ ในโหมดอาร์เคด คุณสามารถเล่นเกมด้วยเกมแพดได้ มีการเพิ่มการรองรับเกมแพดเมื่อประมาณ 1 ปีที่แล้ว ณ เวลาที่เขียนหนังสือไว้ในการยืนยันโดย Reilly Grant จะเห็นได้ว่าเกมเป็นโอเพนซอร์สโดยสมบูรณ์เช่นเดียวกับโปรเจ็กต์อื่นๆ ที่เหลือของโปรเจ็กต์ Chromium ในโพสต์นี้ ผมจะแสดงวิธีใช้ Gamepad API
ใช้ Gamepad API
การตรวจหาฟีเจอร์และการรองรับเบราว์เซอร์
Gamepad API มีการรองรับเบราว์เซอร์ที่ยอดเยี่ยมทั้งในเดสก์ท็อปและอุปกรณ์เคลื่อนที่ คุณสามารถตรวจสอบว่าระบบรองรับ 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
ที่แสดงข้อมูลท่าทางที่เชื่อมโยงกับตัวควบคุม WebVRaxes
: อาร์เรย์ของค่าสำหรับแกนทั้งหมดของเกมแพด โดยปรับให้เป็นเส้นตรงในช่วง-1.0
–1.0
buttons
: อาร์เรย์ของสถานะปุ่มสำหรับปุ่มทั้งหมดของเกมแพด
โปรดทราบว่าปุ่มอาจเป็นรูปแบบดิจิทัล (กดหรือไม่กด) หรือแอนะล็อก (เช่น กดไป 78%) นี่คือเหตุผลที่ปุ่มถูกรายงานเป็นออบเจ็กต์ GamepadButton
โดยมีแอตทริบิวต์ต่อไปนี้
pressed
: สถานะที่มีการกด (true
หากกดปุ่ม และfalse
หากไม่ได้กดtouched
: สถานะการแตะปุ่ม หากปุ่มตรวจจับการแตะได้ พร็อพเพอร์ตี้นี้จะtrue
หากถูกแตะปุ่ม และfalse
ในกรณีอื่นๆvalue
: สำหรับปุ่มที่มีเซ็นเซอร์แอนะล็อก คุณสมบัตินี้หมายถึงจำนวนที่มีการกดปุ่มดังกล่าว ซึ่งปรับให้เป็นเส้นตรงในช่วง0.0
–1.0
hapticActuators
: อาร์เรย์ที่มีออบเจ็กต์GamepadHapticActuator
ซึ่งแต่ละรายการแสดงถึงฮาร์ดแวร์การตอบสนองแบบรู้สึกได้ที่มีอยู่ในตัวควบคุม
อีกสิ่งหนึ่งที่คุณอาจพบก็คือพร็อพเพอร์ตี้ vibrationActuator
ทั้งนี้ขึ้นอยู่กับเบราว์เซอร์และเกมแพดที่คุณมี โดยจะสร้างเอฟเฟกต์แตกได้ 2 แบบ ดังนี้
- Dual-Rumble: เอฟเฟกต์การตอบสนองแบบรู้สึกได้ที่เกิดจากเครื่องเปิดใช้งานมวลชนแบบหมุนที่ผิดปกติ 2 ตัว ซึ่งใช้ในแต่ละการจับของเกมแพด
- ทริกเกอร์-รุมเบิล: การตอบสนองแบบรู้สึกได้ที่เกิดจากมอเตอร์อิสระ 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'
เท่านั้น
เอฟเฟกต์ 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 ชนิดไม่เท่ากัน จึงนำผลของมวลแต่ละชนิดมารวมกันเพื่อสร้างเอฟเฟกต์แบบรู้สึกได้ที่ซับซ้อนมากขึ้น เอฟเฟกต์แบบ Dual-rumble กำหนดโดยพารามิเตอร์ 4 ตัวดังนี้
duration
: ตั้งค่าระยะเวลาของเอฟเฟกต์การสั่นเป็นมิลลิวินาทีstartDelay
: ตั้งระยะหน่วงเวลาจนกว่าการสั่นจะเริ่มstrongMagnitude
และweakMagnitude
: ตั้งระดับความเข้มของการสั่นสำหรับมอเตอร์มวลแบบหมุนนอกศูนย์กลางที่หนักกว่าและเบากว่า ซึ่งปรับให้เป็นช่วง0.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,
});
};
เรียกเสียงฮือฮา
Trigger Rumble คือเอฟเฟกต์การตอบสนองแบบรู้สึกได้ที่สร้างขึ้นจากมอเตอร์อิสระ 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>
ข้อมูลประชากร
มีการสาธิตสำหรับผู้ทดสอบ Gamepad ในตัวอย่างต่อไปนี้ ซอร์สโค้ดมีให้ใช้งานใน Glitch ลองดูการสาธิตโดยเชื่อมต่อเกมแพดโดยใช้ USB หรือบลูทูธ แล้วกดปุ่มใดก็ได้หรือขยับแกนเกม
โบนัส: เล่นไดโนเสาร์ Chrome ใน web.dev
คุณเล่นไดโนเสาร์ Chrome ด้วยเกมแพดในเว็บไซต์นี้ได้ ซอร์สโค้ดมีอยู่ใน GitHub
ดูการใช้งานแบบสำรวจเกมแพดใน
trex-runner.js
และดูว่าฟีเจอร์นี้จำลองการกดแป้นได้อย่างไร
เพื่อให้การสาธิตเกมแพดไดโนเสาร์ Chrome ใช้งานได้ ผมได้นำเกมไดโนเสาร์ Chrome ออกจากโปรเจ็กต์ Chromium หลัก (อัปเดตความพยายามก่อนหน้านี้โดย Arnelle Ballane) วางบนเว็บไซต์แบบสแตนด์อโลน ขยายการติดตั้งใช้งาน 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