Wanneer jy 'n beheerder inprop, druk jy knoppies, beweeg die stokke, trek die snellers ... en as 'n ontwikkelaar sien jy niks daarvan nie. Die blaaier tel dit op, maar tensy jy nommers in die konsole aanteken, is dit onsigbaar. Dit is die kopseer met die Gamepad API. Dit bestaan ​​al jare, en dit is eintlik redelik kragtig. Jy kan knoppies, stokke, snellers, die werke lees. Maar die meeste mense raak nie daaraan nie. Hoekom? Want daar is geen terugvoer nie. Geen paneel in ontwikkelaarnutsgoed nie. Geen duidelike manier om te weet of die beheerder selfs doen wat jy dink nie. Dit voel soos om blind te vlieg. Dit het my genoeg gepla om 'n klein hulpmiddel te bou: Gamepad Cascade Debugger. In plaas daarvan om na die konsole-uitset te staar, kry jy 'n lewendige, interaktiewe aansig van die beheerder. Druk iets en dit reageer op die skerm. En met CSS Cascade Layers bly die style georganiseer, so dit is skoner om te ontfout. In hierdie pos sal ek jou wys hoekom ontfoutingbeheerders so 'n pyn is, hoe CSS help om dit op te ruim en hoe jy 'n herbruikbare visuele ontfouter vir jou eie projekte kan bou.

Selfs as jy hulle almal kan aanteken, sal jy vinnig met onleesbare konsole-strooipos eindig. Byvoorbeeld: [0,0,1,0,0,0.5,0,...] [0,0,0,0,1,0,0,...] [0,0,1,0,0,0,0,...]

Kan jy sê watter knoppie gedruk is? Miskien, maar eers nadat jy jou oë gespanne het en 'n paar insette gemis het. So, nee, ontfouting kom nie maklik wanneer dit by leesinsette kom nie. Probleem 3: Gebrek aan struktuur Selfs as jy 'n vinnige visualiseerder saamstel, kan style vinnig deurmekaar raak. Verstek-, aktiewe- en ontfouttoestande kan oorvleuel, en sonder 'n duidelike struktuur word jou CSS bros en moeilik om uit te brei. CSS Cascade Layers kan help. Hulle groepeer style in "lae" wat volgens prioriteit georden word, sodat jy ophou om spesifisiteit te beveg en te raai, "Hoekom wys my ontfoutstyl nie?" In plaas daarvan, handhaaf jy afsonderlike bekommernisse:

Basis: Die beheerder se standaard, aanvanklike voorkoms. Aktief: Hoogtepunte vir gedrukte knoppies en verskuifde stokke. Ontfouting: Oorlegsels vir ontwikkelaars (bv. numeriese uitlesings, gidse, ensovoorts).

As ons lae in CSS hiervolgens sou definieer, sou ons: /* laagste tot hoogste prioriteit */ @laagbasis, aktief, ontfout;

@laagbasis { /* ... */ }

@laag aktief { /* ... */ }

@laag ontfout { /* ... */ }

