headerimage

SneManden.com

Fox, escape from Evil McGrabberson

11. September 2014

Her i starten af september fik jeg endelig færdiggjort mit seneste spil, Foxescape (fuldt title: "Fox, escape from Evil McGrabberson"). Det er alt, alt, for længe siden jeg sidst har "færdig-gjort" et af mine mange spil-projekter, så det er lækkert endelig at kunne præsentere et "færdigt" spil — forstået på den måde, at det indeholder start-skærm, instruktioner, "story", gameplay (duh) og en "you lose"/"you win" skærm.

Fra ludum dare failure til nye kompetencer

Projektets begyndelse er at finde i slutningen af August. Et af årets mange højdepunkter, Ludum Dare, blev afholdt i weekenden 22.-25. august. Jeg kom selv hjem fra min fantastiske vandretur i Sverige sent på eftermiddagen om lørdagen, 16 timer inde i konkurrencen, så det var på ingen måde givet om jeg ville deltage i konkurrencen. (Ludum Dare er et globalt Game Jam: lav et spil på 48 timer.)

Flere faktorer gjorde desværre, at jeg ikke bidrog med et spil, men jeg vendte dog denne beslutning til noget positivt. I stedet for bare at stene rundt i min sidste uge af sommerferien, besluttede jeg mig for at lære WebGL! WebGL er et JavaScript API til "rendering" af 3D og 2D grafik i en kompatibel browser uden brug af plug-ins.

Jeg har hidtil altid lavet mine spil (projekter) i ren 2D grafik, så det var utroligt spændende endelig at stifte egentligt bekendtskab med den 3. dimension! Jeg brugte primært learningwebgl.com som resource (WebGL tutorials baserede på OpenGL tutorials fra NeHE). Derudover har jeg været stærkt inspireret af Notch (Minecraft-skaberen); især hans (arkiverede) livestream fra LD28 (december 2013), hvor han ligeså lavede et spil med WebGL, skrevet ved brug af Google's Dart. Jeg har også gjort stor brug af Mozillas udvikler netværk (MDN) og deres side om WebGL.

Spillet stog sin form fra mine eksperimenter fra WebGL; da jeg først kunne rendere sprites i 3 dimensioner, rotere, skalere og positionere dem ved manipulation af matricer, kunne spillet hurtigt tage form.

Indledning

Første screenshot

Ovenfor ses det første screenshot jeg har. Her er ideen om at lave eksperimenterne til et spil lige opstået.

I første omgang koncentrerede jeg mig om at lave gameplayet; spilleren løber ned gennem den grønne bane, hvilket senere bliver til en mørk, mørk skov. At kontrollere spilleren, uendelig scrolling og andre objekter (her, drilske sten på vejen) blev implementeret i denne fase.

Objekter og kollisionsbehandling

I næste fase introducerede jeg træer; ingen skov uden træer. Simpel collision- detection var nødvendig. Det blev implementeret fra ide ved Notch; for hvert objekt i spillet, udregnes en vektor fra spiller til objektet. Hvis længden af denne vektor er mindre end spilleren's og objektets radius kombineret, er der kollision! Dernæst udregnes spillerens (fordi træet er solidt, urokkeligt) nye position: skub spilleren væk fra objektet i retning af førnævnte vektor. Simpelt, men fungerede rigtig fint.

Præcis 5 timer og 15 minutter efter ovenstående screenshot blev taget, tog jeg det andet (og eneste andet) screenshot i udviklingsfasen af spillet:

Andet screenshot

Andet screenshot: Her er implementeret træer, og pænere grafik (sprites)!

Her har jeg været igang med pennen og tegne sprites til træer, sten og spilleren, som blev en flot, rødbrun ræv (det bedste dyr i verden, selvfølgelig)!

Shaders

Efterhånden var det på tid at beskæftige sig med belysningen i spillet, hvilket involverede den mest komplicerede del af 3D-grafikken, som jeg har været introduceret til indtil videre: shaders. En shader er et lille computer program, som håndterer renderings effekter på GPU'en. Fx position, farvetone, lysstyrke og kontrast af pixels, knuder (vertices) og textures bliver manipuleret her.

