وقتی یک کنترلر را وصل میکنید، دکمهها را له میکنید، چوبها را حرکت میدهید، ماشهها را میکشید... و بهعنوان یک توسعهدهنده، هیچیک از آنها را نمیبینید. مطمئناً مرورگر در حال برداشتن آن است، اما مگر اینکه اعداد را در کنسول ثبت کنید، نامرئی است. این سردرد با 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 تعریف کنیم، خواهیم داشت: /* کمترین تا بالاترین اولویت */ پایه لایه @، فعال، اشکال زدایی.
@ پایه لایه { /* ... */ }
@لایه فعال { /* ... */ }
رفع اشکال لایه @ /* ... */ }
از آنجا که هر لایه به طور قابل پیش بینی روی هم قرار می گیرد، همیشه می دانید که کدام قوانین برنده می شوند. این قابلیت پیش بینی باعث می شود که اشکال زدایی نه تنها آسان تر، بلکه در واقع قابل مدیریت باشد. ما مشکل (ورودی نامرئی و نامرتب) و رویکرد (اشکالزدای بصری ساخته شده با لایههای Cascade) را پوشش دادهایم. اکنون مراحل گام به گام ساختن دیباگر را طی می کنیم. مفهوم اشکال زدا ساده ترین راه برای نمایان کردن ورودی مخفی این است که فقط آن را روی صفحه بکشید. این همان کاری است که این دیباگر انجام می دهد. دکمهها، ماشهها و جوی استیکها همگی جلوه بصری پیدا میکنند.
A را فشار دهید: یک دایره روشن می شود. تکان دادن چوب: دایره به اطراف می لغزد. یک ماشه را تا نیمه بکشید: یک میله تا نیمه پر می شود.
اکنون به 0 و 1 خیره نشده اید، بلکه در واقع واکنش کنترلر را به صورت زنده تماشا می کنید. البته، هنگامی که شروع به جمع کردن حالت هایی مانند اطلاعات پیش فرض، فشرده، اشکال زدایی، شاید حتی حالت ضبط کنید، CSS بزرگتر و پیچیده تر می شود. اینجاست که لایه های آبشاری به کار می آیند. در اینجا یک مثال ساده آورده شده است: @ پایه لایه { دکمه { پس زمینه: #222; شعاع مرزی: 50%; عرض: 40 پیکسل؛ ارتفاع: 40 پیکسل؛ } }
@لایه فعال { دکمه.فشرده شده { پس زمینه: #0f0; /*سبز روشن*/ } }
رفع اشکال لایه @ .button::بعد از { محتوا: attr(data-value); اندازه فونت: 12px; رنگ: #fff; } }
ترتیب لایه مهم است: پایه → فعال → اشکال زدایی.
پایه کنترلر را ترسیم می کند. حالت های فشرده را کنترل می کند. اشکال زدایی روی همپوشانی ها پرتاب می شود.
شکستن آن به این معنی است که شما در حال مبارزه با جنگ های عجیب و غریب نیستید. هر لایه جای خود را دارد و شما همیشه می دانید چه چیزی برنده می شود. ساختن آن بیایید ابتدا چیزی را روی صفحه نمایش دهیم. نیازی نیست که خوب به نظر برسد - فقط باید وجود داشته باشد تا چیزی برای کار کردن داشته باشیم.
Debugger Cascade Gamepad
این به معنای واقعی کلمه فقط جعبه است. هنوز هیجان انگیز نیست، اما در اختیار ما قرار می دهد تا بعداً با CSS و جاوا اسکریپت به دست آوریم. خوب، من از لایههای آبشاری در اینجا استفاده میکنم زیرا وقتی حالتهای بیشتری را اضافه کنید، موارد را مرتب نگه میدارد. در اینجا یک پاس خشن است:
/* =================================== راه اندازی لایه های آبشاری سفارش مهم است: پایه → فعال → اشکال زدایی ===================================*/
/* ترتیب لایه ها را از قبل تعریف کنید */ پایه لایه @، فعال، اشکال زدایی.
/* لایه 1: سبک های پایه - ظاهر پیش فرض */ @ پایه لایه { دکمه { پس زمینه: #333; شعاع مرزی: 50%; عرض: 70 پیکسل؛ ارتفاع: 70px; صفحه نمایش: انعطاف پذیر; justify-content: center; align-اقلام: مرکز; }
مکث { عرض: 20 پیکسل؛ ارتفاع: 70px; پس زمینه: #333; صفحه نمایش: inline-block; } }
/* لایه 2: حالت های فعال - دکمه های فشرده را کنترل می کند */ @لایه فعال { .button.active { پس زمینه: #0f0; /* با فشار دادن سبز روشن */ transform: scale(1.1); /* دکمه را کمی بزرگ می کند */ }
.pause.active { پس زمینه: #0f0; transform: scaleY(1.1); /* با فشار دادن */ به صورت عمودی کشیده می شود } }
/* لایه 3: همپوشانی اشکال زدایی - اطلاعات توسعه دهنده */ رفع اشکال لایه @ .button::بعد از { محتوا: attr(data-value); /* مقدار عددی را نشان می دهد */ اندازه فونت: 12px; رنگ: #fff; } }
زیبایی این رویکرد این است که هر لایه هدف مشخصی دارد. لایه پایه هرگز نمی تواند فعال را نادیده بگیرد، و فعال هرگز نمی تواند اشکال زدایی را لغو کند، صرف نظر از ویژگی. این امر جنگ های اختصاصی CSS را که معمولاً ابزارهای اشکال زدایی را آزار می دهد، از بین می برد. اکنون به نظر می رسد که برخی از خوشه ها روی یک پس زمینه تاریک نشسته اند. راستش خیلی هم بد نیست.
افزودن جاوا اسکریپت زمان جاوا اسکریپت اینجاست که کنترلر در واقع کاری انجام می دهد. ما این را گام به گام خواهیم ساخت. مرحله 1: مدیریت دولتی را تنظیم کنید ابتدا به متغیرهایی برای ردیابی وضعیت دیباگر نیاز داریم: // =================================== // مدیریت دولتی // ===================================
let running = false; // فعال بودن دیباگر را ردیابی می کند let rafId; // شناسه requestAnimationFrame را برای لغو ذخیره می کند
این متغیرها حلقه انیمیشن را کنترل می کنند که به طور مداوم ورودی گیم پد را می خواند. مرحله 2: منابع DOM را بگیرید در مرحله بعد، ما به تمام عناصر HTML که به روز می کنیم ارجاع می دهیم: // =================================== // DOM ELEMENT References // ===================================
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 status = document.getElementById("وضعیت");
ذخیره سازی این مراجع در جلو کارآمدتر از پرس و جوی مکرر DOM است. مرحله 3: اضافه کردن صفحه کلید بازگشتی برای آزمایش بدون کنترلکننده فیزیکی، کلیدهای صفحهکلید را به دکمهها نگاشت میکنیم: // =================================== // KEYBOARD FALLBACK (برای آزمایش بدون کنترلر) // ===================================
Const KeyMap = { "a": btnA، "b": btnB، "x": btnX، "p": [pause1, pause2] // کلید 'p' هر دو نوار مکث را کنترل می کند };
این به ما امکان میدهد UI را با فشار دادن کلیدهای روی صفحهکلید آزمایش کنیم. مرحله 4: حلقه به روز رسانی اصلی را ایجاد کنید اینجا جایی است که جادو اتفاق می افتد. این تابع به طور مداوم اجرا می شود و وضعیت صفحه بازی را می خواند: // =================================== // حلقه به روز رسانی اصلی بازی // ===================================
تابع updateGamepad() { // همه گیم پدهای متصل را دریافت کنید const gamepads = navigator.getGamepads(); اگر (!gamepads) بازگشت.
// از اولین گیم پد متصل استفاده کنید const gp = gamepads[0];
اگر (gp) { // وضعیت های دکمه به روز رسانی با جابجایی کلاس "active". btnA.classList.toggle("active"، gp.buttons[0].pressed); btnB.classList.toggle("active"، gp.buttons[1].pressed); btnX.classList.toggle("active"، gp.buttons[2].pressed);
// دکمه مکث دسته (نمایش دکمه 9 در اکثر کنترلرها) const pausePressed = gp.buttons[9].pressed; pause1.classList.toggle("active", pausePressed); pause2.classList.toggle("active", pausePressed);
// لیستی از دکمه های فشار داده شده در حال حاضر برای نمایش وضعیت ایجاد کنید let pressed = []; gp.buttons.forEach((btn, i) => { اگر (btn.pressed)pressed.push("Button" + i); })؛
// در صورت فشار دادن هر یک از دکمه ها، متن وضعیت را به روز کنید if (pressed.length > 0) { status.textContent = "فشرده شده: " + pressed.join(", "); } }
// اگر دیباگر در حال اجرا است، حلقه را ادامه دهید اگر (در حال اجرا) { rafId = requestAnimationFrame(updateGamepad); } }
متد classList.toggle () کلاس فعال را بر اساس فشار دادن دکمه اضافه یا حذف می کند، که سبک های لایه CSS ما را فعال می کند. مرحله 5: رویدادهای صفحه کلید را مدیریت کنید این شنوندههای رویداد باعث میشوند صفحه کلید مجدد کار کند: // =================================== // کنترل کننده رویداد صفحه کلید // ===================================
document.addEventListener("keydown", (e) => { if (keyMap[e.key]) { // عناصر تک یا چندگانه را کنترل کنید if (Array.isArray(keyMap[e.key])) { keyMap[e.key].forEach(el => el.classList.add("فعال")); }دیگر { keyMap[e.key].classList.add("فعال"); } status.textContent = "کلید فشرده شد: " + e.key.toUpperCase(); } })؛
document.addEventListener("keyup", (e) => { if (keyMap[e.key]) { // وقتی کلید آزاد شد حالت فعال را حذف کنید if (Array.isArray(keyMap[e.key])) { keyMap[e.key].forEach(el => el.classList.remove("فعال")); }دیگر { keyMap[e.key].classList.remove("فعال"); } status.textContent = "کلید منتشر شد: " + e.key.toUpperCase(); } })؛
مرحله 6: کنترل Start/Stop را اضافه کنید در نهایت، ما به راهی برای روشن و خاموش کردن دیباگر نیاز داریم: // =================================== // TOGLE DEBUGGER ON/OFF // ===================================
document.getElementById("toggle").addEventListener("کلیک کنید"، () => { در حال اجرا = !running; // وضعیت در حال اجرا را برگردانید
اگر (در حال اجرا) { status.textContent = "اشکال زدا در حال اجرا..."; updateGamepad(); // حلقه به روز رسانی را شروع کنید }دیگر { status.textContent = "اشکال زدا غیر فعال"; cancelAnimationFrame(rafId); // حلقه را متوقف کنید } })؛
بنابراین بله، یک دکمه را فشار دهید و می درخشد. چوب را فشار دهید و حرکت می کند. همین است. یک چیز دیگر: ارزش های خام. گاهی اوقات شما فقط می خواهید اعداد را ببینید، نه نورها را.
در این مرحله باید ببینید:
یک کنترلر ساده روی صفحه، دکمه هایی که هنگام تعامل با آنها واکنش نشان می دهند و یک بازخوانی اشکال زدایی اختیاری که شاخص های فشار داده شده دکمه را نشان می دهد.
برای اینکه این موضوع کمتر انتزاعی شود، در اینجا یک نسخه نمایشی سریع از واکنش کنترلر روی صفحه در زمان واقعی ارائه شده است:
اکنون، با فشار دادن Start Recording همه چیز ثبت می شود تا زمانی که Stop Recording را بزنید. 2. صادرات داده به CSV/JSON هنگامی که یک گزارش داشته باشیم، می خواهیم آن را ذخیره کنیم.
مرحله 1: راهنما دانلود را ایجاد کنید ابتدا به یک تابع کمکی نیاز داریم که بارگیری فایل ها را در مرورگر مدیریت کند: // =================================== // FILE DOWNLOAD HELPER // ===================================
function downloadFile (نام فایل، محتوا، نوع = "text/plain") { // یک حباب از محتوا ایجاد کنید const blob = new Blob([content], { type }); const url = URL.createObjectURL(blob);
// یک لینک دانلود موقت ایجاد کنید و روی آن کلیک کنید const a = document.createElement("a"); a.href = url; a.download = نام فایل; a.click();
// URL شیء را پس از دانلود پاک کنید setTimeout(() => URL.revokeObjectURL(url), 100); }
این تابع با ایجاد یک Blob (شئی بزرگ باینری) از داده های شما، ایجاد یک URL موقت برای آن و کلیک برنامه نویسی بر روی لینک دانلود کار می کند. پاکسازی تضمین میکند که حافظه را از بین نبریم. مرحله 2: صادرات JSON را مدیریت کنید JSON برای حفظ ساختار داده کامل است:
// =================================== // صادرات به عنوان JSON // ===================================
document.getElementById("export-json").addEventListener("کلیک کنید"، () => { // بررسی کنید که آیا چیزی برای صادرات وجود دارد یا خیر if (!frames.length) { console.warn("ضبطی برای صادرات موجود نیست."); بازگشت؛ }
// با متادیتا و فریم ها یک پیلود ایجاد کنید صرف بار = { createAt: new Date().toISOSstring()، قاب ها };
// با فرمت JSON دانلود کنید دانلود فایل( "gamepad-log.json"، JSON.stringify(payload، null، 2)، "application/json" ) })؛
فرمت JSON همه چیز را ساختار یافته و به راحتی قابل تجزیه نگه می دارد، و آن را برای بارگیری مجدد در ابزارهای توسعه دهنده یا اشتراک گذاری با هم تیمی ها ایده آل می کند. مرحله 3: صادرات CSV را مدیریت کنید برای صادرات CSV، باید داده های سلسله مراتبی را به ردیف و ستون مسطح کنیم:
//=================================== // صادرات به عنوان CSV // ===================================
document.getElementById("export-csv").addEventListener("کلیک کنید"، () => { // بررسی کنید که آیا چیزی برای صادرات وجود دارد یا خیر if (!frames.length) { console.warn("ضبطی برای صادرات موجود نیست."); بازگشت؛ }
// ساخت ردیف سرصفحه CSV (ستون ها برای مهر زمانی، همه دکمه ها، همه محورها) const headerButtons = frames[0].buttons.map((_, i) => btn${i}); const headerAxes = frames[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); بازگشت [f.t، ...btnVals، ...f.axes].join(""); }).join("\n");
// دانلود به صورت CSV downloadFile ("gamepad-log.csv"، header + rows، "text/csv"); })؛
CSV برای تجزیه و تحلیل داده ها بسیار عالی است زیرا مستقیماً در Excel یا Google Sheets باز می شود و به شما امکان می دهد نمودارها را ایجاد کنید، داده ها را فیلتر کنید یا الگوهای بصری را مشاهده کنید. اکنون که دکمههای صادرات فعال هستند، دو گزینه جدید را در پانل مشاهده خواهید کرد: Export JSON و Export CSV. JSON خوب است اگر می خواهید گزارش خام را به ابزارهای توسعه دهنده خود برگردانید یا ساختار را بررسی کنید. از طرف دیگر، CSV مستقیماً در Excel یا Google Sheets باز می شود تا بتوانید ورودی ها را نمودار، فیلتر یا مقایسه کنید. شکل زیر نشان می دهد که پانل با آن کنترل های اضافی چگونه به نظر می رسد.
3. سیستم عکس فوری گاهی اوقات شما نیازی به ضبط کامل ندارید، فقط به یک "اسکرین شات" سریع از وضعیت های ورودی نیاز دارید. اینجاست که دکمه Take Snapshot کمک می کند.
و جاوا اسکریپت:
// =================================== // TAKE SNAPSHOT // ===================================
document.getElementById("snapshot").addEventListener("click", () => { // همه گیم پدهای متصل را دریافت کنید const pads = navigator.getGamepads(); const activePads = [];
// حلقه بزنید و وضعیت هر گیم پد متصل را ثبت کنید برای (const gp of pads) { اگر (!gp) ادامه یابد؛ // از اسلات خالی رد شوید
activePads.push({ شناسه: gp.id، // نام/مدل کنترلر برچسب زمانی: performance.now()، دکمه ها: gp.buttons.map(b => ({ فشرده: ب.فشرده شده، ارزش: b.value })) محورها: [...gp.axes] })؛ }
// بررسی کنید که آیا گیم پد پیدا شده است if (!activePads.length) { console.warn("هیچ صفحه بازی برای عکس فوری وصل نشده است."); هشدار ("هیچ کنترل کننده ای شناسایی نشد!"); بازگشت؛ }
// وارد سیستم شوید و به کاربر اطلاع دهید console.log("Snapshot:", activePads); alert(عکس فوری گرفته شد! کنترلر(های) ${activePads.length} گرفته شد.); })؛
عکس های فوری وضعیت دقیق کنترلر شما را در یک لحظه ثابت می کند. 4. Ghost Input Replay اکنون برای سرگرمی: پخش مجدد ورودی شبح. این یک گزارش را می گیرد و آن را به صورت بصری پخش می کند که گویی یک پخش کننده فانتوم از کنترلر استفاده می کند.
جاوا اسکریپت برای پخش مجدد: // =================================== // GHOST REPLAY // ===================================
document.getElementById("replay").addEventListener("click", () => { // مطمئن شوید که ضبطی برای پخش مجدد داریم if (!frames.length) { هشدار ("ضبط برای پخش مجدد وجود ندارد!"); بازگشت؛ }
console.log("شروع پخش مجدد شبح...");
// پیگیری زمان برای پخش همگامسازی شده let startTime = performance.now(); اجازه دهید frameIndex = 0;
// پخش مجدد حلقه انیمیشن تابع مرحله () { const now = performance.now(); const elapsed = now - startTime;
// پردازش تمام فریم هایی که باید تا کنون رخ داده باشند while (frameIndex < frames.length && frames[frameIndex].t <= سپری شده) { قاب const = فریم[frameIndex];
// رابط کاربری را با حالت های دکمه ضبط شده به روز کنید btnA.classList.toggle("active", frame.buttons[0].pressed); btnB.classList.toggle("active", frame.buttons[1].pressed); btnX.classList.toggle("active", frame.buttons[2].pressed);
// نمایش وضعیت را به روز کنید let pressed = []; frame.buttons.forEach((btn, i) => { if (btn.pressed) pressed.push("Button" + i); })؛ if (pressed.length > 0) { status.textContent = "شبح: " + pressed.join(", "); }
FrameIndex++; }
// اگر فریم های بیشتری وجود دارد، حلقه را ادامه دهید if (frameIndex < frames.length) { requestAnimationFrame (مرحله); }دیگر { console.log("بازپخشتمام شد.") status.textContent = "بازپخش کامل شد"; } }
// پخش مجدد را شروع کنید step(); })؛
برای اینکه اشکال زدایی کمی عملی تر شود، یک پخش ارواح اضافه کردم. هنگامی که یک جلسه را ضبط کردید، می توانید replay را فشار دهید و رابط کاربری را تماشا کنید، تقریباً مانند یک پخش کننده فانتوم که در حال اجرای پد است. یک دکمه Replay Ghost جدید برای این کار در پانل ظاهر می شود.
Record را بزنید، کمی کنترلر را به هم بزنید، توقف کنید، سپس دوباره پخش کنید. رابط کاربری فقط هر کاری را که انجام دادید بازتاب می دهد، مانند شبحی که ورودی های شما را دنبال می کند. چرا خود را با این موارد اضافی خسته کنید؟
ضبط/صادرات این امکان را برای آزمایشکنندگان آسان میکند که دقیقاً چه اتفاقی افتاده است. عکسهای فوری لحظهای در زمان ثابت میشوند، زمانی که در حال تعقیب باگهای عجیب و غریب هستید، بسیار مفید است. پخش مجدد Ghost برای آموزش، بررسی دسترسپذیری، یا فقط مقایسه تنظیمات کنترل در کنار هم عالی است.
در این مرحله، دیگر فقط یک نسخه ی نمایشی منظم نیست، بلکه چیزی است که می توانید عملاً آن را اجرا کنید. موارد استفاده در دنیای واقعی اکنون ما این دیباگر را داریم که می تواند کارهای زیادی انجام دهد. ورودی زنده را نشان میدهد، گزارشها را ضبط میکند، آنها را صادر میکند و حتی موارد را دوباره پخش میکند. اما سوال واقعی این است: چه کسی واقعاً اهمیت می دهد؟ این برای چه کسانی مفید است؟ توسعه دهندگان بازی کنترلرها بخشی از کار هستند، اما اشکال زدایی آنها؟ معمولا یک درد تصور کنید در حال آزمایش یک ترکیب بازی مبارزه ای هستید، مانند ↓ → + پانچ. به جای دعا، دو بار آن را به همان شکل فشار دادید، یک بار آن را ضبط می کنید و دوباره پخش می کنید. انجام شد. یا لاگهای JSON را با یکی از هم تیمیهای خود تعویض میکنید تا بررسی کنید که آیا کد چندنفره شما در دستگاه آنها یکسان واکنش نشان میدهد یا خیر. این بزرگ است. پزشکان دسترسی این یکی به قلب من نزدیک است همه با یک کنترلر "استاندارد" بازی نمی کنند. کنترل کننده های تطبیقی گاهی اوقات سیگنال های عجیب و غریبی را پرتاب می کنند. با استفاده از این ابزار می توانید دقیقاً آنچه را که اتفاق می افتد مشاهده کنید. معلمان، محققین، هر کسی که باشد. آنها می توانند سیاهههای مربوط را بگیرند، آنها را مقایسه کنند، یا ورودی ها را در کنار هم پخش کنند. ناگهان چیزهای نامرئی آشکار می شوند. تست تضمین کیفیت آزمایشکنندگان معمولاً یادداشتهایی مانند «دکمهها را له کردم و خراب شد» مینویسند. خیلی مفید نیست حالا؟ آنها می توانند فشارهای دقیق را ضبط کنند، گزارش را صادر کرده و آن را ارسال کنند. بدون حدس زدن مربیان اگر در حال ساختن آموزش ها یا ویدیوهای یوتیوب هستید، پخش شبح طلایی است. شما می توانید به معنای واقعی کلمه بگویید، "اینجا کاری است که من با کنترلر انجام دادم"، در حالی که رابط کاربری نشان می دهد که این اتفاق می افتد. توضیحات را بسیار واضح تر می کند. فراتر از بازی ها و بله، این فقط مربوط به بازی ها نیست. مردم از کنترلکنندهها برای روباتها، پروژههای هنری و رابطهای دسترسی استفاده کردهاند. هر بار یک مسئله مشابه: مرورگر در واقع چه چیزی را می بیند؟ با این کار، لازم نیست حدس بزنید. نتیجه گیری اشکال زدایی ورودی کنترلر همیشه شبیه به پرواز کور بوده است. برخلاف DOM یا CSS، هیچ بازرسی داخلی برای گیمپدها وجود ندارد. فقط اعداد خام در کنسول هستند که به راحتی در نویز گم می شوند. با چند صد خط HTML، CSS و جاوا اسکریپت، چیزی متفاوت ساختیم:
یک دیباگر بصری که ورودی های نامرئی را قابل مشاهده می کند. یک سیستم لایه لایه CSS که رابط کاربری را تمیز و قابل اشکال زدایی نگه می دارد. مجموعهای از پیشرفتها (ضبط، صادرات، عکسهای فوری، پخش شبح) که آن را از نسخه آزمایشی به ابزار توسعهدهنده ارتقا میدهد.
این پروژه نشان میدهد که با ترکیب کردن قدرت پلتفرم وب با کمی خلاقیت در لایههای آبشار CSS چقدر میتوانید پیش بروید. ابزاری که به طور کامل توضیح دادم منبع باز است. می توانید مخزن GitHub را شبیه سازی کنید و خودتان آن را امتحان کنید. اما مهمتر از آن، شما می توانید آن را خودتان بسازید. لایه های خود را اضافه کنید. منطق پخش خود را بسازید. آن را با نمونه اولیه بازی خود ادغام کنید. یا حتی از آن به روش هایی استفاده کنم که تصورش را نمی کردم. برای آموزش، دسترسی یا تجزیه و تحلیل داده ها. در پایان، این فقط مربوط به اشکال زدایی گیم پدها نیست. این در مورد روشن کردن ورودی های پنهان است و به توسعه دهندگان این اطمینان را می دهد که با سخت افزاری کار کنند که وب هنوز به طور کامل از آن استفاده نمی کند. بنابراین، کنترلر خود را وصل کنید، ویرایشگر خود را باز کنید و شروع به آزمایش کنید. ممکن است از آنچه که مرورگر و CSS شما واقعاً می توانند انجام دهند شگفت زده شوید.