Кога ќе приклучите контролер, ги матите копчињата, ги поместувате стапчињата, ги повлекувате чкрапалите... и како развивач, не гледате ништо од тоа. Прелистувачот, секако, го зема, но освен ако не запишувате броеви во конзолата, тоа е невидливо. Тоа е главоболката со 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 каскадните слоеви можат да помогнат. Тие ги групираат стиловите во „слоеви“ кои се подредени по приоритет, така што ќе престанете да се борите со специфичноста и да погодувате: „Зошто не се прикажува мојот стил за отстранување грешки? Наместо тоа, одржувате посебни грижи:

Основа: Стандард, првичен изглед на контролорот. Активни: Определување за притиснати копчиња и поместени стапчиња. Отстранување грешки: Преклопувања за програмери (на пр., нумерички отчитувања, водичи и така натаму).

Ако ги дефинираме слоевите во CSS според ова, ќе имаме: /* најнизок до највисок приоритет */ @layer база, активна, дебагирање;

@слој база { /* ... */ }

@layer активен { /* ... */ }

Отстранување грешки на @layer { /* ... */ }

Бидејќи секој слој се натрупува предвидливо, секогаш знаете кои правила победуваат. Таа предвидливост го прави дебагирањето не само полесно, туку всушност може да се управува. Го опфативме проблемот (невидлив, неуреден влез) и пристапот (визуелен дебагер изграден со каскадни слоеви). Сега ќе одиме низ процесот чекор-по-чекор за да го изградиме дебагерот. Концептот за дебагер Најлесен начин да се направи видлив скриениот влез е само да го нацртате на екранот. Тоа го прави овој дебагер. Копчињата, тригерите и џојстиците добиваат визуелен изглед.

Притиснете A: Се пали круг. Придвижете го стапот: Кругот се лизга наоколу. Повлечете го чкрапалото до половина: шипката се полни до половина.

