Paw Ormstrup Madsen. English

Projekter · Atomic Grid

Sådan byggede jeg Atomic Grid

Atomic Grid er et arenaspil til browseren, som jeg har bygget i fritiden. Her er historien om grafikken, 3D-modellerne, animationerne, serveren og klienten, og hvad du kan tage med, hvis du selv vil lave et spil til browseren.

  • 10spillere i samme arena
  • 100opdateringer i sekundet fra serveren
  • 0ting at installere
  • 32baner i 11 arenaer
  • 717automatiske tests
  • 962replikker til speakeren

Idéen: et link, og så spiller man

Inspirationen er Atomic Bomberman: man lægger bomber i et gitter, sprænger kasser væk, samler powerups op og prøver at være den sidste, der står. Det er sjovest, når mange spiller sammen. Målet var at gøre det lige så let at samle folk i dag: en lobby, en kode på fire bogstaver, og så er man i gang, på computeren eller på mobilen.

Atomic Grid er skrevet fra bunden i C# og .NET 10, og grafik, baner, lyd og musik er lavet til spillet. Det har også sin egen verden: maskinerne har overtaget, og udrangerede nedrivningsrobotter kæmper i indhegnede arenaer, mens en munter speaker kommenterer kampene uden at lægge mærke til, at verden er gået under.

Sådan voksede spillet

Jeg byggede spillet sammen med Claude Code. Jeg bestemte retningen, designet, og hvad der skulle testes, og AI'en skrev en stor del af koden. Over halvdelen af de 289 commits er skrevet sammen med Claude. Spillet voksede i den her rækkefølge:

  1. Spilmotoren, serveren og klienten.
  2. Bots, gamepads, touch på mobilen og QR-koder til at komme ind i en lobby.
  3. Spillets egen verden og pipelinen til grafikken.
  4. Robotterne som 3D-modeller.
  5. 3D-visningen i browseren.
  6. Konti og blokke som rigtige 3D-modeller.
  7. Derefter sikkerhed, drift og dokumentation.

Server og klient: serveren bestemmer

Spillet afvikles på serveren 100 gange i sekundet. Selve spillet er en ren simulering i C#, uden skærm og netværk, og tilfældighederne kommer fra en fast startværdi. Det samme udgangspunkt og de samme tastetryk giver derfor præcis det samme spil hver gang. Det er grundstenen for resten:

  • Ingen kan snyde ved at ændre i browseren, for browseren viser kun, hvad serveren siger.
  • En kamp kan afspilles igen fra en log, og en lobby kan fortsætte efter en genstart af serveren.
  • Bots kan spille hele turneringer mod hinanden uden skærm, omkring 120 kampe i sekundet. Det er sådan, de er blevet gode. I spillet fylder de tomme pladser og tager over, hvis en spiller mister forbindelsen.

Browseren og serveren taler sammen over en WebSocket. Browseren sender tastetryk, og serveren sender et lille binært øjebliksbillede tilbage hver hundrededel sekund, på 200–330 bytes. Browseren viser spillet 30 millisekunder bagud og glider mellem billederne, så bevægelserne er bløde.

Klienten er Blazor WebAssembly. Den samme C#-kode, som kører på serveren, er kompileret til WebAssembly og kører i browseren. Mens man venter i en lobby, spiller otte bots derfor en rigtig kamp direkte på siden med den samme spilmotor.

Hostet derhjemme

Spillet kører i en Docker-container på min server derhjemme. Hver ændring bygges og testes på GitHub: 717 tests skal være grønne, og WebAssembly-pakken må højst fylde 25 MB. Når en ændring er godkendt, henter serveren selv den nye version inden for et minut, og igangværende lobbyer fortsætter efter genstarten.

Hvorfor WebAssembly og 3D i browseren?

Et spil i browseren har én stor fordel: der er intet at installere. Man får et link eller scanner en QR-kode, og så er man med, også på mobilen, hvor der er et styrekryds på skærmen. Man behøver ikke engang en konto. Det gør det let at samle en hel stue eller et helt kontor til en kamp.

WebAssembly gør, at et rigtigt programmeringssprog kan køre i browseren med næsten fuld fart. For mig betød det én kodebase i C#: den samme spilmotor kører på serveren, i bot-turneringerne og i demoen i browseren.

3D i browseren kræver heller ikke store biblioteker. 3D-visningen i Atomic Grid er en egen WebGL2-renderer på omkring 1.000 linjer JavaScript, uden three.js eller Babylon.js. Den viser robotterne som billeder i 3D-rummet og blokkene som rigtige 3D-modeller, med lys til dag, skumring og nat og op til 32 lyskilder.

Der er også noget at passe på. WebAssembly-pakken skal hentes, før spillet starter, så størrelsen skal holdes nede. Og ikke alle maskiner er lige glade for 3D. Derfor er den klassiske 2D-visning standard, og 3D er et valg i indstillingerne.

  • En powerup vist to gange i tre lag: det færdige billede, et sort-hvidt lyskort og et farvet normalkort
    Hver figur har tre lag: det færdige billede, et lyskort med det, der selv lyser, og et normalkort, så lyset falder rigtigt på hver pixel.Render
  • En arena i 3D om natten, hvor eksplosioner lyser kasserne og gulvet op i rødt og hvidt
    Om natten er eksplosionerne lyskilder.I spillet
  • En grøn arena i 3D med kasser og søjler som 3D-modeller og robotter i hjørnerne
    En arena med blokkene som rigtige 3D-modeller.I spillet

