SuomiGameHUB

Maailma tarvitsee pelejä

Toistolauseet

Tarkastelimme viimeksi ehtolauseita ja nyt siirrymme tutkimaan toistolauseita eli looppeja. Tutkimme tässä oppaassa toistolauseita WinUI-esimerkin kautta C#-kielellä, mutta sama asia pätee lähes jokaiseen ohjelmointikieleen.

Miksi tarvitsemme looppeja ja miksi emme voi suorittaa kaikkea ilman niitä? Loopit ovat erityisen tärkeitä pelikehityksessä. Peleissä pelitilaa päivitetään jatkuvasti toistolauseiden avulla, usein jopa satoja kertoja sekunnissa. Tämä on välttämätöntä, jotta pelitilanne pysyy ajan tasalla ja näyttö päivittyy sujuvasti. Pelimoottorit kuten Unity ja Unreal käyttävät tätä logiikkaa; ne sisältävät tiloja kuten ”update”, joka päivittää pelitilannetta ja renderöintitoimintoja, jotka piirtävät pelitilanteen näytölle.

Otetaan esimerkki toistolauseesta käyttäen while-avainsanaa.

while (true)
{
  // toista jotain
}

Tämä silmukka toimii niin, että se pyörii niin kauan kuin määrätty ehto on voimassa. Jos esimerkiksi asetetaan ehdoksi ”true”, silmukka pyörii ikuisesti, ellei sitä erikseen pysäytetä. Tämä voi olla ongelmallista, sillä se saattaa jumittaa koko koneen. Järkevämpää on käyttää jotain konkreettista ehtoa, joka voidaan asettaa ”falseksi” tarvittaessa. Jos ehto on jatkuvasti ”true”, voidaan silmukasta poistua käyttämällä break-avainsanaa.

Peleissä `while`-silmukka voi esimerkiksi pyöriä niin kauan kuin peli on käynnissä. Toistolauseen sisällä voidaan kutsua erilaisia funktioita, kuten päivitys- ja renderöintifunktioita. Kun pelaaja painaa ”exit”, silmukka katkeaa. Tämän jälkeen voidaan suorittaa ohjelman sulkemiseen liittyvät toimenpiteet. Koska sulkutoiminnot ovat silmukan ulkopuolella, ne eivät aktivoitu ennen kuin silmukasta on poistuttu.

while (true)
{
  update();
  render();
  // Jos exit-muuttujan arvo on true, mennään breakilla pois silmukasta.
  if (exit)
  {
    break;
  }
}

// Tänne päästään vasta kun silmukasta on poistuttu
suljeOhjelma();

Erikoisoperaattorit

Nämä erikoisemmat operaattorit ovat usein hyödyllisiä juuri toistolauseiden kanssa, mutta niitä voi toki käyttää muuallakin.

Operaattori Selitys Esimerkki
% Modulo, palauttaa jakojäännöksen a % b
++ Kasvattaa arvoa yhdellä a++
Vähentää arvoa yhdellä a–
+= Lisää arvon muuttujaan ja sijoittaa tuloksen muuttujaan a += b
-= Vähentää arvon muuttujasta ja sijoittaa tuloksen muuttujaan a -= b
*= Kertoo muuttujan arvon ja sijoittaa tuloksen muuttujaan a *= b
/= Jakaa muuttujan arvon ja sijoittaa tuloksen muuttujaan a /= b
%= Modulo-operaatio muuttujalle ja sijoittaa tuloksen muuttujaan a %= b

Rajatut toistolauseet

Jos haluamme toistolauseen, joka toistetaan vain vaikkapa 10 kertaa, voimme käyttää kokonaislukumuuttujaa nimeltä `index`, jonka arvo on aluksi nolla. Asetamme `while`-silmukan ehdoksi, että `index` on alle 10. Silmukan sisällä voimme kasvattaa `index`-muuttujan arvoa yhdellä. Tämä voidaan ilmaista monella tapaa, mutta yksinkertaisin on käyttää lyhennettyä ++ notaatiota. Silmukan sisällä voidaan esimerkiksi päivittää näyttöä tai lisätä tekstiä tulostusmuuttujaan.

int index = 0;
string tulos = "";

while (index > 10)
{
  tulos += "hei";
  index++;
}

tekstikentta.Text = tulos;

Kun puhutaan `while`-silmukasta, se on yleensä suunniteltu tekemään jotain toistuvasti niin kauan, kunnes tietty ehto täyttyy. Tämä on erityisen hyödyllistä, kun etukäteen ei voi tietää, kuinka monta kertaa silmukan pitäisi toistua. `While`-silmukkaa voisi kuvata jatkuvaksi toiminnaksi, joka jatkuu, kunnes jokin tapahtuma tai ehto saa sen pysähtymään.

Toisaalta for-silmukka on usein selkeämpi, kun tiedät tarkalleen, kuinka monta kertaa silmukan pitäisi toistua. Koodin luettavuuden ja ymmärrettävyyden kannalta `for`-silmukka on usein parempi vaihtoehto kuin `while`-silmukka. Se sisältää kolme osaa: alkuasetelman, ehtotarkistuksen ja silmukan päivityksen. Nämä kolme osaa erotetaan toisistaan puolipisteillä ja ne kertovat selkeästi silmukan toimintalogiikan.

for (int i = 0; i < count; ++i)
{
  /* code */
}

Otetaan esimerkki peliohjelmoinnista, jossa haluat käydä läpi pelialustan kaikki ruudut tai ”tilet”. Jos pelialustasi on esimerkiksi 30 ruutua leveä ja 20 ruutua korkea, voit käyttää kaksiulotteista `for`-silmukkaa ruutujen läpikäymiseen. Tässä yhteydessä ensimmäinen `for`-silmukka voisi käydä läpi y-akselin ja toinen x-akselin. Silmukoiden ehdot määritellään pelialustan leveyden ja korkeuden mukaan ja koodissa voidaan esimerkiksi renderöidä jokainen tile sen koordinaattien mukaan.

int leveys = 30;
int korkeus = 20;

for (int y = 0; y < korkeus; y++)
{
  for (int x = 0; x < leveys; x++)
  {
    // Piirrä ruutu, joka on koordinaateissa x, y.
    draw(x, y);
  }
}

Tämä lähestymistapa on erityisen tehokas, koska se tekee koodista selkeämmän ja helpommin ymmärrettävän. Kun muut ohjelmoijat lukevat koodiasi ja näkevät `for`-silmukan, he ymmärtävät heti, että olet käymässä läpi jotain tietorakennetta määrätyin askelin. Se poistaa tarpeen syventyä silmukan yksityiskohtiin, kun sen perusidea on heti selvä.

Edellinen osa: Lisää ehtolauseista.