Сега не гледате во 0 и 1, туку всушност гледате како контролорот реагира во живо. Се разбира, штом ќе почнете да натрупувате состојби како стандардни, притиснати, информации за отстранување грешки, можеби дури и режим на снимање, CSS почнува да станува се поголем и покомплексен. Тоа е местото каде што каскадните слоеви доаѓаат во рака. Еве еден соголен пример: @слој база { .копче { позадина: #222; граница-радиус: 50%; ширина: 40 px; висина: 40 px; } }

@layer активен { .копче.притиснато { позадина: #0f0; /* светло зелена */ } }

@layer debug { .копче::по { содржина: attr(податоци-вредност); големина на фонтот: 12 px; боја: #fff; } }

Редоследот на слоевите е важен: база → активно → дебагирање.

база го црта контролерот. активни се справува со притиснати состојби. отстранување грешки на преклопувања.

Вака раскинувањето значи дека не се борите со чудни војни за специфичности. Секој слој има свое место и секогаш знаете што победува. Изградба на тоа Ајде прво да добиеме нешто на екранот. Не треба да изгледа добро - само треба да постои за да имаме со што да работиме.

Каскаден дебагер на играта подлога

А
Б
X

дебагерот е неактивен

Тоа се буквално само кутии. Сè уште не е возбудливо, но ни дава можности да ги зграбиме подоцна со CSS и JavaScript. Океј, јас користам каскадни слоеви овде бидејќи ги одржува работите организирани откако ќе додадете повеќе состојби. Еве еден груб премин:

/* ==================================== ПОСТАВУВАЊЕ НА КАСКАДНИ СЛОЕВИ Редот е важен: база → активно → дебагирање ==================================== */

/* Дефинирајте го редоследот на слоеви однапред */ @layer база, активна, дебагирање;

/* Слој 1: Основни стилови - стандарден изглед */ @слој база { .копче { позадина: #333; граница-радиус: 50%; ширина: 70 px; висина: 70 px; дисплеј: флекс; оправда-содржина: центар; порамни-артикли: центар; }

.пауза { ширина: 20 px; висина: 70 px; позадина: #333; дисплеј: вграден блок; } }

/* Слој 2: Активни состојби - се справува со притиснати копчиња */ @layer активен { .button.active { позадина: #0f0; /* Светло зелена кога ќе се притисне */ transform: скала (1.1); /* Малку го зголемува копчето */ }

.pause.active { позадина: #0f0; трансформација: scaleY(1.1); /* Се протега вертикално кога е притиснато */ } }

/* Слој 3: Отстранување грешки преклопувања - информации за програмер */ @layer debug { .копче::по { содржина: attr(податоци-вредност); /* Ја прикажува нумеричката вредност */ големина на фонтот: 12 px; боја: #fff; } }

Убавината на овој пристап е што секој слој има јасна цел. Основниот слој никогаш не може да го отфрли активниот, а активниот никогаш не може да го отфрли отстранувањето грешки, без оглед на специфичноста. Ова ги елиминира војните за специфичноста на CSS кои обично ги мачат алатките за дебагирање. Сега изгледа како некои кластери да седат на темна позадина. Искрено, не е премногу лошо.

Додавање на JavaScript Време на JavaScript. Ова е местото каде што контролорот всушност прави нешто. Ќе го градиме ова чекор по чекор. Чекор 1: Поставете државен менаџмент Прво, потребни ни се променливи за следење на состојбата на дебагерот: // ==================================== // МЕНАЏМЕНТ НА ДРЖАВАТА // ====================================

нека трчање = неточно; // Следи дали дебагерот е активен нека rafId; // Го складира ID-то на requestAnimationFrame за откажување

Овие променливи ја контролираат јамката за анимација која континуирано го чита влезот на тастатурата за игри. Чекор 2: Земете ги референците на DOM Следно, добиваме референци за сите HTML елементи што ќе ги ажурираме: // ==================================== // ДОМ ЕЛЕМЕНТ РЕФЕРЕНЦИ // ====================================

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, "б": btnB, "x": btnX, "p": [pause1, pause2] // копчето 'p' ги контролира двете ленти за пауза };

Ова ни овозможува да го тестираме интерфејсот со притискање на копчињата на тастатурата. Чекор 4: Креирајте ја главната јамка за ажурирање Еве каде се случува магијата. Оваа функција работи постојано и ја чита состојбата на тастатурата за игра: // ==================================== // ГЛАВНА КЕМКИ ЗА АЖУРИРАЊЕ НА ГАМЕПАДОТ // ====================================

функција updateGamepad() { // Добијте ги сите поврзани табли за игри const gamepads = navigator.getGamepads(); ако (!gamepads) се вратат;

// Користете ја првата поврзана подлога за игри const gp = подлоги за игри[0];

ако (gp) { // Состојбите на копчето за ажурирање со префрлање на класата „активна“. btnA.classList.toggle("активно", gp.копчиња[0].притиснато); btnB.classList.toggle(„активно“, gp.копчиња[1].притиснато); btnX.classList.toggle(„активно“, gp.копчиња[2].притиснато);

// Копче за пауза за ракување (индекс на копчиња 9 кај повеќето контролери) const pausePressed = gp.копчиња[9].притиснато; pause1.classList.toggle("активен", pausePressed); pause2.classList.toggle("активен", pausePressed);

// Направете листа на моментално притиснати копчиња за прикажување на статусот нека се притисне = []; gp.buttons.forEach((btn, i) => { ако (btn.притиснато)притиснато.push ("Копче" + i); });

// Ажурирајте го текстот на статусот ако се притиснат некои копчиња ако (притиснато.должина > 0) { status.textContent = "Притиснато: " + pressed.join(", "); } }

// Продолжете со циклусот ако работи дебагерот ако (работи) { rafId = requestAnimationFrame (updateGamepad); } }

Методот classList.toggle() ја додава или отстранува активната класа врз основа на тоа дали е притиснато копчето, што ги активира нашите CSS стилови на слоеви. Чекор 5: Ракувајте со настани од тастатурата Овие слушатели на настани прават резервната тастатура да функционира: // ==================================== // РАКУВАЧИ НА НАСТАНИ НА ТАСТАТА // ====================================

document.addEventListener("keydown", (д) => { 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", (д) => { 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: Додајте Контрола за почеток/стоп Конечно, ни треба начин да го вклучиме и исклучиме дебагерот: // ==================================== // ВКЛУЧЕТЕ/ИСКЛУЧИ ГО ДЕБАГЕРОТ // ====================================

document.getElementById("toggle").addEventListener("кликни", () => { трчање = !трчање; // Превртете ја состојбата на трчање

ако (работи) { status.textContent = "Дебагерот работи..."; updateGamepad(); // Започнете ја јамката за ажурирање } друго { status.textContent = "Дебагерот е неактивен"; cancelAnimationFrame(rafId); // Запрете ја јамката } });

Така да, притиснете едно копче и свети. Турнете го стапот и тој се движи. Тоа е тоа. Уште една работа: сурови вредности. Понекогаш сакате само да гледате бројки, а не светла.

Во оваа фаза, треба да видите:

Едноставен контролер на екранот, Копчиња кои реагираат додека комуницирате со нив, и Изборно читање за отстранување грешки што ги прикажува индексите на притиснато копче.

За да го направите ова помалку апстрактно, еве брзо демо на контролорот на екранот кој реагира во реално време:

Сега, со притискање на Start Recording се евидентира сè додека не притиснете Стоп за снимање. 2. Извезување податоци во CSV/JSON Откако ќе имаме дневник, ќе сакаме да го зачуваме.

Чекор 1: Направете Помошник за преземање Прво, потребна ни е помошна функција која се справува со преземање датотеки во прелистувачот: // ==================================== // ПОМОШ ЗА ПРЕЗЕМАЊЕ НА ДАТОТЕКИТЕ // ====================================

функција преземањеДатотека (име на датотека, содржина, тип = „текст/обичен“) { // Направете дупка од содржината const blob = new Blob([содржина], {тип }); 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("кликни", () => { // Проверете дали има нешто за извоз ако (!рамки.должина) { console.warn("Нема достапна снимка за извоз."); враќање; }

// Креирајте носивост со метаподатоци и рамки постојано носивост = { createAt: new Date().toISOSstring(), рамки };

// Преземете како форматиран JSON преземете датотека ( "gamepad-log.json", JSON.stringify(оптоварување, нула, 2), "апликација/json" ); });

Форматот JSON одржува сè што е структурирано и лесно може да се анализира, што го прави идеален за повторно вчитување во алатките за развој или споделување со соиграчите. Чекор 3: Ракувајте со извоз на CSV За извоз на CSV, треба да ги израмниме хиерархиските податоци во редови и колони:

//==================================== // ИЗВОЗ КАКО CSV // ====================================

document.getElementById("export-csv").addEventListener("кликни", () => { // Проверете дали има нешто за извоз ако (!рамки.должина) { console.warn("Нема достапна снимка за извоз."); враќање; }

// Направете ред за заглавие на CSV (колони за временскиот печат, сите копчиња, сите оски) const headerButtons = рамки[0].buttons.map((_, i) => btn${i}); const headerAxes = рамки[0].axes.map((_, i) => оска${i}); const header = ["t", ...headerButtons, ...headerAxes].join(",") + "\n";

// Изградете CSV редови со податоци const rows = frames.map(f => { const btnVals = f.buttons.map(b => b.value); врати [f.t, ...btnVals, ...f.axes].join(","); }).join("\n");

// Преземете како CSV downloadFile ("gamepad-log.csv", заглавие + редови, "текст/csv"); });

CSV е брилијантен за анализа на податоци бидејќи се отвора директно во Excel или Google Sheets, овозможувајќи ви да креирате графикони, да филтрирате податоци или визуелно да забележувате обрасци. Сега кога се вклучени копчињата за извоз, ќе видите две нови опции на панелот: Извези JSON и Извези CSV. JSON е убаво ако сакате да го фрлите необработениот дневник назад во вашите алатки за развој или да ја пробиете структурата. CSV, од друга страна, се отвора директно во Excel или Google Sheets за да можете да графиконите, филтрирате или споредувате влезови. Следната слика покажува како изгледа панелот со тие дополнителни контроли.

3. Систем за слика Понекогаш не ви треба целосно снимање, само брза „скриншот“ од состојбите на внесување. Тоа е местото каде што помага копчето „Преземи слика“.

И JavaScript:

// ==================================== // НАПРАВИ СЛИКА // ====================================

document.getElementById("слика").addEventListener("клик", () => { // Добијте ги сите поврзани табли за игри const влошки = navigator.getGamepads(); const activePads = [];

// Прегледајте ја и снимете ја состојбата на секоја поврзана табла за игри за (const gp на влошки) { ако (!gp) продолжи; // Прескокнете празни слотови

activePads.push({ id: gp.id, // Име/модел на контролорот временски печат: performance.now(), копчиња: gp.buttons.map(b => ({ притиснато: б.притиснато, вредност: б.вредност })), оски: [...gp.axes] }); }

// Проверете дали се пронајдени подлоги за игри ако (!activePads.length) { console.warn("Нема поврзани табли за игри за слика."); alert ("Не е откриен контролер!"); враќање; }

// Најавете се и известете го корисникот console.log ("Снимка:", activePads); alert(Снимката е направена! Снимен ${activePads.length} контролер(и).); });

Снимките ја замрзнуваат точната состојба на вашиот контролер во еден момент во времето. 4. Реприза на внесување дух Сега за забавното: повторување на влезот на духови. Ова зема дневник и го репродуцира визуелно како фантомски плеер да го користи контролерот.

JavaScript за повторување: // ==================================== // РЕПЛЕЈ НА ДУХ // ====================================

document.getElementById("повторно").addEventListener("кликни", () => { // Погрижете се да имаме снимка за репродукција ако (!рамки.должина) { alert ("Нема снимање за повторување!"); враќање; }

console.log ("Започнува репродукција на духови...");

// Следете го времето за синхронизирана репродукција нека startTime = performance.now(); нека рамкаИндекс = 0;

// Репродукција на јамка за анимација функција чекор() { const now = performance.now(); const elapsed = сега - startTime;

// Обработете ги сите рамки што требаше да се појават досега додека (frameIndex < frames.length && frames[frameIndex].t <= изминат) { const рамка = рамки[frameIndex];

// Ажурирајте го UI со состојбите на снимените копчиња btnA.classList.toggle(„активно“, рамка.копчиња[0].притиснато); btnB.classList.toggle(„активно“, рамка.копчиња[1].притиснато); btnX.classList.toggle(„активно“, рамка.копчиња[2].притиснато);

// Ажурирај го приказот на статусот нека се притисне = []; frame.buttons.forEach((btn, i) => { ако (btn.притиснато) е притиснато.притиснете ("Копче " + i); }); ако (притиснато.должина > 0) { status.textContent = "Дух: " + pressed.join(", "); }

рамкаИндекс++; }

// Продолжи јамка ако има повеќе рамки ако (frameIndex < frames.length) { requestAnimationFrame(чекор); } друго { console.log („Повторно репродукцијазаврши."); status.textContent = "Репродукцијата е завршена"; } }

// Започнете ја репродукцијата чекор(); });

За да го направам дебагирањето малку попрактично, додадов повторување на духови. Откако ќе снимите сесија, можете да притиснете на репродукција и да гледате како интерфејсот го прави тоа, речиси како фантомски плеер да работи на подлогата. За ова на панелот се појавува ново копче Replay Ghost.

Допрете Record, малку се зезајте со контролорот, застанете, па повторно репродуцирајте. UI само одекнува сè што сте направиле, како дух што ги следи вашите влезови. Зошто да се замарате со овие додатоци?

Снимањето/извозот им олеснува на тестерите да покажат што точно се случило. Снимките замрзнуваат момент во времето, супер корисни кога бркате чудни грешки. Репродукцијата на Ghost е одлична за упатства, проверки на пристапност или само споредување на контролните поставки рамо до рамо.

Во овој момент, тоа веќе не е само уредно демо, туку нешто што всушност би можеле да го ставите на работа. Случаи за употреба во реалниот свет Сега го имаме овој дебагер кој може да направи многу. Прикажува влез во живо, снима дневници, ги извезува, па дури и репродуцира работи. Но, вистинското прашање е: на кого всушност му е грижа? За кого е ова корисно? Програмери на игри Контролорите се дел од работата, но ги дебагирате? Обично болка. Замислете дека тестирате комбинација на борбени игри, како ↓ → + удар. Наместо да се молите, двапати сте го притиснале на ист начин, еднаш го снимате и повторно го пуштате. Готово. Или ги заменувате дневниците на JSON со соиграч за да проверите дали вашиот код за повеќе играчи реагира исто на нивната машина. Тоа е огромно. Практичари за пристапност Ова ми е блиску до срцето. Не сите играат со „стандарден“ контролер. Адаптивните контролери понекогаш исфрлаат чудни сигнали. Со оваа алатка, можете да видите што точно се случува. Наставници, истражувачи, кој и да е. Тие можат да земат дневници, да ги споредуваат или да ги репродуцираат влезовите рамо до рамо. Одеднаш, невидливите работи стануваат очигледни. Тестирање за обезбедување квалитет Тестерите обично пишуваат белешки како „Тука смачкав копчиња и се скрши“. Не е многу корисно. Сега? Тие можат да ги фатат точните преси, да го извезат дневникот и да го испратат. Без погодување. Едукатори Ако правите упатства или видеа на YouTube, репродукцијата на духови е златна. Можете буквално да кажете: „Еве што направив со контролорот“, додека интерфејсот покажува дека тоа се случува. Ги прави објаснувањата многу појасни. Надвор од игрите И да, ова не е само за игри. Луѓето користеле контролери за роботи, уметнички проекти и интерфејси за пристапност. Истиот проблем секој пат: што всушност гледа прелистувачот? Со ова, не треба да погодувате. Заклучок Отстранувањето грешки на влезот на контролорот отсекогаш се чувствувал како да се слепи. За разлика од DOM или CSS, нема вграден инспектор за подлоги за игри; тоа се само необработени бројки во конзолата, кои лесно се губат во бучавата. Со неколку стотици линии HTML, CSS и JavaScript, изградивме нешто различно:

Визуелен дебагер кој ги прави видливи невидливите влезови. Слоен CSS систем што го одржува корисничкиот интерфејс чист и може да се дебагира. Збир на подобрувања (снимање, извоз, снимки, повторување на духови) кои го издигнуваат од демо во алатка за развивачи.

Овој проект покажува колку далеку можете да одите со мешање на моќта на веб-платформата со малку креативност во CSS каскадните слоеви. Алатката што штотуку ја објаснив во целост е со отворен код. Можете да го клонирате репото на GitHub и да го испробате сами. Но, уште поважно, можете да го направите ваш. Додадете свои слоеви. Изградете своја сопствена логика за повторување. Интегрирајте го со прототипот на вашата игра. Или дури и да го користам на начини што не сум ги замислил. За настава, пристапност или анализа на податоци. На крајот на денот, ова не е само за дебагирање на тампони за игри. Станува збор за осветлување на скриените влезови и за давање доверба на програмерите да работат со хардвер што мрежата сè уште не го прифаќа целосно. Значи, приклучете го вашиот контролер, отворете го уредникот и почнете да експериментирате. Можеби ќе бидете изненадени од тоа што навистина можат да постигнат вашиот прелистувач и вашиот CSS.

You May Also Like

Enjoyed This Article?

Get weekly tips on growing your audience and monetizing your content — straight to your inbox.

No spam. Join 138,000+ creators. Unsubscribe anytime.

Create Your Free Bio Page

Join 138,000+ creators on Seemless.

Get Started Free