Omdat elke laag voorspelbaar stapels, weet jy altyd watter reëls wen. Daardie voorspelbaarheid maak ontfouting nie net makliker nie, maar eintlik hanteerbaar. Ons het die probleem (onsigbare, morsige invoer) en die benadering ('n visuele ontfouter gebou met Cascade Layers) gedek. Nou sal ons deur die stap-vir-stap proses loop om die ontfouter te bou. Die Debugger-konsep Die maklikste manier om verborge invoer sigbaar te maak, is om dit net op die skerm te teken. Dit is wat hierdie ontfouter doen. Knoppies, snellers en joysticks kry almal 'n beeld.

Druk A: 'n Sirkel brand. Stoot die stok: Die sirkel gly rond. Trek 'n sneller halfpad: 'n Staaf vul halfpad.

Nou staar jy nie na 0'e en 1'e nie, maar kyk eintlik hoe die beheerder regstreeks reageer. Natuurlik, sodra jy begin opstapel op state soos verstek, gedruk, ontfout inligting, miskien selfs 'n opname-modus, begin die CSS groter en meer kompleks word. Dit is waar kaskade lae handig te pas kom. Hier is 'n gestroopte voorbeeld: @laagbasis { .button { agtergrond: #222; grens-radius: 50%; breedte: 40px; hoogte: 40px; } }

@laag aktief { .button.pressed { agtergrond: #0f0; /* heldergroen */ } }

@laag ontfout { .button::after { inhoud: attr(data-waarde); lettergrootte: 12px; kleur: #fff; } }

Die laagvolgorde maak saak: basis → aktief → ontfout.

basis teken die kontroleerder. aktiewe handvatsels gedrukte state. ontfout gooi oorleggings.

Om dit so op te breek, beteken dat jy nie vreemde spesifisiteitsoorloë veg nie. Elke laag het sy plek, en jy weet altyd wat wen. Bou dit uit Kom ons kry eers iets op die skerm. Dit hoef nie goed te lyk nie - moet net bestaan ​​sodat ons iets het om mee te werk.

Gamepad Cascade Debugger

A
B
X

Ontfouter onaktief

Dit is letterlik net bokse. Nog nie opwindend nie, maar dit gee ons handvatsels om later met CSS en JavaScript te gryp. Goed, ek gebruik kaskade lae hier, want dit hou dinge georganiseer sodra jy meer state byvoeg. Hier is 'n rowwe pas:

/* ================================== KASKADELAE OPSTEL Orde maak saak: basis → aktief → ontfout ===================================== */

/* Definieer laagvolgorde vooraf */ @laagbasis, aktief, ontfout;

/* Laag 1: Basisstyle - verstekvoorkoms */ @laagbasis { .button { agtergrond: #333; grens-radius: 50%; breedte: 70px; hoogte: 70px; vertoon: buig; regverdig-inhoud: sentreer; belyn-items: centreer; }

.pouse { breedte: 20px; hoogte: 70px; agtergrond: #333; vertoon: inlyn-blok; } }

/* Laag 2: Aktiewe toestande - hanteer gedrukte knoppies */ @laag aktief { .button.active { agtergrond: #0f0; /* Helder groen wanneer gedruk word */ transformeer: skaal(1.1); /* Vergroot die knoppie effens */ }

.pause.active { agtergrond: #0f0; transformeer: ​​skaalY(1.1); /* Strek vertikaal wanneer dit gedruk word */ } }

/* Laag 3: Ontfout oorleggings - ontwikkelaarinligting */ @laag ontfout { .button::after { inhoud: attr(data-waarde); /* Toon die numeriese waarde */ lettergrootte: 12px; kleur: #fff; } }

Die skoonheid van hierdie benadering is dat elke laag 'n duidelike doel het. Die basislaag kan nooit aktief ignoreer nie, en aktief kan nooit ontfouting ignoreer nie, ongeag spesifisiteit. Dit skakel die CSS-spesifisiteitsoorloë uit wat gewoonlik ontfoutingsnutsmiddels teister. Nou lyk dit of sommige trosse op 'n donker agtergrond sit. Eerlik, nie te sleg nie.

Voeg die JavaScript by JavaScript tyd. Dit is waar die beheerder eintlik iets doen. Ons sal dit stap vir stap bou. Stap 1: Stel staatsbestuur op Eerstens het ons veranderlikes nodig om die ontfouter se toestand op te spoor: // =================================== // STAATSBESTUUR // ===================================

laat loop = vals; // Volg of die ontfouter aktief is laat rafId; // Stoor die requestAnimationFrame ID vir kansellasie

Hierdie veranderlikes beheer die animasielus wat voortdurend gamepad-invoer lees. Stap 2: Gryp DOM-verwysings Vervolgens kry ons verwysings na al die HTML-elemente wat ons gaan opdateer: // =================================== // DOM ELEMENT VERWYSINGS // ===================================

const btnA = document.getElementById("btn-a"); const btnB = document.getElementById("btn-b"); const btnX = document.getElementById("btn-x"); const pause1 = document.getElementById("pouse1"); const pause2 = document.getElementById("pouse2"); const status = document.getElementById("status");

Om hierdie verwysings vooraf te stoor, is meer doeltreffend as om die DOM herhaaldelik navraag te doen. Stap 3: Voeg sleutelbordterugval by Vir toetsing sonder 'n fisiese beheerder, sal ons sleutelbordsleutels na knoppies karteer: // =================================== // TERUGSLEUTELBORD (vir toets sonder 'n kontroleerder) // ===================================

const keyMap = { "a": btnA, "b": btnB, "x": btnX, "p": [pouse1, pouse2] // 'p' sleutel beheer beide pouse bars };

Dit laat ons die UI toets deur sleutels op 'n sleutelbord te druk. Stap 4: Skep die hoofopdateringlus Hier is waar die magie gebeur. Hierdie funksie loop deurlopend en lees gamepad-status: // =================================== // HOOFSPELBLAD UPDATE LOOP // ===================================

funksie updateGamepad() { // Kry alle gekoppelde gamepads const gamepads = navigator.getGamepads(); as (!gamepads) terugkeer;

// Gebruik die eerste gekoppelde gamepad const gp = gamepads[0];

if (gp) { // Dateer knoppie state op deur die "aktiewe" klas te wissel btnA.classList.toggle("aktief", gp.knoppies[0].gedruk); btnB.classList.toggle("aktief", gp.knoppies[1].gedruk); btnX.classList.toggle("aktief", gp.knoppies[2].gedruk);

// Hanteer pouse-knoppie (knoppie-indeks 9 op die meeste beheerders) const pausePressed = gp.buttons[9].pressed; pause1.classList.toggle("aktief", pausePressed); pause2.classList.toggle("aktief", pausePressed);

// Bou 'n lys van tans gedrukte knoppies vir status vertoon laat druk = []; gp.buttons.forEach((btn, i) => { if (btn.pressed)pressed.push("Knoppie " + i); });

// Werk statusteks op as enige knoppies gedruk word if (gedruk.lengte > 0) { status.textContent = "Gedruk: " + pressed.join(", "); } }

// Gaan voort met die lus as ontfouter loop if (hardloop) { rafId = requestAnimationFrame(updateGamepad); } }

Die classList.toggle() metode voeg die aktiewe klas by of verwyder, gebaseer op of die knoppie gedruk word, wat ons CSS-laagstyle aktiveer. Stap 5: Hanteer sleutelbordgebeurtenisse Hierdie gebeurtenisluisteraars laat die terugval van die sleutelbord werk: // =================================== // SLEUTELBORD GELEENTHEID HANTERS // ===================================

document.addEventListener("keydown", (e) => { if (keyMap[e.key]) { // Hanteer enkele of veelvuldige elemente if (Array.isArray(keyMap[e.key])) { keyMap[e.key].forEach(el => el.classList.add("aktief")); } anders { keyMap[e.key].classList.add("aktief"); } status.textContent = "Sleutel gedruk: " + e.key.toUpperCase(); } });

document.addEventListener("keyup", (e) => { if (keyMap[e.key]) { // Verwyder aktiewe toestand wanneer sleutel vrygestel word if (Array.isArray(keyMap[e.key])) { keyMap[e.key].forEach(el => el.classList.remove("aktief")); } anders { keyMap[e.key].classList.remove("aktief"); } status.textContent = "Sleutel vrygestel: " + e.key.toUpperCase(); } });

Stap 6: Voeg Start/Stop Control by Ten slotte het ons 'n manier nodig om die ontfouter aan en af te skakel: // =================================== // SKAKEL DEBUGGER AAN/UIT // ===================================

document.getElementById("toggle").addEventListener("click", () => { hardloop = !hardloop; // Draai die lopende toestand om

if (hardloop) { status.textContent = "Ontfouter loop..."; updateGamepad(); // Begin die opdateringslus } anders { status.textContent = "Ontfouter onaktief"; cancelAnimationFrame(rafId); // Stop die lus } });

So ja, druk 'n knoppie en dit gloei. Druk die stok en dit beweeg. Dit is dit. Nog iets: rou waardes. Soms wil jy net syfers sien, nie ligte nie.

Op hierdie stadium moet jy sien:

'n Eenvoudige beheerder op die skerm, Knoppies wat reageer as jy met hulle interaksie het, en 'n Opsionele ontfoutlesing wat gedrukte knoppie-indekse wys.

Om dit minder abstrak te maak, is hier 'n vinnige demonstrasie van die kontroleerder op die skerm wat intyds reageer:

Nou, deur op Begin opname te druk, word alles gelog totdat jy Stop Recording druk. 2. Voer data uit na CSV/JSON Sodra ons 'n log het, sal ons dit wil stoor.

Stap 1: Skep die aflaaihulp Eerstens benodig ons 'n helperfunksie wat lêeraflaaie in die blaaier hanteer: // =================================== // LÊER AFLAAI HELPER // ===================================

funksie downloadFile (lêernaam, inhoud, tik = "text/plain") { // Skep 'n blob uit die inhoud const blob = nuwe Blob([inhoud], {tipe }); const url = URL.createObjectURL(blob);

// Skep 'n tydelike aflaaiskakel en klik daarop const a = document.createElement("a"); a.href = url; a.download = lêernaam; a.klik();

// Maak die voorwerp-URL skoon na aflaai setTimeout(() => URL.revokeObjectURL(url), 100); }

Hierdie funksie werk deur 'n Blob (binêre groot voorwerp) uit jou data te skep, 'n tydelike URL daarvoor te genereer en programmaties op 'n aflaaiskakel te klik. Die skoonmaak verseker dat ons nie geheue uitlek nie. Stap 2: Hanteer JSON-uitvoer JSON is perfek om die volledige datastruktuur te bewaar:

// =================================== // VOER UIT AS JSON // ===================================

document.getElementById("export-json").addEventListener("click", () => { // Kyk of daar iets is om uit te voer if (!rame.length) { console.warn("Geen opname beskikbaar om uit te voer nie."); terugkeer; }

// Skep 'n loonvrag met metadata en rame const loonvrag = { geskep By: nuwe Datum().toISOString(), rame };

// Laai af as geformateerde JSON laai lêer af( "gamepad-log.json", JSON.stringify(payload, null, 2), "toepassing/json" ); });

Die JSON-formaat hou alles gestruktureer en maklik ontleedbaar, wat dit ideaal maak om terug te laai in ontwikkelaarnutsgoed of om met spanmaats te deel. Stap 3: Hanteer CSV-uitvoer Vir CSV-uitvoere moet ons die hiërargiese data in rye en kolomme plat:

//=================================== // VOER UIT AS CSV // ===================================

document.getElementById("export-csv").addEventListener("click", () => { // Kyk of daar iets is om uit te voer if (!rame.length) { console.warn("Geen opname beskikbaar om uit te voer nie."); terugkeer; }

// Bou CSV-kopry (kolomme vir tydstempel, alle knoppies, alle asse) const headerButtons = rame[0].buttons.map((_, i) => btn${i}); const headerAxes = rame[0].axes.map((_, i) => as${i}); const header = ["t", ...headerButtons, ...headerAxes].join(",") + "\n";

// Bou CSV-datarye const rows = frames.map(f => { const btnVals = f.buttons.map(b => b.value); terugkeer [f.t, ...btnVals, ...f.axes].join(","); }).join("\n");

// Laai af as CSV downloadFile("gamepad-log.csv", kopskrif + rye, "teks/csv"); });

CSV is briljant vir data-analise omdat dit direk in Excel of Google Sheets oopmaak, sodat jy kaarte kan skep, data filter of patrone visueel kan sien. Noudat die uitvoerknoppies in is, sal jy twee nuwe opsies op die paneel sien: Voer JSON uit en Voer CSV uit. JSON is lekker as jy die rou log in jou dev-gereedskap wil teruggooi of in die struktuur wil rondkyk. CSV, aan die ander kant, maak reguit oop in Excel of Google Blaaie sodat jy insette kan karteer, filter of vergelyk. Die volgende figuur wys hoe die paneel lyk met daardie ekstra kontroles.

3. Snapshot System Soms het jy nie 'n volledige opname nodig nie, net 'n vinnige "skermkiekie" van invoertoestande. Dit is waar 'n Neem Snapshot-knoppie help.

En die JavaScript:

// =================================== // NEEM SNAPSHOT // ===================================

document.getElementById("snapshot").addEventListener("click", () => { // Kry alle gekoppelde gamepads const pads = navigator.getGamepads(); const activePads = [];

// Loop deur en vang die toestand van elke gekoppelde gamepad vas vir (konst gp van pads) { as (!gp) voortgaan; // Slaan leë gleuwe oor

activePads.push({ id: gp.id, // Beheerder naam/model tydstempel: performance.now(), knoppies: gp.buttons.map(b => ({ gedruk: b.gedruk, waarde: b.waarde })), asse: [...gp.asse] }); }

// Kyk of enige gamepads gevind is if (!activePads.length) { console.warn("Geen gamepads gekoppel vir momentopname nie."); alert ("Geen kontroleerder bespeur nie!"); terugkeer; }

// Teken aan en stel gebruiker in kennis console.log("Kiekie:", activePads); waarskuwing (Kieskie geneem! Gevang ${activePads.length} kontroleerder(s).); });

Snapshots vries die presiese toestand van jou beheerder op een oomblik in tyd. 4. Ghost Input Replay Nou vir die pret: herhaling van spookinvoer. Dit neem 'n log en speel dit visueel terug asof 'n spookspeler die beheerder gebruik.

JavaScript vir herhaling: // =================================== // SPOOKHERSPEEL // ===================================

document.getElementById("replay").addEventListener("click", () => { // Maak seker ons het 'n opname om te herspeel if (!rame.length) { alert("Geen opname om te herspeel nie!"); terugkeer; }

console.log("Begin spookherhaling...");

// Snittydsberekening vir gesinkroniseerde afspeel laat beginTyd = performance.now(); laat frameIndex = 0;

// Speel animasielus weer funksie stap() { const now = performance.now(); const elapsed = nou - beginTyd;

// Verwerk alle rame wat teen hierdie tyd moes plaasgevind het while (raamindeks < rame.length && rame[raamindeks].t <= verloop) { const raam = rame[raamindeks];

// Dateer UI op met die aangetekende knoppie state btnA.classList.toggle("aktief", frame.buttons[0].gedruk); btnB.classList.toggle("aktief", raam.knoppies[1].gedruk); btnX.classList.toggle("aktief", raam.knoppies[2].gedruk);

// Dateer statusvertoning op laat druk = []; frame.buttons.forEach((btn, i) => { if (btn.pressed) pressed.push("Button " + i); }); if (gedruk.lengte > 0) { status.textContent = "Spook: " + pressed.join(", "); }

frameIndex++; }

// Gaan voort met lus as daar meer rame is if (raamindeks < rame.lengte) { requestAnimationFrame(stap); } anders { console.log("Herspeelklaar."); status.textContent = "Herspeel voltooi"; } }

// Begin die herhaling stap(); });

Om ontfouting 'n bietjie meer praktiese te maak, het ek 'n spookherhaling bygevoeg. Sodra jy 'n sessie opgeneem het, kan jy herhaling druk en kyk hoe die UI dit uitwerk, amper soos 'n fantoomspeler wat die pad bestuur. Hiervoor verskyn 'n nuwe Replay Ghost-knoppie in die paneel.

Klik op Rekord, mors 'n bietjie met die kontroleerder, stop en speel dan weer. Die UI eggo net alles wat jy gedoen het, soos 'n spook wat jou insette volg. Hoekom die moeite doen met hierdie ekstras?

Opname/uitvoer maak dit maklik vir toetsers om presies te wys wat gebeur het. Snapshots vries 'n oomblik in tyd, baie nuttig wanneer jy vreemde goggas jaag. Spookherhaling is ideaal vir tutoriale, toeganklikheidkontroles, of om net beheeropstellings langs mekaar te vergelyk.

Op hierdie stadium is dit nie meer net 'n netjiese demo nie, maar iets wat jy eintlik aan die werk kan bring. Regte-wêreld gebruik gevalle Nou het ons hierdie ontfouter wat baie kan doen. Dit wys regstreekse insette, teken logs aan, voer dit uit en speel selfs goed weer. Maar die eintlike vraag is: wie gee nou eintlik om? Vir wie is dit nuttig? Spelontwikkelaars Beheerders is deel van die werk, maar om hulle te ontfout? Gewoonlik 'n pyn. Stel jou voor jy toets 'n vegspeletjie-kombinasie, soos ↓ → + pons. In plaas daarvan om te bid, het jy dit twee keer op dieselfde manier gedruk, jy neem dit een keer op en speel dit weer. Klaar. Of jy ruil JSON-logs met 'n spanmaat om te kyk of jou multiplayer-kode dieselfde op hul masjien reageer. Dit is groot. Toeganklikheidspraktisyns Hierdie een lê my na aan die hart. Nie almal speel met 'n "standaard" kontroleerder nie. Aanpasbare beheerders gooi soms vreemde seine uit. Met hierdie instrument kan jy presies sien wat gebeur. Onderwysers, navorsers, wie ook al. Hulle kan logs gryp, dit vergelyk of insette langs mekaar speel. Skielik word onsigbare goed duidelik. Gehalteversekeringstoetsing Toetsers skryf gewoonlik notas soos "Ek het knoppies hier gedruk en dit het gebreek." Nie baie nuttig nie. Nou? Hulle kan die presiese perse vaslê, die log uitvoer en dit afstuur. Geen raai nie. Opvoeders As jy tutoriale of YouTube-video's maak, is spookherhaling goud. U kan letterlik sê: "Hier is wat ek met die beheerder gedoen het," terwyl die UI wys dat dit gebeur. Maak verduidelikings baie duideliker. Beyond Games En ja, dit gaan nie net oor speletjies nie. Mense het beheerders vir robotte, kunsprojekte en toeganklikheidskoppelvlakke gebruik. Elke keer dieselfde probleem: wat sien die blaaier eintlik? Hiermee hoef jy nie te raai nie. Gevolgtrekking Om 'n beheerderinvoer te ontfout het nog altyd gevoel soos om blind te vlieg. Anders as die DOM of CSS, is daar geen ingeboude inspekteur vir gamepads nie; dit is net rou nommers in die konsole, maklik verlore in die geraas. Met 'n paar honderd reëls HTML, CSS en JavaScript het ons iets anders gebou:

'n Visuele ontfouter wat onsigbare insette sigbaar maak. 'n Gelaagde CSS-stelsel wat die UI skoon en ontfoutbaar hou. 'n Stel verbeterings (opname, uitvoer, foto's, spookherhaling) wat dit verhef van demonstrasie na ontwikkelaarinstrument.

Hierdie projek wys hoe ver jy kan gaan deur die webplatform se krag te meng met 'n bietjie kreatiwiteit in CSS Cascade Layers. Die instrument wat ek sopas in sy geheel verduidelik het, is oopbron. Jy kan die GitHub-repo kloon en dit self probeer. Maar nog belangriker, jy kan dit jou eie maak. Voeg jou eie lae by. Bou jou eie herhalingslogika. Integreer dit met jou speletjie-prototipe. Of selfs gebruik dit op maniere wat ek nie gedink het nie. Vir onderrig, toeganklikheid of data-analise. Aan die einde van die dag gaan dit nie net oor die ontfouting van gamepads nie. Dit gaan daaroor om 'n lig te skyn op verborge insette, en om ontwikkelaars die selfvertroue te gee om met hardeware te werk wat die web steeds nie ten volle omhels nie. Prop dus jou beheerder in, maak jou redigeerder oop en begin eksperimenteer. Jy sal dalk verbaas wees oor wat jou blaaier en jou CSS werklik kan bereik.

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