Kad priključite kontroler, gnječite gumbe, pomičete palice, povlačite okidače... a kao programer ne vidite ništa od toga. Preglednik to hvata, naravno, ali osim ako ne bilježite brojeve u konzoli, nevidljiv je. To je glavobolja s Gamepad API-jem. Postoji godinama i zapravo je prilično moćan. Možete čitati gumbe, palice, okidače, radove. Ali većina ljudi to ne dira. Zašto? Jer nema povratne informacije. Nema ploče u alatima za razvojne programere. Nema jasnog načina da znate radi li kontrolor uopće ono što mislite. Osjećaj je kao da letiš naslijepo. To me dovoljno zasmetalo da napravim mali alat: Gamepad Cascade Debugger. Umjesto buljenja u izlaz konzole, dobit ćete interaktivni prikaz kontrolera uživo. Pritisnite nešto i ono će reagirati na ekranu. A s CSS Cascade Layers, stilovi ostaju organizirani, tako da je čišće otklanjati pogreške. U ovom postu ću vam pokazati zašto je otklanjanje pogrešaka u kontrolerima takva muka, kako CSS pomaže u čišćenju i kako možete izgraditi višekratni vizualni ispravljač pogrešaka za svoje vlastite projekte.

Čak i ako ih možete sve zabilježiti, brzo ćete završiti s nečitljivom neželjenom poštom na konzoli. Na primjer: [0,0,1,0,0,0.5,0,...] [0,0,0,0,1,0,0,...] [0,0,1,0,0,0,0,...]

Možete li reći koji je gumb pritisnut? Možda, ali samo nakon naprezanja očiju i propuštanja nekoliko unosa. Dakle, ne, otklanjanje pogrešaka nije lako kada je riječ o čitanju ulaza. Problem 3: Nedostatak strukture Čak i ako sastavite brzi vizualizator, stilovi mogu brzo postati neuredni. Zadana, aktivna i debug stanja mogu se preklapati, a bez jasne strukture vaš CSS postaje krt i teško ga je proširiti. CSS kaskadni slojevi mogu pomoći. Oni grupiraju stilove u "slojeve" koji su poredani po prioritetu, tako da se prestajete boriti protiv specifičnosti i nagađati: "Zašto se moj stil otklanjanja pogrešaka ne prikazuje?" Umjesto toga, imate odvojene brige:

Baza: Standard kontrolera, početni izgled. Aktivno: Oznake za pritisnute gumbe i pomaknute palice. Debug: Prekrivanja za programere (npr. numerička očitavanja, vodiči i tako dalje).

Ako bismo definirali slojeve u CSS-u prema ovome, imali bismo: /* od najnižeg do najvišeg prioriteta */ @baza sloja, aktivan, ispravljanje pogrešaka;

@baza sloja { /* ... */ }

@sloj aktivan { /* ... */ }

@layer debug { /* ... */ }

Budući da se svaki sloj slaže predvidljivo, uvijek znate koja pravila pobjeđuju. Ta predvidljivost čini uklanjanje pogrešaka ne samo lakšim, već i upravljivim. Pokrili smo problem (nevidljiv, neuredan unos) i pristup (vizualni program za ispravljanje pogrešaka izgrađen s kaskadnim slojevima). Sada ćemo proći kroz postupak korak po korak za izradu programa za uklanjanje pogrešaka. Koncept programa za ispravljanje pogrešaka Najlakši način da skriveni unos učinite vidljivim je da ga samo nacrtate na ekranu. To je ono što ovaj program za uklanjanje pogrešaka radi. Gumbi, okidači i joystickovi dobivaju vizualni izgled.

Pritisnite A: svijetli krug. Gurkanje palice: krug klizi okolo. Povucite okidač do pola: poluga se napuni do pola.

