Când conectați un controler, pisați butoanele, mișcați stick-urile, trageți declanșatoarele... și, în calitate de dezvoltator, nu vedeți nimic. Browserul îl preia, sigur, dar dacă nu înregistrați numere în consolă, este invizibil. Aceasta este durerea de cap cu API-ul Gamepad. Există de ani de zile și, de fapt, este destul de puternic. Puteți citi butoane, stick-uri, declanșatoare, lucrări. Dar majoritatea oamenilor nu o ating. De ce? Pentru că nu există feedback. Niciun panou în instrumentele pentru dezvoltatori. Nu există o modalitate clară de a ști dacă controlerul chiar face ceea ce crezi. Parcă ar zbura orb. Asta m-a deranjat suficient pentru a construi un mic instrument: Gamepad Cascade Debugger. În loc să vă uitați la ieșirea consolei, obțineți o vedere interactivă în direct a controlerului. Apăsați ceva și reacționează pe ecran. Și cu Straturile în cascadă CSS, stilurile rămân organizate, deci este mai curat de depanat. În această postare, vă voi arăta de ce depanarea controlerelor este atât de dificilă, cum CSS ajută la curățarea acesteia și cum puteți construi un depanator vizual reutilizabil pentru propriile proiecte.

Chiar dacă reușiți să le înregistrați pe toate, veți ajunge rapid cu spam de consolă imposibil de citit. De exemplu: [0,0,1,0,0,0.5,0,...] [0,0,0,0,1,0,0,...] [0,0,1,0,0,0,0,...]

Poți spune ce buton a fost apăsat? Poate, dar numai după ce ți-ai încordat ochii și ai ratat câteva intrări. Deci, nu, depanarea nu vine ușor atunci când vine vorba de citirea intrărilor. Problema 3: Lipsa Structurii Chiar dacă combinați un vizualizator rapid, stilurile pot deveni rapid dezordonate. Stările implicite, active și de depanare se pot suprapune și, fără o structură clară, CSS-ul dvs. devine fragil și greu de extins. Straturile în cascadă CSS vă pot ajuta. Ei grupează stilurile în „straturi” care sunt ordonate în funcție de prioritate, așa că încetați să vă luptați cu specificul și să ghiciți „De ce nu apare stilul meu de depanare?” În schimb, mențineți preocupări separate:

Baza: aspectul inițial standard al controlerului. Activ: Evidențieri pentru butoanele apăsate și stick-urile mutate. Depanare: suprapuneri pentru dezvoltatori (de exemplu, citiri numerice, ghiduri și așa mai departe).

Dacă ar fi să definim straturi în CSS în funcție de aceasta, am avea: /* de la cea mai mică la cea mai mare prioritate */ @layer de bază, activ, depanare;

@baza stratului { /* ... */ }

@layer activ { /* ... */ }

@layer depanare { /* ... */ }

Deoarece fiecare strat se stivuiește în mod previzibil, știi întotdeauna care reguli câștigă. Această predictibilitate face depanarea nu doar mai ușoară, ci și gestionabilă. Am acoperit problema (intrare invizibilă, dezordonată) și abordarea (un depanator vizual construit cu Cascade Layers). Acum vom parcurge procesul pas cu pas pentru a construi depanatorul. Conceptul de depanare Cel mai simplu mod de a face vizibilă intrarea ascunsă este să o desenezi pe ecran. Asta face acest depanator. Butoanele, declanșatoarele și joystick-urile au toate un aspect vizual.

Apăsați A: Se aprinde un cerc. Împingeți bățul: cercul alunecă în jur. Apăsați pe trăgaci la jumătate: O bară se umple pe jumătate.

