เมื่อคุณเสียบคอนโทรลเลอร์ คุณจะบดปุ่ม ขยับแท่งไม้ ดึงทริกเกอร์... และในฐานะนักพัฒนา คุณจะไม่เห็นอะไรเลย แน่นอนว่าเบราว์เซอร์กำลังหยิบมันขึ้นมา แต่ถ้าคุณไม่บันทึกหมายเลขในคอนโซล มันจะมองไม่เห็น นั่นเป็นเรื่องที่น่าปวดหัวกับ Gamepad API มีมานานหลายปีแล้ว และมันก็ค่อนข้างทรงพลังจริงๆ คุณสามารถอ่านปุ่ม แท่ง ทริกเกอร์ และการทำงานได้ แต่คนส่วนใหญ่ไม่ได้สัมผัสมัน ทำไม เพราะไม่มีเสียงตอบรับ ไม่มีแผงในเครื่องมือสำหรับนักพัฒนา ไม่มีวิธีที่ชัดเจนในการทราบว่าผู้ควบคุมกำลังทำในสิ่งที่คุณคิดหรือไม่ รู้สึกเหมือนบินตาบอด นั่นรบกวนฉันมากพอที่จะสร้างเครื่องมือเล็ก ๆ น้อย ๆ : Gamepad Cascade Debugger แทนที่จะจ้องมองที่เอาต์พุตของคอนโซล คุณจะได้รับมุมมองแบบโต้ตอบสดของคอนโทรลเลอร์ กดอะไรสักอย่างแล้วมันจะตอบสนองบนหน้าจอ และด้วย CSS Cascade Layers สไตล์ต่างๆ จะได้รับการจัดระเบียบ ดังนั้นจึงแก้ไขจุดบกพร่องได้สะอาดยิ่งขึ้น ในโพสต์นี้ ฉันจะแสดงให้คุณเห็นว่าเหตุใดการดีบักคอนโทรลเลอร์จึงเป็นเรื่องที่ยุ่งยาก CSS ช่วยทำความสะอาดได้อย่างไร และคุณสามารถสร้างวิชวลดีบักเกอร์ที่ใช้ซ้ำได้สำหรับโปรเจ็กต์ของคุณเองได้อย่างไร
แม้ว่าคุณจะสามารถบันทึกข้อมูลทั้งหมดได้ แต่คุณก็จะพบกับสแปมคอนโซลที่ไม่สามารถอ่านได้อย่างรวดเร็ว ตัวอย่างเช่น: [0,0,1,0,0,0.5,0,...] [0,0,0,0,1,0,0,...] [0,0,1,0,0,0,0,...]
คุณบอกได้ไหมว่ากดปุ่มอะไร? อาจจะ แต่หลังจากเพ่งสายตาและป้อนข้อมูลบางอย่างหายไปเท่านั้น ดังนั้น ไม่ การดีบักไม่ใช่เรื่องง่ายเมื่อพูดถึงการอ่านอินพุต ปัญหาที่ 3: ขาดโครงสร้าง แม้ว่าคุณจะรวมวิชวลไลเซอร์แบบรวดเร็วเข้าด้วยกัน แต่สไตล์ก็อาจยุ่งเหยิงได้อย่างรวดเร็ว สถานะเริ่มต้น ใช้งานอยู่ และแก้ไขข้อบกพร่องสามารถทับซ้อนกันได้ และหากไม่มีโครงสร้างที่ชัดเจน CSS ของคุณจะเปราะบางและขยายได้ยาก CSS Cascade Layers สามารถช่วยได้ พวกเขาจัดกลุ่มสไตล์เป็น “เลเยอร์” ที่เรียงลำดับตามลำดับความสำคัญ ดังนั้นคุณจึงหยุดต่อสู้กับความเฉพาะเจาะจงและคาดเดาว่า “ทำไมสไตล์การแก้ไขข้อบกพร่องของฉันจึงไม่แสดง” แต่คุณยังคงมีข้อกังวลแยกต่างหาก:
ฐาน: ลักษณะมาตรฐานของคอนโทรลเลอร์ ใช้งานอยู่: ไฮไลท์สำหรับปุ่มกดและแท่งที่ย้าย แก้ไขข้อบกพร่อง: โอเวอร์เลย์สำหรับนักพัฒนา (เช่น การอ่านตัวเลข คำแนะนำ และอื่นๆ)
หากเรากำหนดเลเยอร์ใน CSS ตามนี้ เราจะมี: /* ลำดับความสำคัญต่ำสุดถึงสูงสุด */ @layer base, ใช้งานอยู่, ดีบัก;
@เลเยอร์ฐาน { /* ... */ }
@layer ใช้งานอยู่ { /* ... */ }
@เลเยอร์ ดีบัก { /* ... */ }
เนื่องจากแต่ละเลเยอร์ซ้อนกันได้อย่างคาดเดาได้ คุณจึงรู้อยู่เสมอว่ากฎข้อใดชนะ ความสามารถในการคาดการณ์นั้นทำให้การดีบักไม่เพียงแต่ง่ายขึ้น แต่ยังจัดการได้จริงอีกด้วย เราได้กล่าวถึงปัญหาแล้ว (อินพุตที่มองไม่เห็นและยุ่งเหยิง) และวิธีการ (โปรแกรมดีบั๊กภาพที่สร้างด้วย Cascade Layers) ตอนนี้เราจะอธิบายกระบวนการทีละขั้นตอนเพื่อสร้างดีบักเกอร์ แนวคิดดีบักเกอร์ วิธีที่ง่ายที่สุดในการทำให้มองเห็นอินพุตที่ซ่อนอยู่คือเพียงแค่วาดมันลงบนหน้าจอ นั่นคือสิ่งที่ดีบักเกอร์นี้ทำ ปุ่ม ทริกเกอร์ และจอยสติ๊กล้วนมองเห็นได้
กด A: วงกลมจะสว่างขึ้น เขยิบไม้: วงกลมเลื่อนไปมา ดึงไกปืนลงครึ่งหนึ่ง: แท่งเหล็กจะเต็มครึ่งทาง
ตอนนี้คุณไม่ได้จ้องไปที่ 0 และ 1 แต่จริงๆ แล้วดูคอนโทรลเลอร์ตอบสนองแบบสดๆ แน่นอนว่า เมื่อคุณเริ่มซ้อนสถานะต่างๆ เช่น ข้อมูลเริ่มต้น ข้อมูลการกด ข้อมูลการแก้ไข หรือแม้แต่โหมดการบันทึก CSS ก็เริ่มมีขนาดใหญ่ขึ้นและซับซ้อนมากขึ้น นั่นคือจุดที่เลเยอร์แบบเรียงซ้อนมีประโยชน์ นี่เป็นตัวอย่างแบบแยกส่วน: @เลเยอร์ฐาน { .ปุ่ม { พื้นหลัง: #222; รัศมีชายแดน: 50%; ความกว้าง: 40px; ความสูง: 40px; } }
@layer ใช้งานอยู่ { .button.กด { พื้นหลัง: #0f0; /* สีเขียวสดใส */ } }
@เลเยอร์ ดีบัก { .button::หลังจาก { เนื้อหา: attr (ข้อมูล-ค่า); ขนาดตัวอักษร: 12px; สี: #fff; } }
ลำดับเลเยอร์มีความสำคัญ: base → active → debug
ฐานดึงตัวควบคุม สถานะการกดที่จับที่ใช้งานอยู่ การดีบักพ่นบนโอเวอร์เลย์
การแยกย่อยแบบนี้หมายความว่าคุณไม่ได้ต่อสู้กับสงครามความจำเพาะที่แปลกประหลาด แต่ละชั้นมีที่อยู่ของมัน และคุณจะรู้เสมอว่าอะไรชนะ สร้างมันออกมา มาทำอะไรบนหน้าจอกันก่อน ไม่จำเป็นต้องดูดี แค่ต้องมีอยู่ เพื่อที่เราจะได้มีบางอย่างที่ต้องทำ
ดีบักเกอร์ Gamepad Cascade
นั่นเป็นเพียงกล่องเท่านั้น ยังไม่น่าตื่นเต้น แต่มันช่วยให้เราสามารถคว้า CSS และ JavaScript ในภายหลังได้ โอเค ฉันใช้เลเยอร์แบบเรียงซ้อนที่นี่เพราะมันจะจัดระเบียบสิ่งต่าง ๆ เมื่อคุณเพิ่มสถานะเพิ่มเติม นี่เป็นการส่งผ่านคร่าวๆ:
/* ================================== การตั้งค่าเลเยอร์ CASCADE ความสำคัญของการสั่งซื้อ: ฐาน → ใช้งานอยู่ → ดีบัก =================================== */
/* กำหนดลำดับชั้นล่วงหน้า */ @layer base, ใช้งานอยู่, ดีบัก;
/* เลเยอร์ 1: รูปแบบพื้นฐาน - ลักษณะเริ่มต้น */ @เลเยอร์ฐาน { .ปุ่ม { พื้นหลัง: #333; รัศมีชายแดน: 50%; ความกว้าง: 70px; ความสูง: 70px; จอแสดงผล: ดิ้น; ปรับเนื้อหา: กึ่งกลาง; จัดรายการ: กึ่งกลาง; }
.หยุดชั่วคราว { ความกว้าง: 20px; ความสูง: 70px; พื้นหลัง: #333; จอแสดงผล: อินไลน์บล็อก; } }
/* เลเยอร์ 2: สถานะใช้งานอยู่ - จัดการกับปุ่มกด */ @layer ใช้งานอยู่ { .button.ใช้งานอยู่ { พื้นหลัง: #0f0; /* สีเขียวสดใสเมื่อกด */ แปลงร่าง: สเกล (1.1); /* ขยายปุ่มเล็กน้อย */ }
.pause.active { พื้นหลัง: #0f0; แปลงร่าง: สเกลY(1.1); /* ยืดออกในแนวตั้งเมื่อกด */ } }
/* เลเยอร์ 3: โอเวอร์เลย์แก้ไขข้อบกพร่อง - ข้อมูลนักพัฒนา */ @เลเยอร์ ดีบัก { .button::หลังจาก { เนื้อหา: attr (ข้อมูล-ค่า); /* แสดงค่าตัวเลข */ ขนาดตัวอักษร: 12px; สี: #fff; } }
ข้อดีของแนวทางนี้คือแต่ละชั้นมีจุดประสงค์ที่ชัดเจน เลเยอร์ฐานไม่สามารถแทนที่แอคทีฟได้ และแอคทีฟไม่สามารถแทนที่การดีบักได้ โดยไม่คำนึงถึงความจำเพาะ สิ่งนี้จะกำจัดสงครามความจำเพาะของ CSS ซึ่งมักจะรบกวนเครื่องมือแก้ไขข้อบกพร่อง ตอนนี้ดูเหมือนว่ามีกลุ่มบางส่วนกำลังนั่งอยู่บนพื้นหลังที่มืด สุจริตไม่เลวร้ายเกินไป
การเพิ่มจาวาสคริปต์ เวลาจาวาสคริปต์ นี่คือจุดที่ผู้ควบคุมทำอะไรบางอย่างจริงๆ เราจะสร้างสิ่งนี้ทีละขั้นตอน ขั้นตอนที่ 1: ตั้งค่าการจัดการสถานะ อันดับแรก เราต้องการตัวแปรเพื่อติดตามสถานะของดีบักเกอร์: // =================================== // การจัดการของรัฐ // ===================================
ปล่อยให้วิ่ง = เท็จ; // ติดตามว่าดีบักเกอร์ทำงานอยู่หรือไม่ ให้ rafId; // เก็บ requestAnimationFrame ID เพื่อยกเลิก
ตัวแปรเหล่านี้ควบคุมลูปภาพเคลื่อนไหวที่อ่านอินพุตเกมแพดอย่างต่อเนื่อง ขั้นตอนที่ 2: คว้าการอ้างอิง DOM ต่อไป เราจะได้รับการอ้างอิงถึงองค์ประกอบ HTML ทั้งหมดที่เราจะอัปเดต: // =================================== // การอ้างอิงองค์ประกอบ DOM // ===================================
const btnA = document.getElementById("btn-a"); const btnB = document.getElementById("btn-b"); const btnX = document.getElementById("btn-x"); const Pause1 = document.getElementById("pause1"); const Pause2 = document.getElementById("pause2"); สถานะ const = document.getElementById("สถานะ");
การจัดเก็บการอ้างอิงเหล่านี้ไว้ข้างหน้าจะมีประสิทธิภาพมากกว่าการสืบค้น DOM ซ้ำๆ ขั้นตอนที่ 3: เพิ่มทางเลือกแป้นพิมพ์ สำหรับการทดสอบโดยไม่มีตัวควบคุมทางกายภาพ เราจะจับคู่แป้นคีย์บอร์ดกับปุ่มต่างๆ: // =================================== // KEYBOARD FALLBACK (สำหรับการทดสอบโดยไม่มีคอนโทรลเลอร์) // ===================================
const keyMap = { "เป็น": btnA, "b": btnB, "x": btnX, "p": [pause1, Pause2] // ปุ่ม 'p' ควบคุมแถบหยุดชั่วคราวทั้งสองแถบ };
ซึ่งช่วยให้เราทดสอบ UI ได้โดยการกดปุ่มบนแป้นพิมพ์ ขั้นตอนที่ 4: สร้างลูปการอัปเดตหลัก นี่คือจุดที่ความมหัศจรรย์เกิดขึ้น ฟังก์ชั่นนี้ทำงานอย่างต่อเนื่องและอ่านสถานะเกมแพด: // =================================== // ลูปการอัปเดต GAMEPAD หลัก // ===================================
ฟังก์ชั่น updateGamepad() { // รับเกมแพดที่เชื่อมต่อทั้งหมด const gamepads = navigator.getGamepads(); ถ้า (!gamepads) กลับมา;
// ใช้เกมแพดแรกที่เชื่อมต่อ const gp = เกมแพด [0];
ถ้า (gp) { // อัปเดตสถานะปุ่มโดยการสลับคลาส "ใช้งานอยู่" btnA.classList.toggle("ใช้งานอยู่", gp.buttons[0].pressed); btnB.classList.toggle("ใช้งานอยู่", gp.buttons[1].pressed); btnX.classList.toggle("ใช้งานอยู่", gp.buttons[2].pressed);
// จัดการปุ่มหยุดชั่วคราว (ดัชนีปุ่ม 9 บนคอนโทรลเลอร์ส่วนใหญ่) const PausePressed = gp.buttons[9].กด; Pause1.classList.toggle("ใช้งานอยู่", PausePressed); Pause2.classList.toggle("ใช้งานอยู่", PausePressed);
// สร้างรายการปุ่มที่กดในปัจจุบันเพื่อแสดงสถานะ ให้กด = []; gp.buttons.forEach((btn, i) => { ถ้า (btn.pressed)pressed.push("ปุ่ม" + i); });
// อัปเดตข้อความสถานะหากมีการกดปุ่มใด ๆ ถ้า (ความยาวกด > 0) { status.textContent = "กดแล้ว: " + pressed.join(", "); } }
// ดำเนินการวนซ้ำต่อไปหากดีบักเกอร์กำลังทำงานอยู่ ถ้า (ทำงาน) { rafId = requestAnimationFrame (อัพเดตเกมแพด); } }
เมธอด classList.toggle() จะเพิ่มหรือลบคลาสที่ใช้งานอยู่โดยขึ้นอยู่กับว่ามีการกดปุ่มหรือไม่ ซึ่งจะทริกเกอร์สไตล์เลเยอร์ CSS ของเรา ขั้นตอนที่ 5: จัดการเหตุการณ์คีย์บอร์ด Listener เหตุการณ์เหล่านี้ทำให้การใช้แป้นพิมพ์สำรองทำงานได้: // =================================== // ตัวจัดการเหตุการณ์คีย์บอร์ด // ===================================
document.addEventListener("keydown", (e) => { ถ้า (keyMap[e.key]) { // จัดการองค์ประกอบเดียวหรือหลายองค์ประกอบ ถ้า (Array.isArray (keyMap [e.key])) { keyMap[e.key].forEach(el => el.classList.add("active")); } อื่น ๆ { keyMap[e.key].classList.add("ใช้งานอยู่"); } status.textContent = "กดปุ่ม: " + e.key.toUpperCase(); } });
document.addEventListener("คีย์อัพ", (e) => { ถ้า (keyMap[e.key]) { // ลบสถานะที่ใช้งานอยู่เมื่อปล่อยกุญแจ ถ้า (Array.isArray (keyMap [e.key])) { keyMap[e.key].forEach(el => el.classList.remove("ใช้งานอยู่")); } อื่น ๆ { keyMap[e.key].classList.remove("ใช้งานอยู่"); } status.textContent = "ปล่อยคีย์: " + e.key.toUpperCase(); } });
ขั้นตอนที่ 6: เพิ่มการควบคุมการเริ่ม/หยุด สุดท้ายนี้ เราต้องการวิธีเปิดและปิดดีบักเกอร์: // =================================== // สลับเปิด/ปิดดีบักเกอร์ // ===================================
document.getElementById("toggle").addEventListener("คลิก", () => { วิ่ง = !วิ่ง; // พลิกสถานะการทำงาน
ถ้า (ทำงาน) { status.textContent = "ดีบักเกอร์กำลังทำงาน..."; อัพเดตเกมแพด(); // เริ่มการวนรอบการอัปเดต } อื่น ๆ { status.textContent = "ดีบักเกอร์ไม่ทำงาน"; cancelAnimationFrame(rafId); //หยุดการวนซ้ำ } });
ใช่แล้ว กดปุ่มแล้วมันเรืองแสง ดันไม้เท้าแล้วมันเคลื่อนที่ แค่นั้นแหละ. อีกสิ่งหนึ่ง: คุณค่าดิบ บางครั้งคุณเพียงต้องการเห็นตัวเลข ไม่ใช่ไฟ
ในขั้นตอนนี้ คุณควรเห็น:
ตัวควบคุมบนหน้าจอที่เรียบง่าย ปุ่มที่ตอบสนองเมื่อคุณโต้ตอบกับปุ่มเหล่านั้นและ การอ่านค่าดีบักเพิ่มเติมซึ่งแสดงดัชนีปุ่มกด
เพื่อให้สิ่งนี้เป็นนามธรรมน้อยลง ต่อไปนี้เป็นตัวอย่างสั้นๆ ของตัวควบคุมบนหน้าจอที่ตอบสนองแบบเรียลไทม์:
ตอนนี้การกด Start Recording จะบันทึกทุกอย่างจนกว่าคุณจะกด Stop Recording 2. การส่งออกข้อมูลเป็น CSV/JSON เมื่อเรามีบันทึกแล้ว เราจะต้องการบันทึกมัน
ขั้นตอนที่ 1: สร้างตัวช่วยดาวน์โหลด ขั้นแรก เราต้องการฟังก์ชันตัวช่วยที่จัดการการดาวน์โหลดไฟล์ในเบราว์เซอร์: // =================================== // ตัวช่วยดาวน์โหลดไฟล์ // ===================================
ฟังก์ชั่นดาวน์โหลดไฟล์ (ชื่อไฟล์, เนื้อหา, ประเภท = "ข้อความ/ธรรมดา") { // สร้างหยดจากเนื้อหา const blob = new Blob([เนื้อหา], { ประเภท }); const url = URL.createObjectURL(หยด);
// สร้างลิงค์ดาวน์โหลดชั่วคราวแล้วคลิก const a = document.createElement("a"); ก.href = url; ก. ดาวน์โหลด = ชื่อไฟล์; ก.คลิก();
// ทำความสะอาด URL วัตถุหลังจากดาวน์โหลด setTimeout(() => URL.revoidObjectURL(url), 100); }
ฟังก์ชันนี้ทำงานโดยการสร้าง Blob (วัตถุไบนารีขนาดใหญ่) จากข้อมูลของคุณ สร้าง URL ชั่วคราวสำหรับข้อมูลนั้น และคลิกลิงก์ดาวน์โหลดโดยทางโปรแกรม การล้างข้อมูลทำให้มั่นใจได้ว่าเราจะไม่ทำให้หน่วยความจำรั่วไหล ขั้นตอนที่ 2: จัดการการส่งออก JSON JSON เหมาะอย่างยิ่งสำหรับการรักษาโครงสร้างข้อมูลที่สมบูรณ์:
// =================================== // ส่งออกเป็น JSON // ===================================
document.getElementById("export-json").addEventListener("คลิก", () => { // ตรวจสอบว่ามีอะไรที่จะส่งออกหรือไม่ ถ้า (!frames.length) { console.warn("ไม่มีการบันทึกที่สามารถส่งออกได้"); กลับ; }
// สร้างเพย์โหลดด้วยข้อมูลเมตาและเฟรม เพย์โหลด const = { สร้างขึ้นเมื่อ: วันที่ใหม่ ().toISOString() เฟรม };
// ดาวน์โหลดเป็นรูปแบบ JSON ดาวน์โหลดไฟล์( "gamepad-log.json", JSON.stringify(เพย์โหลด, โมฆะ, 2), "แอปพลิเคชัน / json" ); });
รูปแบบ JSON ช่วยให้ทุกอย่างมีโครงสร้างและแยกวิเคราะห์ได้ง่าย ทำให้เหมาะสำหรับการโหลดกลับเข้าสู่เครื่องมือ dev หรือแชร์กับเพื่อนร่วมทีม ขั้นตอนที่ 3: จัดการการส่งออก CSV สำหรับการส่งออก CSV เราจำเป็นต้องแบ่งข้อมูลตามลำดับชั้นออกเป็นแถวและคอลัมน์:
//=================================== // ส่งออกเป็น CSV // ===================================
document.getElementById("export-csv").addEventListener("คลิก", () => { // ตรวจสอบว่ามีอะไรที่จะส่งออกหรือไม่ ถ้า (!frames.length) { console.warn("ไม่มีการบันทึกที่สามารถส่งออกได้"); กลับ; }
// สร้างแถวส่วนหัว CSV (คอลัมน์สำหรับการประทับเวลา, ปุ่มทั้งหมด, แกนทั้งหมด) const headerButtons = เฟรม[0].buttons.map((_, i) => btn${i}); const headerAxes = เฟรม[0].axes.map((_, i) => axis${i}); const header = ["t", ...headerButtons, ...headerAxes].join(",") + "\n";
// สร้างแถวข้อมูล CSV แถว const = frames.map(f => { const btnVals = f.buttons.map(b => b.value); กลับ [ft, ...btnVals, ...f.axes].join(","); }).join("\n");
// ดาวน์โหลดเป็น CSV downloadFile("gamepad-log.csv", ส่วนหัว + แถว, "text/csv"); });
CSV เหมาะสำหรับการวิเคราะห์ข้อมูลเนื่องจากเปิดโดยตรงใน Excel หรือ Google ชีต ช่วยให้คุณสร้างแผนภูมิ กรองข้อมูล หรือมองเห็นรูปแบบได้ เมื่อปุ่มส่งออกเข้ามาแล้ว คุณจะเห็นสองตัวเลือกใหม่บนแผงควบคุม: ส่งออก JSON และส่งออก CSV JSON ดีถ้าคุณต้องการโยนบันทึกดิบกลับเข้าไปในเครื่องมือ dev ของคุณหรือสำรวจโครงสร้าง ในทางกลับกัน CSV จะเปิดโดยตรงใน Excel หรือ Google ชีต เพื่อให้คุณสามารถสร้างแผนภูมิ กรอง หรือเปรียบเทียบอินพุตได้ รูปต่อไปนี้จะแสดงลักษณะของแผงควบคุมพร้อมกับส่วนควบคุมพิเศษเหล่านั้น
3. ระบบสแนปชอต บางครั้งคุณไม่จำเป็นต้องมีการบันทึกแบบเต็ม เพียงแค่ "ภาพหน้าจอ" สั้นๆ ของสถานะอินพุต นั่นคือสิ่งที่ปุ่มถ่ายภาพสแนปชอตช่วยได้
และจาวาสคริปต์:
// =================================== // ถ่ายภาพสแนปชอต // ===================================
document.getElementById("snapshot").addEventListener("คลิก", () => { // รับเกมแพดที่เชื่อมต่อทั้งหมด แผ่น const = navigator.getGamepads(); const activePads = [];
// วนซ้ำและบันทึกสถานะของเกมแพดที่เชื่อมต่อแต่ละอัน สำหรับ (const gp ของแผ่นอิเล็กโทรด) { ถ้า (!gp) ดำเนินการต่อ; // ข้ามช่องว่าง
activePads.push ({ id: gp.id, // ชื่อ/รุ่นของคอนโทรลเลอร์ การประทับเวลา: Performance.now() ปุ่ม: gp.buttons.map(b => ({ กด: b.กด, ค่า: b.value })) แกน: [...gp.axes] }); }
// ตรวจสอบว่าพบแผ่นเกมหรือไม่ ถ้า (!activePads.length) { console.warn("ไม่มีเกมแพดที่เชื่อมต่อสำหรับสแน็ปช็อต"); alert("ไม่พบคอนโทรลเลอร์!"); กลับ; }
// เข้าสู่ระบบและแจ้งให้ผู้ใช้ทราบ console.log("สแนปชอต:", activePads); การแจ้งเตือน (ถ่ายภาพสแนปช็อต! จับคอนโทรลเลอร์ $ {activePads.length} ตัว); });
สแน็ปช็อตจะหยุดสถานะที่แน่นอนของคอนโทรลเลอร์ของคุณในช่วงเวลาหนึ่ง 4. เล่นซ้ำอินพุตผี ตอนนี้เพื่อความสนุก: เล่นซ้ำอินพุตผี วิธีนี้จะบันทึกและเล่นกลับด้วยสายตาเหมือนกับว่าผู้เล่น Phantom กำลังใช้คอนโทรลเลอร์
JavaScript สำหรับเล่นซ้ำ: // =================================== // ผีเล่นซ้ำ // ===================================
document.getElementById("เล่นซ้ำ").addEventListener("คลิก", () => { // ตรวจสอบให้แน่ใจว่าเรามีการบันทึกเพื่อเล่นซ้ำ ถ้า (!frames.length) { alert("ไม่มีการบันทึกให้เล่นซ้ำ!"); กลับ; }
console.log("กำลังเริ่มเล่นโกสต์ซ้ำ...");
// ติดตามระยะเวลาสำหรับการเล่นที่ซิงค์กัน ให้ startTime = Performance.now(); ให้ frameIndex = 0;
// เล่นซ้ำแอนิเมชั่นวนซ้ำ ฟังก์ชั่นขั้นตอน () { const ตอนนี้ = Performance.now(); const ที่ผ่านไป = ตอนนี้ - startTime;
// ประมวลผลเฟรมทั้งหมดที่ควรจะเกิดขึ้นในตอนนี้
ในขณะที่ (frameIndex // อัปเดต UI ด้วยสถานะปุ่มที่บันทึกไว้
btnA.classList.toggle("active", frame.buttons[0].pressed);
btnB.classList.toggle("active", frame.buttons[1].pressed);
btnX.classList.toggle("active", frame.buttons[2].pressed); // อัพเดตการแสดงสถานะ
ให้กด = [];
frame.buttons.forEach((btn, i) => {
ถ้า (btn.pressed) pressed.push("ปุ่ม " + i);
});
ถ้า (ความยาวกด > 0) {
status.textContent = "ผี: " + pressed.join(", ");
} เฟรมดัชนี++;
} // วนซ้ำต่อไปหากมีเฟรมเพิ่มเติม
ถ้า (frameIndex // เริ่มเล่นซ้ำ
ขั้นตอน();
}); เพื่อให้การดีบักใช้งานได้จริงมากขึ้น ฉันจึงเพิ่มการเล่นซ้ำแบบโกสต์ เมื่อคุณบันทึกเซสชันแล้ว คุณสามารถกดเล่นซ้ำและดู UI ดำเนินการได้ เกือบจะเหมือนกับว่ามีผู้เล่น Phantom กำลังเรียกใช้แพด ปุ่ม Replay Ghost ใหม่จะปรากฏขึ้นในแผงสำหรับสิ่งนี้ กดบันทึก ยุ่งกับคอนโทรลเลอร์เล็กน้อย หยุดแล้วเล่นซ้ำ UI จะสะท้อนทุกสิ่งที่คุณทำ เหมือนผีตามอินพุตของคุณ
ทำไมต้องกังวลกับสิ่งพิเศษเหล่านี้? การบันทึก/ส่งออกทำให้ผู้ทดสอบสามารถแสดงให้เห็นว่าเกิดอะไรขึ้นได้อย่างง่ายดาย
สแนปชอตจะหยุดนิ่งชั่วขณะ ซึ่งมีประโยชน์อย่างยิ่งเมื่อคุณไล่ตามจุดบกพร่องแปลกๆ
การเล่นซ้ำของ Ghost นั้นยอดเยี่ยมสำหรับการฝึกสอน การตรวจสอบการเข้าถึง หรือเพียงแค่เปรียบเทียบการตั้งค่าการควบคุมแบบเคียงข้างกัน ณ จุดนี้ มันไม่ใช่แค่การสาธิตที่เรียบร้อยอีกต่อไป แต่เป็นสิ่งที่คุณสามารถนำไปใช้งานได้จริง
กรณีการใช้งานจริง
ตอนนี้เรามีดีบักเกอร์ที่สามารถทำอะไรได้มากมาย มันแสดงอินพุตสด บันทึกบันทึก ส่งออกและแม้แต่เล่นซ้ำ แต่คำถามที่แท้จริงคือใครสนใจจริงๆ? สิ่งนี้มีประโยชน์สำหรับใคร?
นักพัฒนาเกม
คอนโทรลเลอร์เป็นส่วนหนึ่งของงาน แต่กำลังแก้ไขข้อบกพร่องใช่ไหม มักมีอาการปวด ลองจินตนาการว่าคุณกำลังทดสอบคอมโบเกมต่อสู้ เช่น ↓ → + ต่อย แทนที่จะสวดมนต์ คุณกดมันด้วยวิธีเดิมสองครั้ง บันทึกหนึ่งครั้ง แล้วเล่นซ้ำ เสร็จแล้ว หรือคุณสลับบันทึก JSON กับเพื่อนร่วมทีมเพื่อตรวจสอบว่าโค้ดผู้เล่นหลายคนของคุณตอบสนองเหมือนกันบนเครื่องของพวกเขาหรือไม่ นั่นมันใหญ่มาก
ผู้ปฏิบัติงานด้านการเข้าถึง
อันนี้ใกล้กับหัวใจของฉัน ไม่ใช่ทุกคนที่เล่นกับคอนโทรลเลอร์ "มาตรฐาน" ตัวควบคุมแบบปรับเปลี่ยนได้จะส่งสัญญาณแปลก ๆ ออกมาในบางครั้ง ด้วยเครื่องมือนี้ คุณจะเห็นได้อย่างชัดเจนว่าเกิดอะไรขึ้น ครู นักวิจัย หรือใครก็ตาม พวกเขาสามารถหยิบบันทึก เปรียบเทียบ หรือเล่นซ้ำอินพุตแบบเคียงข้างกัน ทันใดนั้นสิ่งที่มองไม่เห็นก็ปรากฏชัดเจน
การทดสอบการประกันคุณภาพ
ผู้ทดสอบมักจะเขียนข้อความ เช่น “ฉันบดกระดุมตรงนี้แล้วมันพัง” ไม่ค่อยมีประโยชน์ ตอนนี้? พวกเขาสามารถจับภาพการกดที่แน่นอน ส่งออกบันทึก และส่งออกไปได้ ไม่มีการคาดเดา
นักการศึกษา
หากคุณกำลังสร้างบทช่วยสอนหรือวิดีโอ YouTube การเล่นซ้ำแบบโกสต์ถือเป็นเรื่องทอง คุณสามารถพูดได้อย่างแท้จริงว่า “นี่คือสิ่งที่ฉันทำกับคอนโทรลเลอร์” ในขณะที่ UI แสดงให้เห็นว่ามันเกิดขึ้น ทำให้คำอธิบายชัดเจนยิ่งขึ้น
นอกเหนือจากเกม
และใช่แล้ว นี่ไม่ใช่แค่เกี่ยวกับเกมเท่านั้น ผู้คนได้ใช้ตัวควบคุมสำหรับหุ่นยนต์ โครงการศิลปะ และอินเทอร์เฟซสำหรับการเข้าถึง ปัญหาเดียวกันทุกครั้ง: เบราว์เซอร์เห็นอะไรจริงๆ ด้วยวิธีนี้คุณไม่จำเป็นต้องเดา
บทสรุป
การดีบักอินพุตคอนโทรลเลอร์ให้ความรู้สึกเหมือนบินตาบอดอยู่เสมอ ต่างจาก DOM หรือ CSS ตรงที่ไม่มีตัวตรวจสอบเกมแพดในตัว มันเป็นเพียงตัวเลขดิบในคอนโซลซึ่งหายไปอย่างง่ายดายด้วยเสียง
ด้วย HTML, CSS และ JavaScript ไม่กี่ร้อยบรรทัด เราได้สร้างสิ่งที่แตกต่างออกไป: ดีบักเกอร์แบบวิชวลที่ทำให้อินพุตที่มองไม่เห็นมองเห็นได้
ระบบ CSS แบบเลเยอร์ที่ช่วยให้ UI สะอาดและแก้ไขจุดบกพร่องได้
ชุดการปรับปรุง (การบันทึก การส่งออก สแน็ปช็อต เล่นซ้ำแบบโกสต์) ที่ยกระดับจากการสาธิตไปสู่เครื่องมือสำหรับนักพัฒนา โปรเจ็กต์นี้แสดงให้เห็นว่าคุณสามารถไปได้ไกลแค่ไหนโดยการผสมผสานประสิทธิภาพของแพลตฟอร์มเว็บเข้ากับความคิดสร้างสรรค์เล็กๆ น้อยๆ ใน CSS Cascade Layers
เครื่องมือที่ฉันเพิ่งอธิบายไปทั้งหมดคือโอเพ่นซอร์ส คุณสามารถโคลน repo GitHub และลองด้วยตัวคุณเอง
แต่ที่สำคัญกว่านั้นคือคุณสามารถทำมันเองได้ เพิ่มเลเยอร์ของคุณเอง สร้างตรรกะการเล่นซ้ำของคุณเอง รวมเข้ากับต้นแบบเกมของคุณ หรือแม้กระทั่งนำไปใช้ในแบบที่ผมนึกไม่ถึง สำหรับการสอน การเข้าถึง หรือการวิเคราะห์ข้อมูล
ท้ายที่สุดแล้ว นี่ไม่ใช่แค่การดีบักเกมแพดเท่านั้น มันเกี่ยวกับการฉายแสงให้กับอินพุตที่ซ่อนอยู่ และให้ความมั่นใจแก่นักพัฒนาในการทำงานกับฮาร์ดแวร์ที่เว็บยังไม่รองรับอย่างเต็มที่
ดังนั้น เสียบคอนโทรลเลอร์ของคุณ เปิดโปรแกรมแก้ไข และเริ่มการทดลอง คุณอาจแปลกใจกับสิ่งที่เบราว์เซอร์และ CSS ของคุณสามารถบรรลุผลสำเร็จได้อย่างแท้จริง