Sada ne buljite u 0s i 1s, već zapravo gledate kako kontroler reagira uživo. Naravno, kada počnete gomilati stanja kao što su zadano, pritisnuto, informacije o otklanjanju pogrešaka, možda čak i način snimanja, CSS postaje sve veći i složeniji. Tu kaskadni slojevi dobro dolaze. Evo skraćenog primjera: @baza sloja { .gumb { pozadina: #222; rubni radijus: 50%; širina: 40px; visina: 40px; } }

@sloj aktivan { .button.pressed { pozadina: #0f0; /* svijetlo zelena */ } }

@layer debug { .button::after { sadržaj: attr(podatak-vrijednost); veličina fonta: 12px; boja: #fff; } }

Redoslijed slojeva je bitan: baza → aktivno → debug.

baza crta kontroler. aktivno obrađuje pritisnuta stanja. debug baca na slojeve.

Razbijanje na ovaj način znači da ne vodite čudne ratove specifičnosti. Svaki sloj ima svoje mjesto i uvijek znate što pobjeđuje. Izgradnja Hajdemo prvo prikazati nešto na ekranu. Ne mora izgledati dobro - samo treba postojati kako bismo imali s čime raditi.

Gamepad Cascade Debugger

A
B
X

Program za ispravljanje pogrešaka nije aktivan

To su doslovno samo kutije. Još nije uzbudljivo, ali nam daje ručke za kasnije s CSS-om i JavaScriptom. U redu, ovdje koristim kaskadne slojeve jer održavaju stvari organiziranima nakon što dodate više stanja. Evo grubog prolaza:

/* ==================================== POSTAVLJANJE KASKADNIH SLOJEVA Redoslijed je bitan: baza → aktivno → debug ===================================== */

/* Definirajte redoslijed slojeva unaprijed */ @baza sloja, aktivan, ispravljanje pogrešaka;

/* Sloj 1: Osnovni stilovi - zadani izgled */ @baza sloja { .gumb { pozadina: #333; rubni radijus: 50%; širina: 70px; visina: 70px; zaslon: savitljiv; justify-content: centar; align-items: center; }

.pauza { širina: 20px; visina: 70px; pozadina: #333; prikaz: inline-block; } }

/* Sloj 2: Aktivna stanja - rukuje pritisnutim gumbima */ @sloj aktivan { .button.active { pozadina: #0f0; /* Svijetlo zelena kada se pritisne */ transformacija: mjerilo (1.1); /* Lagano povećava gumb */ }

.pause.active { pozadina: #0f0; transformacija: scaleY(1.1); /* Rasteže se okomito kada se pritisne */ } }

/* Sloj 3: Prekrivanja za otklanjanje pogrešaka - informacije o razvojnom programeru */ @layer debug { .button::after { sadržaj: attr(podatak-vrijednost); /* Prikazuje numeričku vrijednost */ veličina fonta: 12px; boja: #fff; } }

Ljepota ovog pristupa je u tome što svaki sloj ima jasnu svrhu. Osnovni sloj nikada ne može nadjačati aktivni, a aktivni nikada ne može nadjačati debug, bez obzira na specifičnost. Ovo eliminira ratove specifičnosti CSS-a koji obično muče alate za uklanjanje pogrešaka. Sada izgleda kao da neki klasteri sjede na tamnoj pozadini. Iskreno, nije loše.

Dodavanje JavaScripta JavaScript vrijeme. Ovdje kontroler zapravo nešto radi. Izgradit ćemo ovo korak po korak. Korak 1: Postavite upravljanje stanjem Prvo, trebamo varijable za praćenje stanja programa za ispravljanje pogrešaka: // ==================================== // UPRAVLJANJE DRŽAVOM // ====================================

let run = lažno; // Prati je li debugger aktivan neka rafId; // Pohranjuje ID requestAnimationFrame za otkazivanje

Ove varijable kontroliraju petlju animacije koja neprekidno čita unos gamepada. Korak 2: Dohvatite DOM reference Zatim dobivamo reference na sve HTML elemente koje ćemo ažurirati: // ==================================== // REFERENCE DOM ELEMENTA // ====================================

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("status");

Pohranjivanje ovih referenci unaprijed je učinkovitije od ponovnog postavljanja upita DOM-u. Korak 3: Dodajte rezervnu tipkovnicu Za testiranje bez fizičkog kontrolera, preslikat ćemo tipke tipkovnice na gumbe: // ==================================== // REZERVNA TIPKOVNICA (za testiranje bez kontrolera) // ====================================

const keyMap = { "a": btnA, "b": btnB, "x": btnX, "p": [pauza1, pauza2] // tipka 'p' kontrolira obje trake pauze };

To nam omogućuje testiranje korisničkog sučelja pritiskom tipki na tipkovnici. Korak 4: Stvorite glavnu petlju ažuriranja Ovdje se događa magija. Ova funkcija radi kontinuirano i čita stanje gamepada: // ==================================== // GLAVNA PETLJA AŽURIRANJA GAMEPAD-a // ====================================

funkcija updateGamepad() { // Nabavite sve povezane gamepadove const gamepads = navigator.getGamepads(); if (!gamepads) return;

// Koristite prvi spojeni gamepad const gp = gamepads[0];

if (gp) { // Ažuriraj stanja gumba prebacivanjem "aktivne" klase btnA.classList.toggle("aktivan", gp.buttons[0].pritisnut); btnB.classList.toggle("aktivan", gp.buttons[1].pritisnut); btnX.classList.toggle("aktivan", gp.buttons[2].pritisnut);

// Rukovanje gumbom pauze (gumb indeks 9 na većini kontrolera) const pausePressed = gp.buttons[9].pressed; pause1.classList.toggle("aktivan", pausePressed); pause2.classList.toggle("aktivan", pausePressed);

// Izradite popis trenutno pritisnutih tipki za prikaz statusa pusti pritisnut = []; gp.buttons.forEach((btn, i) => { ako (btn.pritisnuto)pritisnuto.push("Gumb " + i); });

// Ažuriraj tekst statusa ako se pritisne bilo koji gumb if (pressed.length > 0) { status.textContent = "Pressed: " + pressed.join(", "); } }

// Nastavak petlje ako radi program za ispravljanje pogrešaka if (trči) { rafId = requestAnimationFrame(updateGamepad); } }

Metoda classList.toggle() dodaje ili uklanja aktivnu klasu ovisno o tome je li gumb pritisnut, što pokreće naše stilove CSS sloja. Korak 5: Rukovanje događajima na tipkovnici Ovi slušatelji događaja omogućuju rad zamjenske tipkovnice: // ==================================== // OBRAĐIVAČI DOGAĐAJA NA TIPKOVNICI // ====================================

document.addEventListener("keydown", (e) => { if (keyMap[e.key]) { // Rukovanje jednim ili više elemenata if (Array.isArray(keyMap[e.key])) { keyMap[e.key].forEach(el => el.classList.add("active")); } inače { keyMap[e.key].classList.add("aktivno"); } status.textContent = "Tipka pritisnuta: " + e.key.toUpperCase(); } });

document.addEventListener("keyup", (e) => { if (keyMap[e.key]) { // Ukloni aktivno stanje kada se tipka otpusti if (Array.isArray(keyMap[e.key])) { keyMap[e.key].forEach(el => el.classList.remove("active")); } inače { keyMap[e.key].classList.remove("aktivno"); } status.textContent = "Ključ pušten: " + e.key.toUpperCase(); } });

Korak 6: Dodajte kontrolu Start/Stop Konačno, trebamo način da uključimo i isključimo program za ispravljanje pogrešaka: // ==================================== // UKLJUČIVANJE/ISKLJUČIVANJE PROGRAMA ZA ISKLJUČIVANJE POGREŠAKA // ====================================

document.getElementById("toggle").addEventListener("click", () => { trčanje = !trčanje; // Preokreni stanje rada

if (trči) { status.textContent = "Debugger radi..."; ažurirajGamepad(); // Pokretanje petlje ažuriranja } inače { status.textContent = "Debugger neaktivan"; cancelAnimationFrame(rafId); // Zaustavi petlju } });

Pa da, pritisnite tipku i svijetli. Gurni palicu i ona se pomiče. to je to Još jedna stvar: sirove vrijednosti. Ponekad želite samo vidjeti brojeve, a ne svjetla.

U ovoj fazi trebali biste vidjeti:

Jednostavan upravljač na zaslonu, Gumbi koji reagiraju na vašu interakciju s njima i Dodatno očitavanje otklanjanja pogrešaka koje prikazuje indekse pritisnutih tipki.

Da ovo bude manje apstraktno, evo kratke demonstracije kontrolera na zaslonu koji reagira u stvarnom vremenu:

Sada pritiskom na Pokreni snimanje bilježi se sve dok ne pritisnete Zaustavi snimanje. 2. Izvoz podataka u CSV/JSON Kada budemo imali dnevnik, htjet ćemo ga spremiti.

Korak 1: Izradite Pomoćnik za preuzimanje Prvo, trebamo pomoćnu funkciju koja upravlja preuzimanjem datoteka u pregledniku: // ==================================== // POMOĆ ZA PREUZIMANJE DATOTEKA // ====================================

funkcija downloadFile(filename, content, type = "text/plain") { // Stvorite blob iz sadržaja const blob = new Blob([content], { type }); const url = URL.createObjectURL(blob);

// Stvorite privremenu vezu za preuzimanje i kliknite je const a = document.createElement("a"); a.href = url; a.download = naziv datoteke; a.klik();

// Očistite URL objekta nakon preuzimanja setTimeout(() => URL.revokeObjectURL(url), 100); }

Ova funkcija radi stvaranjem Bloba (binarni veliki objekt) iz vaših podataka, generiranjem privremenog URL-a za njega i programskim klikom na vezu za preuzimanje. Čišćenje osigurava da nećemo izgubiti memoriju. 2. korak: rukovanje JSON izvozom JSON je savršen za očuvanje kompletne strukture podataka:

// ==================================== // IZVOZ KAO JSON // ====================================

document.getElementById("export-json").addEventListener("klik", () => { // Provjerite ima li nešto za izvoz if (!frames.length) { console.warn("Nema dostupnih snimaka za izvoz."); povratak; }

// Stvorite korisni teret s metapodacima i okvirima const nosivost = { createdAt: novi datum().toISOString(), okviri };

// Preuzmi kao formatirani JSON preuzimanje datoteke( "gamepad-log.json", JSON.stringify(payload, null, 2), "aplikacija/json" ); });

Format JSON održava sve strukturirano i lako raščlanjivo, što ga čini idealnim za ponovno učitavanje u razvojne alate ili dijeljenje sa suigračima. 3. korak: rukovanje CSV izvozom Za izvoz CSV-a moramo izravnati hijerarhijske podatke u retke i stupce:

//==================================== // IZVOZ KAO CSV // ====================================

document.getElementById("export-csv").addEventListener("click", () => { // Provjerite ima li nešto za izvoz if (!frames.length) { console.warn("Nema dostupnih snimaka za izvoz."); povratak; }

// Izgradite redak zaglavlja CSV-a (stupci za vremensku oznaku, sve gumbe, sve osi) 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";

// Izradi CSV podatkovne retke const rows = frames.map(f => { const btnVals = f.buttons.map(b => b.value); return [f.t, ...btnVals, ...f.axes].join(","); }).pridruži("\n");

// Preuzmi kao CSV downloadFile("gamepad-log.csv", zaglavlje + redovi, "tekst/csv"); });

CSV je sjajan za analizu podataka jer se otvara izravno u Excelu ili Google tablicama, omogućujući vam izradu grafikona, filtriranje podataka ili vizualno uočavanje uzoraka. Sada kada su gumbi za izvoz uključeni, vidjet ćete dvije nove opcije na ploči: Izvezi JSON i Izvezi CSV. JSON je zgodan ako želite baciti neobrađeni dnevnik natrag u svoje razvojne alate ili čeprkati po strukturi. CSV se, s druge strane, otvara izravno u Excel ili Google tablice tako da možete ucrtati, filtrirati ili usporediti unose. Sljedeća slika prikazuje kako ploča izgleda s tim dodatnim kontrolama.

3. Snapshot sustav Ponekad vam nije potrebna potpuna snimka, samo brzi "screenshot" stanja unosa. Tu pomaže gumb Napravi snimku.

I JavaScript:

// ==================================== // SNIMAJ // ====================================

document.getElementById("snimak").addEventListener("klik", () => { // Nabavite sve povezane gamepadove const pads = navigator.getGamepads(); const activePads = [];

// Prođite i uhvatite stanje svakog povezanog gamepada za (const gp jastučića) { if (!gp) nastaviti; // Preskoči prazne utore

activePads.push({ id: gp.id, // Naziv/model kontrolera vremenska oznaka: performance.now(), gumbi: gp.buttons.map(b => ({ pritisnut: b. pritisnut, vrijednost: b.vrijednost })), osi: [...gp.osovine] }); }

// Provjerite jesu li pronađeni gamepadovi if (!activePads.length) { console.warn("Gamepadovi nisu povezani za snimku."); upozorenje("Kontroler nije otkriven!"); povratak; }

// Prijavite i obavijestite korisnika console.log("Snimak:", activePads); upozorenje(Snimak napravljen! Snimljeni ${activePads.length} kontroler(i).); });

Snimke zamrzavaju točno stanje vašeg kontrolera u jednom trenutku. 4. Ghost Input Replay A sada ono zabavno: ponavljanje unosa duhova. Ovo uzima zapisnik i reproducira ga vizualno kao da fantomski igrač koristi kontroler.

JavaScript za ponavljanje: // ==================================== // REPLIKACIJA DUHOVA // ====================================

document.getElementById("replay").addEventListener("click", () => { // Osigurajte da imamo snimku za reprodukciju if (!frames.length) { upozorenje("Nema snimanja za reprizu!"); povratak; }

console.log("Pokretanje reprodukcije duhova...");

// Pratite vrijeme za sinkroniziranu reprodukciju let startTime = performance.now(); neka frameIndex = 0;

// Ponovno reproduciraj petlju animacije funkcija step() { const sada = performance.now(); const proteklo = sada - vrijeme početka;

// Obrada svih okvira koji su se do sada trebali pojaviti dok (frameIndex < frames.length && frames[frameIndex].t <= proteklo) { const okvir = okviri[frameIndex];

// Ažuriranje korisničkog sučelja sa snimljenim stanjima gumba btnA.classList.toggle("aktivan", frame.buttons[0].pritisnut); btnB.classList.toggle("aktivan", frame.buttons[1].pritisnut); btnX.classList.toggle("aktivan", frame.buttons[2].pritisnut);

// Ažuriranje prikaza statusa pusti pritisnut = []; frame.buttons.forEach((btn, i) => { if (btn.pressed) pressed.push("Button " + i); }); if (pressed.length > 0) { status.textContent = "Duh: " + pressed.join(", "); }

frameIndex++; }

// Nastavi petlju ako ima više okvira if (frameIndex < frames.length) { requestAnimationFrame(korak); } inače { console.log("Replaygotovo."); status.textContent = "Ponovna reprodukcija dovršena"; } }

// Pokreni reprizu korak(); });

Da bi otklanjanje pogrešaka bilo malo praktičnije, dodao sam ponovnu reprodukciju duhova. Nakon što snimite sesiju, možete pritisnuti Replay i gledati kako korisničko sučelje glumi, gotovo kao da fantomski igrač trči po podlozi. Za to se na ploči pojavljuje novi gumb Replay Ghost.

Pritisnite Snimanje, malo se petljajte s kontrolerom, zaustavite, a zatim ponovno reproducirajte. UI samo ponavlja sve što ste učinili, poput duha koji prati vaše unose. Zašto se gnjaviti s ovim dodacima?

Snimanje/izvoz olakšava testerima da pokažu što se točno dogodilo. Snimke se zamrznu na trenutak u vremenu, super korisne kada lovite čudne greške. Ghost replay je izvrstan za poduke, provjere pristupačnosti ili samo usporedbu postavki kontrole.

U ovom trenutku to više nije samo uredan demo, već nešto što biste zapravo mogli upotrijebiti. Slučajevi korištenja iz stvarnog svijeta Sada imamo ovaj program za otklanjanje pogrešaka koji može učiniti puno. Prikazuje unos uživo, snima zapise, izvozi ih, pa čak i reproducira stvari. Ali pravo pitanje je: koga je zapravo briga? Kome je ovo korisno? Programeri igara Kontroleri su dio posla, ali njihovo otklanjanje pogrešaka? Obično bol. Zamislite da testirate kombinaciju borbene igre, poput ↓ → + udarac. Umjesto molitve, dvaput ste ga pritisnuli na isti način, jednom snimite i ponovno pustite. Gotovo. Ili zamijenite JSON zapise sa suigračem da provjerite reagira li vaš kod za više igrača isto na njihovom računalu. To je ogromno. Praktičari pristupačnosti Ova mi je prirasla srcu. Ne igraju svi sa "standardnim" kontrolerom. Adaptivni kontroleri ponekad ispuštaju čudne signale. Pomoću ovog alata možete točno vidjeti što se događa. Učitelji, istraživači, tko god. Oni mogu uhvatiti zapise, usporediti ih ili reproducirati unose jedan pored drugog. Odjednom, nevidljive stvari postaju očite. Ispitivanje osiguranja kvalitete Testeri obično pišu bilješke poput "Ovdje sam zgnječio gumbe i pokvario se." Nije od velike pomoći. Sada? Oni mogu snimiti točnu prešu, izvesti zapisnik i poslati ga. Bez nagađanja. Odgojitelji Ako izrađujete vodiče ili videozapise na YouTubeu, ponavljanje duhova je zlato. Doslovno možete reći: "Evo što sam učinio s kontrolerom", dok korisničko sučelje pokazuje da se to događa. Čini objašnjenja mnogo jasnijima. Izvan igara I da, ne radi se samo o igrama. Ljudi su koristili kontrolere za robote, umjetničke projekte i sučelja pristupačnosti. Svaki put isti problem: što preglednik zapravo vidi? S ovim ne morate pogađati. Zaključak Otklanjanje pogrešaka na ulazu kontrolera uvijek se činilo kao letenje naslijepo. Za razliku od DOM-a ili CSS-a, ne postoji ugrađeni inspektor za gamepadove; to su samo sirovi brojevi u konzoli, koji se lako izgube u buci. S nekoliko stotina redaka HTML-a, CSS-a i JavaScripta napravili smo nešto drugačije:

Vizualni program za ispravljanje pogrešaka koji nevidljive unose čini vidljivima. Slojeviti CSS sustav koji održava korisničko sučelje čistim i s mogućnošću otklanjanja pogrešaka. Skup poboljšanja (snimanje, eksportiranje, snimke, reproduciranje duhova) koja ga podižu iz demo alata u alat za razvojne programere.

Ovaj projekt pokazuje koliko daleko možete ići miješanjem snage web platforme s malo kreativnosti u CSS kaskadnim slojevima. Alat koji sam upravo objasnio u cijelosti je otvorenog koda. Možete klonirati GitHub repo i isprobati ga sami. Ali što je još važnije, možete ga napraviti vlastitim. Dodajte vlastite slojeve. Izgradite vlastitu logiku ponavljanja. Integrirajte ga sa svojim prototipom igre. Ili ga čak koristiti na načine koje nisam zamislio. Za podučavanje, pristupačnost ili analizu podataka. Na kraju dana, ne radi se samo o otklanjanju pogrešaka na gamepadima. Riječ je o bacanju svjetla na skrivene unose i davanju povjerenja programerima za rad s hardverom koji web još uvijek ne prihvaća u potpunosti. Dakle, uključite svoj kontroler, otvorite uređivač i počnite eksperimentirati. Možda ćete se iznenaditi što vaš preglednik i vaš CSS uistinu mogu postići.

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