Acum nu te uiți la 0 și 1, ci privești de fapt controlerul reacționând în direct. Desigur, odată ce începeți să acumulați stări precum implicit, apăsat, informații de depanare, poate chiar un mod de înregistrare, CSS începe să devină mai mare și mai complex. Acolo sunt utile straturile în cascadă. Iată un exemplu redus: @baza stratului { .button { fundal: #222; raza-limită: 50%; latime: 40px; înălțime: 40px; } }

@layer activ { .buton.apăsat { fundal: #0f0; /* verde aprins */ } }

@layer depanare { .button::după { continut: attr(data-valoare); dimensiunea fontului: 12px; culoare: #fff; } }

Ordinea straturilor contează: bază → activ → depanare.

baza trage controlerul. active manevrează stările apăsate. aruncări de depanare pe suprapuneri.

O despărțire astfel înseamnă că nu luptați cu războaie ciudate de specificitate. Fiecare strat are locul lui și știi mereu ce câștigă. Construindu-l Mai întâi să vedem ceva pe ecran. Nu trebuie să arate bine, trebuie doar să existe, astfel încât să avem cu ce să lucrăm.

Gamepad Cascade Debugger

A
B
X

Depanator inactiv

Acestea sunt literalmente doar cutii. Nu este încă interesant, dar ne oferă mânere pe care să le folosim mai târziu cu CSS și JavaScript. Bine, folosesc straturi în cascadă aici, deoarece păstrează lucrurile organizate odată ce adăugați mai multe stări. Iată o trecere brută:

/* ==================================== Configurarea straturilor în cascadă Ordinea contează: bază → activ → depanare ==================================== */

/* Definiți ordinea straturilor în avans */ @layer de bază, activ, depanare;

/* Stratul 1: Stiluri de bază - aspect implicit */ @baza stratului { .button { fundal: #333; raza-limită: 50%; latime: 70px; inaltime: 70px; display: flex; justificare-conținut: centru; alinierea elementelor: centru; }

.pause { latime: 20px; inaltime: 70px; fundal: #333; display: inline-block; } }

/* Stratul 2: Stări active - gestionează butoanele apăsate */ @layer activ { .button.activ { fundal: #0f0; /* Verde strălucitor când este apăsat */ transforma: scară(1,1); /* Mărește ușor butonul */ }

.pause.active { fundal: #0f0; transforma: scaleY(1.1); /* Se întinde vertical când este apăsat */ } }

/* Stratul 3: Depanare suprapuneri - informații despre dezvoltator */ @layer depanare { .button::după { continut: attr(data-valoare); /* Afișează valoarea numerică */ dimensiunea fontului: 12px; culoare: #fff; } }

Frumusețea acestei abordări este că fiecare strat are un scop clar. Stratul de bază nu poate suprascrie niciodată activ, iar activ nu poate suprascrie niciodată depanarea, indiferent de specificitate. Acest lucru elimină războaiele specifice CSS care afectează de obicei instrumentele de depanare. Acum se pare că unele ciorchine stau pe un fundal întunecat. Sincer, nu prea rău.

Adăugarea JavaScript Ora JavaScript. Aici controlerul face de fapt ceva. Vom construi acest lucru pas cu pas. Pasul 1: Configurați managementul statului În primul rând, avem nevoie de variabile pentru a urmări starea depanatorului: // ==================================== // MANAGEMENTUL DE STAT // ====================================

let running = false; // Urmărește dacă depanatorul este activ let rafId; // Stochează ID-ul requestAnimationFrame pentru anulare

Aceste variabile controlează bucla de animație care citește continuu intrarea gamepadului. Pasul 2: luați referințe DOM În continuare, obținem referințe la toate elementele HTML pe care le vom actualiza: // ==================================== // REFERINȚELE ELEMENTULUI 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 status = document.getElementById("status");

Stocarea acestor referințe în avans este mai eficientă decât interogarea DOM în mod repetat. Pasul 3: Adăugați funcția de rezervă pentru tastatură Pentru testarea fără controler fizic, vom mapa tastele de la tastatură la butoane: // ==================================== // TASTATURA FALLBACK (pentru testare fără controler) // ====================================

const keyMap = { "a": btnA, „b”: btnB, "x": btnX, „p”: [pauză1, pauză2] // Tasta „p” controlează ambele bare de pauză };

Acest lucru ne permite să testăm interfața de utilizare apăsând tastele de pe o tastatură. Pasul 4: Creați bucla principală de actualizare Aici se întâmplă magia. Această funcție rulează continuu și citește starea gamepadului: // ==================================== // BUCLĂ DE ACTUALIZARE PRINCIPALĂ A PĂDULUI DE JOC // ====================================

function updateGamepad() { // Obțineți toate gamepad-urile conectate const gamepads = navigator.getGamepads(); dacă (!gamepad-uri) revine;

// Folosește primul gamepad conectat const gp = gamepad-uri[0];

dacă (gp) { // Actualizați stările butonului prin comutarea între clasa „activă”. btnA.classList.toggle("activ", gp.buttons[0].apasat); btnB.classList.toggle(„activ”, gp.buttons[1].apasat); btnX.classList.toggle(„activ”, gp.buttons[2].apasat);

// Manevrează butonul de pauză (butonul index 9 pe majoritatea controlerelor) const pausePressed = gp.buttons[9].apasat; pause1.classList.toggle("activ", pausePressed); pause2.classList.toggle("activ", pausePressed);

// Creați o listă de butoane apăsate curent pentru afișarea stării lasă apăsat = []; gp.buttons.forEach((btn, i) => { dacă (btn.apăsat)pressed.push ("Buton " + i); });

// Actualizați textul de stare dacă este apăsat vreun buton if (apăsat.lungime > 0) { status.textContent = "Apăsat: " + pressed.join(", "); } }

// Continuați bucla dacă rulează depanator dacă (alergă) { rafId = requestAnimationFrame(updateGamepad); } }

Metoda classList.toggle() adaugă sau elimină clasa activă în funcție de dacă butonul este apăsat, ceea ce declanșează stilurile noastre de strat CSS. Pasul 5: Gestionați evenimentele de la tastatură Acești ascultători de evenimente fac ca funcția de rezervă a tastaturii să funcționeze: // ==================================== // MANIPULATORI DE EVENIMENTE DE LA TASTATURA // ====================================

document.addEventListener("keydown", (e) => { dacă (keyMap[e.key]) { // Gestionează elemente simple sau multiple dacă (Array.isArray(keyMap[e.key])) { keyMap[e.key].forEach(el => el.classList.add("activ")); } altfel { keyMap[e.key].classList.add(„activ”); } status.textContent = "Tasta apăsată: " + e.key.toUpperCase(); } });

document.addEventListener("keyup", (e) => { dacă (keyMap[e.key]) { // Eliminați starea activă când tasta este eliberată dacă (Array.isArray(keyMap[e.key])) { keyMap[e.key].forEach(el => el.classList.remove("activ")); } altfel { keyMap[e.key].classList.remove(„activ”); } status.textContent = "Cheie eliberată: " + e.key.toUpperCase(); } });

Pasul 6: Adăugați controlul Start/Stop În cele din urmă, avem nevoie de o modalitate de a activa și dezactiva depanatorul: // ==================================== // ACTIVAȚI/DEZACTIVAȚI DEBUGGER // ====================================

document.getElementById(„toggle”).addEventListener(„clic”, () => { alergare = !alergare; // Întoarce starea de rulare

dacă (alergă) { status.textContent = "Depanator rulează..."; updateGamepad(); // Începe bucla de actualizare } altfel { status.textContent = "Depanator inactiv"; cancelAnimationFrame(rafId); // Opriți bucla } });

Deci da, apăsați un buton și luminează. Apăsați bastonul și se mișcă. Asta este. Încă un lucru: valorile brute. Uneori vrei doar să vezi numere, nu lumini.

În această etapă, ar trebui să vedeți:

Un controler simplu pe ecran, Butoane care reacționează în timp ce interacționați cu ei și O citire opțională de depanare care arată indicii butoanelor apăsate.

Pentru a face acest lucru mai puțin abstract, iată o demonstrație rapidă a controlerului de pe ecran care reacționează în timp real:

Acum, apăsând Start Recording, înregistrează totul până când apăsați Stop Recording. 2. Exportarea datelor în CSV/JSON Odată ce avem un jurnal, vom dori să-l salvăm.

Pasul 1: Creați Asistentul pentru descărcare În primul rând, avem nevoie de o funcție de ajutor care se ocupă de descărcarea fișierelor în browser: // ==================================== // AJUTOR DE DESCARCARE FIȘIER // ====================================

funcția downloadFile(nume fișier, conținut, tip = „text/plain”) { // Creați un blob din conținut const blob = new Blob([conținut], { tip }); const url = URL.createObjectURL(blob);

// Creați un link de descărcare temporar și faceți clic pe el const a = document.createElement("a"); a.href = url; a.download = nume de fișier; a.click();

// Curățați adresa URL a obiectului după descărcare setTimeout(() => URL.revokeObjectURL(url), 100); }

Această funcție funcționează prin crearea unui Blob (obiect binar mare) din datele dvs., generând o adresă URL temporară pentru acesta și făcând clic programatic pe un link de descărcare. Curățarea asigură că nu pierdem memorie. Pasul 2: Gestionați exportul JSON JSON este perfect pentru păstrarea structurii complete a datelor:

// ==================================== // EXPORTĂ CA JSON // ====================================

document.getElementById("export-json").addEventListener("clic", () => { // Verificați dacă există ceva de exportat dacă (!cadre.lungime) { console.warn(„Nici o înregistrare disponibilă pentru export.”); întoarcere; }

// Creați o sarcină utilă cu metadate și cadre sarcină utilă const = { creatLa: data nouă().toISOString(), rame };

// Descărcați ca JSON formatat downloadFile( „gamepad-log.json”, JSON.stringify(sarcină utilă, nul, 2), "aplicație/json" ); });

Formatul JSON menține totul structurat și ușor de analizat, făcându-l ideal pentru încărcarea înapoi în instrumentele de dezvoltare sau partajarea cu colegii de echipă. Pasul 3: Gestionați exportul CSV Pentru exporturile CSV, trebuie să aplatizam datele ierarhice în rânduri și coloane:

//==================================== // EXPORTĂ CA CSV // ====================================

document.getElementById("export-csv").addEventListener("clic", () => { // Verificați dacă există ceva de exportat dacă (!cadre.lungime) { console.warn(„Nici o înregistrare disponibilă pentru export.”); întoarcere; }

// Creați un rând de antet CSV (coloane pentru marca temporală, toate butoanele, toate axele) const headerButtons = frames[0].buttons.map((_, i) => btn${i}); const headerAxes = frames[0].axes.map((_, i) => axa${i}); const header = ["t", ...headerButtons, ...headerAxes].join(",") + "\n";

// Creați rânduri de date CSV const rows = frames.map(f => { const btnVals = f.buttons.map(b => b.valoare); return [f.t, ...btnVals, ...f.axes].join(","); }).join("\n");

// Descărcați ca CSV downloadFile("gamepad-log.csv", antet + rânduri, "text/csv"); });

CSV este genial pentru analiza datelor, deoarece se deschide direct în Excel sau Google Sheets, permițându-vă să creați diagrame, să filtrați datele sau să reperați vizual modele. Acum că butoanele de export sunt activate, veți vedea două opțiuni noi pe panou: Export JSON și Export CSV. JSON este bun dacă doriți să aruncați jurnalul brut înapoi în instrumentele dvs. de dezvoltare sau să aruncați în jurul structurii. CSV, pe de altă parte, se deschide direct în Excel sau Foi de calcul Google, astfel încât să puteți diagrama, filtra sau compara intrările. Figura următoare arată cum arată panoul cu aceste controale suplimentare.

3. Sistem de instantanee Uneori nu aveți nevoie de o înregistrare completă, doar de o „captură de ecran” rapidă a stărilor de intrare. Acolo vă ajută un buton Take Snapshot.

Și JavaScript:

// ==================================== // FACEȚI INstantaneu // ====================================

document.getElementById(„snapshot”).addEventListener(„clic”, () => { // Obțineți toate gamepad-urile conectate const pads = navigator.getGamepads(); const activePads = [];

// Buclă și captează starea fiecărui gamepad conectat pentru (const gp of pads) { dacă (!gp) continuă; // Omite sloturile goale

activePads.push({ id: gp.id, // Numele/modelul controlerului marca temporală: performance.now(), butoane: gp.buttons.map(b => ({ apăsat: b. apăsat, valoare: b.valoare })), axe: [...gp.axes] }); }

// Verificați dacă au fost găsite gamepad-uri dacă (!activePads.length) { console.warn(„Niciun gamepad conectat pentru instantaneu.”); alert("Nu a fost detectat niciun controler!"); întoarcere; }

// Înregistrează și anunță utilizatorul console.log("Snapshot:", activePads); alertă(Instantaneu făcut! S-au capturat controler(e) ${activePads.length}); });

Instantaneele îngheață starea exactă a controlerului dvs. la un moment dat. 4. Reluare intrare fantomă Acum, pentru cea distractivă: reluarea intrării fantome. Acesta ia un jurnal și îl redă vizual ca și cum un player fantomă ar fi folosit controlerul.

JavaScript pentru reluare: // ==================================== // RELUARE A FANTOMILOR // ====================================

document.getElementById(„reluare”).addEventListener(„clic”, () => { // Asigurați-vă că avem o înregistrare de reluat dacă (!cadre.lungime) { alert("Nici o înregistrare de reluat!"); întoarcere; }

console.log("Se începe redarea fantomă...");

// Timpul piesei pentru redarea sincronizată let startTime = performance.now(); lasă frameIndex = 0;

// Reluați bucla de animație function step() { const now = performance.now(); const elapsed = acum - startTime;

// Procesează toate cadrele care ar fi trebuit să apară până acum while (frameIndex < frames.length && frames[frameIndex].t <= elapsed) { const frame = frames[frameIndex];

// Actualizați interfața de utilizare cu stările butoanelor înregistrate btnA.classList.toggle("activ", frame.buttons[0].apasat); btnB.classList.toggle(„activ”, frame.buttons[1].apasat); btnX.classList.toggle("activ", frame.buttons[2].apasat);

// Actualizați afișarea stării lasă apăsat = []; frame.buttons.forEach((btn, i) => { if (btn.apasat) apasat.push("Buton " + i); }); if (apăsat.lungime > 0) { status.textContent = "Ghost: " + pressed.join(", "); }

frameIndex++; }

// Continuați bucla dacă există mai multe cadre if (frameIndex < frames.length) { cerereAnimationFrame(pas); } altfel { console.log ("Reluareterminat."); status.textContent = "Reluare finalizată"; } }

// Începe reluarea pas(); });

Pentru a face depanarea un pic mai practică, am adăugat o reluare fantomă. Odată ce ați înregistrat o sesiune, puteți apăsa reluare și puteți urmări interfața de utilizare cum o joacă, aproape ca și cum un player fantomă rulează pad-ul. Un nou buton Replay Ghost apare în panou pentru aceasta.

Apăsați Înregistrare, încurcă puțin cu controlerul, opriți, apoi reluați. Interfața de utilizare doar ecou tot ce ați făcut, ca o fantomă care vă urmărește intrările. De ce să te deranjezi cu aceste extra?

Înregistrarea/exportarea facilitează testatorilor să arate exact ce s-a întâmplat. Instantaneele îngheață un moment în timp, foarte utile atunci când urmăriți erori ciudate. Reluarea Ghost este excelentă pentru tutoriale, verificări de accesibilitate sau doar pentru a compara setările de control una lângă alta.

În acest moment, nu mai este doar o demonstrație elegantă, ci ceva pe care l-ați putea pune efectiv la lucru. Cazuri de utilizare din lumea reală Acum avem acest depanator care poate face multe. Afișează intrarea live, înregistrează jurnalele, le exportă și chiar redă lucruri. Dar adevărata întrebare este: cui îi pasă de fapt? Cui este util asta? Dezvoltatori de jocuri Controllerele fac parte din muncă, dar depanarea lor? De obicei o durere. Imaginează-ți că testezi o combo de joc de luptă, cum ar fi ↓ → + pumn. În loc să te rogi, l-ai apăsat în același mod de două ori, îl înregistrezi o dată și îl redai din nou. Gata. Sau schimbi jurnalele JSON cu un coechipier pentru a verifica dacă codul tău multiplayer reacționează la fel pe mașina lui. Asta este uriaș. Practicieni în accesibilitate Acesta este aproape de inima mea. Nu toată lumea joacă cu un controler „standard”. Controlerele adaptive aruncă uneori semnale ciudate. Cu acest instrument, puteți vedea exact ce se întâmplă. Profesori, cercetători, oricine. Ei pot lua jurnalele, le pot compara sau pot reda intrările unul lângă altul. Dintr-o dată, lucrurile invizibile devin evidente. Testare de asigurare a calității Testerii scriu de obicei note de genul „Am pisat butoanele aici și s-a rupt”. Nu foarte util. Acum? Ei pot captura presele exacte, pot exporta jurnalul și îl pot trimite. Fără ghicit. Educatorii Dacă faci tutoriale sau videoclipuri YouTube, redarea fantomă este de aur. Puteți spune literal: „Iată ce am făcut cu controlerul”, în timp ce interfața de utilizare arată că se întâmplă. Face explicațiile mult mai clare. Dincolo de jocuri Și da, nu este vorba doar despre jocuri. Oamenii au folosit controlere pentru roboți, proiecte de artă și interfețe de accesibilitate. Aceeași problemă de fiecare dată: ce vede de fapt browserul? Cu asta, nu trebuie să ghiciți. Concluzie Depanarea unei intrări a controlerului a fost întotdeauna ca un zbor orb. Spre deosebire de DOM sau CSS, nu există un inspector încorporat pentru gamepad-uri; sunt doar numere brute din consolă, ușor de pierdut în zgomot. Cu câteva sute de linii de HTML, CSS și JavaScript, am construit ceva diferit:

Un depanator vizual care face vizibile intrările invizibile. Un sistem CSS stratificat care menține interfața de utilizare curată și depanabilă. Un set de îmbunătățiri (înregistrare, export, instantanee, reluare fantomă) care îl ridică de la demo la instrument de dezvoltator.

Acest proiect arată cât de departe puteți merge combinând puterea platformei web cu puțină creativitate în straturile în cascadă CSS. Instrumentul pe care tocmai l-am explicat în întregime este open-source. Puteți clona depozitul GitHub și îl puteți încerca singur. Dar, mai important, îl poți face al tău. Adăugați propriile straturi. Construiește-ți propria logică de reluare. Integrați-l cu prototipul de joc. Sau chiar să-l folosesc în moduri pe care nu mi le-am imaginat. Pentru predare, accesibilitate sau analiza datelor. La sfârșitul zilei, nu este vorba doar de depanarea gamepad-urilor. Este vorba de a pune în lumină intrările ascunse și de a oferi dezvoltatorilor încrederea de a lucra cu hardware pe care web-ul încă nu îl îmbrățișează pe deplin. Așadar, conectați controlerul, deschideți editorul și începeți să experimentați. S-ar putea să fii surprins de ceea ce browserul și CSS-ul tău pot realiza cu adevărat.

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