Grafikken: AI, hvor den er stærk, og kode, hvor det skal passe

Reglen blev: 3D til alt, der bevæger sig, AI-billeder til alt, der står stille, og kode til alt, der skal passe præcist i gitteret.

  • AI-billeder: titelbilledet, skærmene, konceptbillederne og arenagulvene er lavet med billedmodellen FLUX.2 i ComfyUI, og det allerførste konceptbillede af robotten med FLUX.1. Gulvene er malet oven på et gitter fra kode, og fugerne lægges bagefter oven på igen, så de passer med felterne.
  • Kode: flammer, HUD, særlige felter og skygger tegnes af små C#-programmer, så kanter og gitter passer på pixlen.
  • Lokalt: alt kører på mit eget grafikkort, en AMD Radeon RX 7900 XTX med 24 GB. Det koster kun strøm, så man kan lave hundrede forsøg uden at tænke på prisen.
  • En lille, kraftig robot med rund hjelm og et blåt, lysende visir på mørk baggrund
    Det første konceptbillede af robotten, lavet med FLUX.1.AI
  • En rund metalbombe med nitter, lysende vinduer og en gnistrende lunte
    Konceptbilledet af bomben, lavet med FLUX.2.AI
  • Et lyst arenagulv i fliser med brandmærker og glødende røde pletter
    Gulvet i arenaen the Kiln, malet af AI oven på et gitter fra kode.AI

3D-modellerne: fra ét billede til en hel robot

AI-billeder har et problem i spil. Laver man hvert billede i en gåcyklus for sig, bliver de aldrig helt ens: detaljer, lys og form flytter sig lidt fra billede til billede, og når de afspilles 15 gange i sekundet, flimrer det. En 3D-model ser derimod ens ud i hvert billede, og en hel gåcyklus i fire retninger renderes på sekunder. Derfor går robotten gennem en pipeline:

  1. Ét godt konceptbillede af robotten, lavet med AI.
  2. TRELLIS.2 laver billedet om til en 3D-model med teksturer.
  3. Blender, styret af Python-scripts uden skærm, skærer bundpladen af modellen, giver den et skelet og animerer den.
  4. Animationerne renderes som spriteark i fire retninger og ti farver, som spillet viser.

Blokkene i arenaerne kommer fra samme slags pipeline. I 3D-visningen er de rigtige modeller i 7 af de 11 arenaer, på omkring 8.000 trekanter hver.

  • Den samme robot som 3D-model, vist fra otte vinkler på en sort plade
    Robotten som 3D-model fra TRELLIS.2, set fra otte vinkler.Render
  • Otte robotter i hver sin farve: hvid, grå, orange, blå, grøn, gul, turkis og lilla
    Robotterne i deres farver. Farven er et materiale i Blender, så en ny farve tager sekunder.Render

Det, der bevæger sig

Gangen er matematik. Lår, knæ, fødder, arme, ryg og hoved følger hver sin sinuskurve over et skridt på 15 billeder. Knæet bøjer midt i svinget og ikke, mens foden står på jorden, for så ligner det moonwalk. Hovedet læner lidt modsat kroppen, så det holder sig i vater.

Handlinger som at sparke, slå og samle noget op er nøglestillinger, som Blender glider imellem. De første forsøg var anatomisk fornuftige og kunne slet ikke ses i lille størrelse. I et spil skal silhuetten ændre sig tydeligt, så alt er overdrevet.

Robotten kan dø på to måder: den falder fra hinanden i en fysiksimulering, eller den smelter. Bomben har seks animationer, og de 14 powerups drejer rundt over 24 billeder hver. Klik på et billede for at se animationen.

Lyd og speaker

Musikken og lydeffekterne er ikke lydfiler. De er skrevet som noder i kode og spilles af en synthesizer i browseren, mens man spiller. 33 loops til arenaerne og 4 melodier til skærmene i otte genrer fylder nogle få titals kilobyte i stedet for 55 MB lydfiler.

Speakeren har 962 replikker, også på andre sprog end engelsk. Stemmen er lavet med tekst-til-tale ud fra en skrevet beskrivelse af stemmen, og der indgår ingen optagelse af en rigtig person.

Vil du selv lave et spil til browseren?

Det her ville jeg gøre igen:

  1. Lad serveren bestemme. En deterministisk simulering på serveren giver dig fair spil, replays og test næsten gratis.
  2. Byg bots tidligt. De fylder tomme pladser, tager over, når nogen mister forbindelsen, og er de bedste testere, du kan få.
  3. Brug AI til enkeltbilleder. Til alt, der skal se ens ud fra billede til billede, er 3D eller kode bedre.
  4. Start i 2D. Gør 3D til et valg, så spillet virker på alle maskiner fra første dag.
  5. Hold øje med størrelsen. Sæt en grænse for, hvor meget browseren skal hente, og lad bygget fejle, hvis grænsen bliver overskredet.
  6. Gør det let at komme ind. Et link, en kode på fire bogstaver eller en QR-kode, og ingen konto, før man selv vil have en.
  7. Automatisér udrulningen. Når en ændring er testet, skal den være live, uden at du skal gøre noget.

Prøv det selv

Atomic Grid er gratis og kører i browseren. Lav en lobby, del koden, og fyld de tomme pladser med bots.

Vil du have hjælp til et spil, en webapp eller AI i jeres arbejdsgange, så skriv gerne.