Johdatus noise-funktioihin (ja satunnaiseen maastogeneraatioon)

By Arttu Mäkinen

 

Sisällysluettelo

Johdatus noise-funktioihin (ja satunnaiseen maastogeneraatioon)Sisällysluetteloigno.cc/loops1D-noisePohjaAloitetaan pisteelläTerrariaSaari-maastogeneraatioYmpyräfraktaalit

 

igno.cc/loops

1614514870377

Jos aikaisempaa kokemusta for-silmukoista ei ole, kannattaa käydä tekemässä igno.cc/loops haaste jossa käydään läpi 2D-taulukon modifioimista for-silmukoilla

1D-noise

Image result for hill climb racing 1

Koodataan Hill Climb Racing -tyylinen maastogeneraatiosysteemi editor.p5js.org ympäristössä

Jos arvostaa silmiään kannattaa vaihtaa Dark Theme:en painamalla oikeassa yläkulmassa olevaa asetus/hammasratasikonia ja valitsemalla Dark tai High Contrast. Ympäristöön kirjautuminen kannattaa ettei menetä koodejaan selaimen kaatuessa etc

Pohja

Uuden projektin kuuluisi näyttää tältä:

setup() funktion aaltosulkujen sisäinen koodi suoritetaan kerran ohjelman alkaessa.

createCanvas(width, height) tekee uuden kuvan (tässä tapauksessa 400 pikseliä korkean ja leveän) jolle voimme jatkossa piirtää P5:n piirtofunktioilla

draw() functio suoritetaan jokaisella framella / ruudunpäivityksellä

background() piirtää taustan. Jos sille annetaan argumenttina yksi luku 0-255 välillä se piirtää väriksi mustan (0) , valkoisen (255) tai jonkun harmaan sävyn niiden välillä (0-255). arvoksi voi antaa myös RGB (red, green, blue) arvon jolloin esim background(255,0,0) piirtäisi punaisen taustan. Arvon voi antaa myös tekstinä background('red')

 

Aloitetaan pisteellä

Aloitetaan piirtämällä piste:

 

Todetaan että sitä ei meinaa nähdä joten lisätään pisteen (ääriviivan eli stroken) kokoa:

 

Lisätään x ja y muuttujat joiden perusteella piste sijoitetaan. Kokeillaan liikutella pistettä manuaalisesti arvoja muuttamalla.

 

Piirretään horisontaali viiva while-silmukalla:

 

Muutetaan while-silmukka for-silmukaksi. Painota että for-silmukka ainoastaan lyhenne/shorthand äsken kirjoitetulle while-silmukalle ja mikään ei oikeasti muutu:

 

Tässä vaiheessa kannattaa ehkä korvata arvo 400 width-muuttujalla:

 

Siirtään muuttujan y määrittely for silmukan sisälle. Kokeilu havainnollistamisen vuoksi y:n arvoja x, 2x, -x tai vaikkapa 200 + sin(x*0.05) * 100 (<= tai .. * x):

 

Kokeile käyttää noise-funktiota. Huomaat että ruudun ylhäälle ilmestyy suttua. Tämä johtuu siitä että noise()-funktio palauttaa aina luvun välillä 0-1 eli y-koodrinaatit saavat arvoiksi 0-1

 

Kokeile kertoa lauseke jollain luvulla (0 - height) ja huomaat että pisteet jakautuvat eri korkeuksille melko kaaoottisesti. Aseta lopulta kertoimeksi height

 

Jaa noise-funktion argumentti eri luvuilla ja katso mitä tapahtuu. Aloita vaikka pienestä jakajasta 10 => 20 => 50 => 100

Lopulta visualisoi tätä hiiren x koordinaatilla noise(x/mouseX) ja liikuttele hiirtä vasemmalle ja oikealle ruudun päällä. Itse jätän luvun lopulta kuitenkin arvoon 200


^ Nyt koodin pitäisi näyttää tältä. Jos painat Play-symbolia useasti suorittaaksesi ohjelman, huomaat että maasto on joka kerralla erilainen. Tämä johtuu siitä että noise-algoritmi arpoo joka suorituksella uuden seedin, joka perusteella maailma generoidaan. Tämä termi voi olla tuttu esimerkiksi Minecraftista, jossa saman seedin kirjoittamalla maailmanluonnissa saa aina samanlaisen maailman

Kokeillaan seuraavaksi miten voisimme valita seedin omaan maailmaamme:


 

Nyt joka suorituksella meille generoituu samanlainen vuoristo. Kokeile vaihtaa numeroita.


