Gamepad API คืออะไรและทำไมถึงหลอกเรา
ถ้าคุณกำลังอยากเขียนเว็บเกมหรือโปรแกรมที่ใช้จอยสติ๊ก (อุปกรณ์ควบคุมเกม) คุณอาจจะตื่นเต้นที่รู้ว่าเบราว์เซอร์มี Gamepad API (ชุดคำสั่งที่ให้เว็บเราคุยกับจอยสติ๊กได้) มาให้ใช้กันฟรีๆ มันเป็นเครื่องมือที่เล็กและใช้ง่ายที่สุดตัวหนึ่ง เพราะไม่ต้องขออนุญาตผู้ใช้ แค่เสียบจอยก็ใช้งานได้เลย
แต่ความง่ายนี้ซ่อนความจริงที่โหดร้ายไว้ครับ ข้อมูลที่มันส่งให้เราไม่ได้เป็นความจริงเสมอไป บางครั้งจอยของคุณพังจนค้างหรือเลื่อนเอง (Stick Drift) แต่เบราว์เซอร์กลับบอกว่ามันปกติดีทุกอย่าง เหมือนเพื่อนที่พยายามบอกว่า "ฉันโอเค" ทั้งที่หน้าซีดเผือด
การเขียนโปรแกรม (การสั่งงานคอมพิวเตอร์ด้วยโค้ด) ไม่ใช่แค่การเรียกใช้คำสั่งแล้วจบไป แต่มันคือการเข้าใจว่าเครื่องมือของเรามีข้อจำกัดตรงไหนบ้าง วันนี้เราจะมาเจาะลึกสิ่งที่เอกสารทางการไม่ได้บอกคุณ เพื่อให้โปรแกรมของคุณอ่านค่าจอยได้อย่างแม่นยำจริงๆ
ทำไมเราถึงต้องคอยถาม (Polling)
ในการเขียนโปรแกรมทั่วไป เรามักจะคุ้นเคยกับการใช้ Event Listener (ตัวดักจับเหตุการณ์ เช่น การคลิกเมาส์) แต่กับจอยสติ๊กในเบราว์เซอร์นั้นไม่มีตัวดักจับการขยับก้านหรือกดปุ่มแบบนั้นเลย มันมีแค่เหตุการณ์ตอนเสียบหรือถอดจอยเท่านั้น
วิธีแก้ปัญหาคือเราต้องใช้วิธี Polling (การวนลูปถามข้อมูลซ้ำๆ ทุกเสี้ยววินาที) เพื่อดูว่าตอนนี้จอยทำอะไรอยู่ โดยเราจะใช้ requestAnimationFrame (ฟังก์ชันของเบราว์เซอร์ที่สั่งให้โค้ดรันตามจังหวะการวาดหน้าจอ) เป็นตัวคอยเช็คค่าความเคลื่อนไหวตลอดเวลา
ถ้าคุณไม่ทำแบบนี้ โปรแกรมของคุณจะไม่มีทางรู้เลยว่าผู้ใช้กำลังกดปุ่มอะไรอยู่ เพราะเบราว์เซอร์ไม่ได้ "ส่ง" ข้อมูลมาให้เราเอง แต่เราต้อง "เดินไปหยิบ" ข้อมูลจากมันแทนครับ
// ฟังก์ชันวนลูปถามสถานะจอย
function checkGamepad() {
const pads = navigator.getGamepads(); // ดึงข้อมูลจอยทั้งหมดออกมา
for (const pad of pads) {
if (!pad) continue; // ข้ามถ้าช่องนั้นไม่มีจอยเสียบอยู่
console.log("ปุ่มที่กดอยู่:", pad.buttons.filter(b => b.pressed).length);
}
requestAnimationFrame(checkGamepad); // เรียกซ้ำไปเรื่อยๆ
}
checkGamepad();
ในโค้ดนี้ navigator.getGamepads() คือหัวใจหลักที่ดึงสถานะปัจจุบันออกมา ส่วน if (!pad) continue มีไว้ป้องกันบั๊กเพราะอาร์เรย์ (ชุดข้อมูล) ของจอยอาจจะมีช่องว่างที่ไม่มีจอยเสียบอยู่ ถ้าไม่เช็คก่อนโปรแกรมจะพังทันที
ผลลัพธ์ที่คุณจะเห็นในคอนโซลคือตัวเลขจำนวนปุ่มที่ถูกกด ซึ่งจะอัปเดตค่าใหม่ทุกครั้งที่หน้าจอวาดเฟรมถัดไป ทำให้คุณรู้สถานะล่าสุดของจอยได้แบบเรียลไทม์
กฎการทำความสะอาดข้อมูลที่ไม่มีใครบอก
คุณเคยสงสัยไหมว่าทำไมจอยที่พังแล้วถึงยังส่งค่าเป็น 0 ตลอดเวลา? นั่นเพราะเบราว์เซอร์มีกฎ Sanitization (การกรองข้อมูลที่ไม่น่าเชื่อถือออก) โดยมันจะไม่รายงานค่าจริงจนกว่าคุณจะขยับจอยผ่านจุดศูนย์กลางเพื่อให้มัน "พิสูจน์" ว่าจอยยังใช้งานได้ปกติ
เบราว์เซอร์จะบังคับให้ค่าแกนเป็น 0.0 เสมอจนกว่าคุณจะขยับก้านจอยให้ค่ามันเข้าใกล้ศูนย์จริงๆ กฎนี้มีไว้เพื่อป้องกันไม่ให้จอยที่เสียหรือมีของหนักทับอยู่ส่งสัญญาณรบกวนหน้าเว็บโดยที่ผู้ใช้ไม่ได้ตั้งใจจะเล่น
นี่คือเหตุผลที่จอยที่พังหนักๆ มักจะดูเหมือนปกติในตอนแรก เพราะมันยังไม่ถูก "ปลดล็อก" ให้ส่งค่าจริงออกมานั่นเองครับ คุณต้องเข้าใจว่านี่ไม่ใช่บั๊ก แต่เป็นระบบป้องกันความปลอดภัยที่เบราว์เซอร์คิดมาให้แล้ว
ระบุตัวตนไม่ได้เหมือนที่คิด
หลายคนพยายามหาทางระบุว่าจอยที่เสียบอยู่นั้นเป็นรุ่นไหน แต่ความจริงคือข้อมูลที่ได้จาก id ของจอยนั้นไม่ค่อยละเอียดเท่าไหร่ครับ เบราว์เซอร์พยายามรักษาความเป็นส่วนตัวของผู้ใช้ จึงไม่ยอมบอกรายละเอียดลึกๆ ของฮาร์ดแวร์ (อุปกรณ์จริง)
คุณไม่สามารถเชื่อถือแค่ชื่อรุ่นเพื่อเดาว่าปุ่มไหนทำหน้าที่อะไรได้ เพราะจอยแต่ละยี่ห้อมีลำดับปุ่มไม่เหมือนกันเลย บางรุ่นปุ่ม X อาจจะอยู่ล่าง แต่บางรุ่นอาจจะอยู่ขวา ทำให้การทำโปรแกรมที่รองรับทุกจอยเป็นเรื่องท้าทายมาก
ทางแก้ที่ดีที่สุดคือการสร้างหน้าจอ Mapping (การตั้งค่าปุ่ม) ให้ผู้ใช้กดปุ่มตามที่โปรแกรมสั่ง เพื่อให้คุณจับคู่ปุ่มบนจอยเข้ากับคำสั่งในเกมของคุณได้อย่างถูกต้องและแม่นยำที่สุดครับ
// ตัวอย่างการอ่านค่าแกนจอย
const pad = navigator.getGamepads()[0];
if (pad) {
const [x, y] = pad.axes; // อ่านแกน X และ Y
console.log(`แกน X: ${x.toFixed(2)}, แกน Y: ${y.toFixed(2)}`);
}
pad.axes จะส่งค่าออกมาเป็นชุดตัวเลขแสดงตำแหน่งก้านจอย ซึ่งค่ามักจะอยู่ระหว่าง -1 ถึง 1 ส่วน toFixed(2) คือการตัดตัวเลขให้เหลือทศนิยม 2 ตำแหน่งเพื่อให้ดูง่ายขึ้น
ผลลัพธ์ที่คุณจะเห็นคือค่าตัวเลขที่เปลี่ยนไปตามการผลักก้านจอย ถ้าคุณไม่ได้ผลักมันควรจะเป็น 0 หรือใกล้เคียง ถ้ามันเป็นค่าอื่นแสดงว่าจอยเริ่มมีอาการค้างแล้วครับ
แยกแยะอาการจอยค้างกับมือคน
ถ้าคุณอยากรู้ว่าจอยเสียจริงไหม หรือแค่คนเล่นวางมือไว้เฉยๆ คุณต้องใช้ Vector Arithmetic (คณิตศาสตร์เรื่องเวกเตอร์) เข้ามาช่วยครับ โดยการหาค่าเฉลี่ยของตำแหน่งก้านจอยในช่วงเวลาสั้นๆ ถ้าค่าเฉลี่ยนี้ไม่อยู่ที่จุดศูนย์กลาง แสดงว่าจอยตัวนั้นมีปัญหาแน่นอน
ลองเก็บตัวอย่างค่า x และ y สัก 10-20 ค่าแล้วเอามาหาค่ากลางดูครับ ถ้าค่ากลางนั้นห่างจาก 0 เกินกว่าที่กำหนด (เช่น มากกว่า 0.1) นั่นคือสัญญาณเตือนว่าจอยของคุณเริ่มจะพังแล้ว
อย่าเพิ่งรีบตัดสินจอยในเฟรมแรกที่เชื่อมต่อ เพราะกฎการกรองข้อมูลจะทำให้ค่ามันหลอกคุณ ให้รอสักพักจนกว่าข้อมูลจะนิ่งแล้วค่อยเริ่มตรวจสอบอาการเสีย วิธีนี้จะช่วยให้โปรแกรมของคุณฉลาดพอที่จะบอกผู้ใช้ได้ว่า "จอยของคุณมีปัญหา กรุณาเปลี่ยนใหม่หรือทำความสะอาดนะ"
สรุป: วิธีนำไปใช้จริง
สรุปแล้ว Gamepad API คือเพื่อนที่ขี้เกรงใจครับ มันจะพยายามซ่อนความผิดพลาดของฮาร์ดแวร์ให้คุณจนกว่าจะมั่นใจจริงๆ ดังนั้นในการทำงานจริง สิ่งที่คุณต้องทำคืออย่าเชื่อค่าเริ่มต้นทันทีที่เสียบจอย แต่ต้องรอให้ผู้ใช้ขยับจอยก่อนเพื่อให้ระบบปลดล็อกข้อมูลที่แท้จริง
ถ้าคุณกำลังทำโปรเจกต์เว็บเกม ให้ลองเขียนระบบตรวจสอบจอยที่เริ่มต้นด้วยการให้ผู้ใช้ "ขยับก้านจอยให้ครบวงกลม" ก่อนเริ่มเกม วิธีนี้จะช่วยให้คุณได้ค่าที่เป็นกลาง (Calibrated) ของจอยนั้นจริงๆ และช่วยให้โปรแกรมของคุณรับมือกับจอยที่พังได้โดยไม่ทำให้ผู้เล่นหงุดหงิด
การเข้าใจเบื้องหลังเครื่องมือที่คุณใช้ คือจุดเริ่มต้นของการเป็นโปรแกรมเมอร์ที่เก่งครับ อย่าหยุดแค่การเรียกใช้คำสั่ง แต่จงตั้งคำถามว่า "มันทำงานยังไง" และ "มันมีอะไรซ่อนอยู่" แล้วคุณจะพบว่าการเขียนโค้ดนั้นสนุกและท้าทายกว่าที่คิดเยอะเลยครับ
ที่มา: The Gamepad API Lies to You: A Practical Guide to Reading Controller Input in JavaScript — freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More