Men med en del trial-and-error, fik jeg givet spillet det "feel" jeg ønskede; flere har dog ytret, at spillet er blevet noget mørk i det. Men Fox, flygter jo også igennem en mørk, uoplyst skov midt om natten! Nedenfor ses et skærmbillede af spillet, som det tager sig ud i sin endelige form.

Det færdige spil

Screenshot af det færdige spil: shaders, flere objekter, etc.

Som det fremgår, bliver mørket mere intenst jo længere væk fra skærmen objekterne er; dette programmeres fx således, at hver pixels lysstyrke (brightness) er afhængig af dennes position (fra kameraet).

Musik og lydeffekter

Jeg har i dette spil også gjort meget ud af at få ordentlig lyd indover spillet. Dette er ofte noget som jeg (og mange andre spil-udviklere) neglecierer eller udsætter som det sidste. Men der gør en kæmpe forskel for spillets udtryk og "feel". Lydeffekter har jeg selv genereret ved brug af den lækre applikation sfxr (se også bfxr, som er browser-baseret). Generering af lydeffekter viste sig at være utrolig nemt og sjovt; man trykker på nogle knapper og der kommer randomiserede lyde ud, om man kan så skrue på en masse parametre for at få præcis dén lyd man ønsker.

Baggrundsmusik er dog et andet issue. Da jeg ingen erfaring har med musik produktion af nogen art (hverken via et instrument eller via computer), måtte jeg jo finde eksisterende materiale. Heldigvis er der en gut under navnet Abstraction på soundcloud som laver fedre tracks til fx spiludvikling (og specielt Ludum Dare!). Dette har jeg virkelig draget nytte af, og al musik er dermed produceret af denne Abstraction.

Sidste finish

Efter at have tilføjet en start-menu, historie, instruktser samt "vind"/"tab" skærmene besluttede jeg at spillet var "færdigt". Jeg kunne blive ved med at touchere spillet; men da spillets formål primært har været læring af WebGL og at få noget fra hånden, føler jeg mere for at gå videre til nye udfordringer. Derfor vil jeg nok heller ikke rette nogle af de bugs der eventuelt måtte forefindes, samt optimere spillets ydeevne og resource-brug (hvilket er stærkt tiltrængt!).

Tanker og erfaringer

Jeg har lover mig selv, at jeg vil notere mig, hvilke erfaringer jeg har gjort mig i udviklingen af spillet, sådan at jeg ikke begår de samme fejl næste gang.

  • Nogle ting bør gøres rigtigt fra starten! ### Eksempel: Koden til rendering af sprites blev desværre ikke implementeret helt korrekt fra starten af; den var ikke uafhængig af den brugte textures størrelse, og tog ikke korrekt højde for højde-bredde forholdet på den sprite'en. Således har jeg altså i min Sprite "class": (se kildekoden for et fuldt overblik)
var Sprite4 = {
    ...
    renderSprite: function(size, offset, pMatrix, mvMatrix, color, mushroom) {...},
    renderSprite2: function(size, offset, pMatrix, mvMatrix, color, mushroom) {...}
    renderSprite3: function(size, offset, pMatrix, mvMatrix, color) {...}
    renderScreenSprite: function(size, offset, pMatrix, mvMatrix, color) {...}
    ...
}
  • Sørg for at cache spillets resourcer og prøv at komprimere musik-filer! Lige nu har spillet alt for lang loade-tid (og dét er ikke hjulpet af, at loadingbaren ikke giver brugeren nogen indikation af, hvor langt den er!)

  • Bare begynd at tegne! Da jeg fra starten havde rimeligt lave ambitioner med projektet, var jeg ikke så nøjeregnende og penitten med grafikken; jeg tegnede bare en rough sketch af en ræv, en gal videnskabsmand, etc. Det er langt fra at være smuk pixelgrafik(art), men jeg synes egentlig det fungerer meget godt.

    Doodling

    Her ses mine små sketches som gik forud for titel-skærmen (venstre) og "game over" skærmens grafik (højre).

    Spil spillet, for selv at sammenligne!

Konklusion

Alt i alt er jeg egentlig meget godt tilfreds med mit lille spil. Jeg ved godt, at det på ingen måde er revolutionerende; det er ikke vildt sjovt, men det har for mig været en rigtig fed proces. Jeg har lært en masse, og det er bare lækkert at være tilbage 'in the game making business'!