Koitetaan saada maasto liikumaan eteenpäin kuten Hill Climb Racing:issa. Tehdään muuttuja x_alku joka kuvastaa vasemman reunan x-koordinaattia.

  1. Alusta muuttuja ensimmäisellä rivillä var x_alku = 0
  2. Sijoita se noise-funktion argumenttiin, jotta se vaikuttaa johonkin noise(x_alku + x/200)
  3. Kokeile kasvattaa muuttujan arvoa vaikka 0.2 kerrallaan. Eli var alku = 0.0, sitten 0.2 => 0.4 => 0.6 => 0.8 => 1.0. Huomaat että maasto siirtyy eteenpäin vähän kerrallaan.

Vuoriston liikutteleminen käsin on työkästä. Lisätään x_alku += 0.1 jotta maasto lähtee liikkumaan automaattisesti. Arvos kasvattamalla saa vuoriston liikkumaan hitaammin tai nopeammin:

 

Vaihdetaan point(x,y)-funktio line(x1,y1, x2,y2)-funktioksi jolloin vuoren korkeudelta piirretään pystysuora viiva ruudun alareunaan. Nyt meille piirtyy jo kunnollinen vuoristo!


Lisätään värit. Muista että stroke() ja background()-funktiot toimivat RGB-arvojen lisäksi myös värien nimillä stroke('blue') ja heksaluvuilla stroke('99F2FF')


 

Lisätään vielä järvet ja lumihuiput vuorille!

Eli jos y on isompi (alempana) kuin veden korkeusraja (200) niin piirretään sininen viiva y-korkeuden ja vesiraja-y:n (200) välille line(x,y, x, 200).

Jos y on pienempi (korkeammalla) kuin lumihuippujen raja (100) niin piirretään viiva y-korkeuden ja lumiraja-y:n (100) välille line(x,y, x, 100)


 

Valitsin äsken värit nimensä perusteella jotta ne vastaisivat esittämiänsä maaelementtejä. Tämä näytti todella rumalta, joten tässä oma yritykseni tyylitellä maasto

^ Laita hiiri kuvan päälle aktivoidaksesi koodi

 

 

Terraria

Tehdään äskinen mutta palikoilla!

Tähän käytetään samaa ruudukkokirjastoa, jota käytettiin igno.cc/loops harjoituksissa.

Tätä varten se on linkattava index.html tiedostoon

 

Kasvata canvaksen resoluutiota createCanvas(600, 600)

Lisää ruudukkokirjaston ruudukko(10, true) funktio. Ensimmäinen argumentti määrittää ruudukon leveyden ja korkeuden, toinen määrää piirretäänkö ruutujen koordinaatit (taulukoissa ruutuja kutsutaan soluiksi cell ja koordinaatteja indekseiksi index)

 

Piirretään harjoituksen vuoksi ensimmäinen ruutu:

 

Lopputuloksen pitäisi näyttää tältä:

1613316260775

 

Piirretään vaakasuora viiva käytteän ruudukko-kirjaston leveys-muuttujaa joka kertoo ruudukon leveyden ja korkeuden (tällä hetkellä 10):

 

Muutetaan äskinen for-silmukaksi. Taas muistetaan ettei koodin toiminnallisuus muutu mitenkään, for-silmukka on vain lyhenne äskiselle silmukalle:

 

Tehdään y-muuttuja ja asetetaan sen arvo noise-funktiolla:

1613316792230

Ruudut ilmestyvät ruudukon väliin. Tämä johtuu taas siitä että noise() palauttaa desimaalilukuja 0-1:n välillä. MIKÄÄN järkevä taulukko ei tukisi desimaaleja, mutta tämä on itse koodaamani joten se ei ole järkevä.


Kerrotaan noise(x):n lopputulos leveys-muuttujalla jotta saadaan ruudut leviämään koko ruudukon korkeudelle:

1613317170041


Jaetaan noise(x) eri luvuilla, jotta saadaan vähemmän kaaoottista noise()-maastoa. Itse pidin noise(x/5) lopputuloksesta:

1613317582692

 

Pyöristetään ruutujen kordinaatit alaspäin floor()-funktiolla, jotta saataisiin ruudukko toimimaan järkevästi:

1613317654770


Värjätään ruudukko vihreäksi:


Piirretään mutaa(/dirt) maahan saakka:


Lisätään ruudukon kokoa


..ja poistetaan koordinaatit:


Lisätään mudan lisäksi kivimateriaali:


Lisätään tausta:


Nyt koodin pitäisi näyttää tältä:

...ja lopputuloksen tältä:

1614512757130


Lisätään vielä puita. Lisää ihan koodin pohjalle piirraPuu(x,y)-funtio:

Ja kutsu koodia vihreän ruoho-blockin piirtokoodin alla (x-for-silmukan sisällä, muttei y-for-silmukan:


Nyt koko koodin pitäisi näyttää tältä:

ja pelin tältä:

1614513196668

Saari-maastogeneraatio

 

saari3

 

Ympyräfraktaalit

Pitäisi näyttää tältä:

fractal