Roguelike-opas:Näkyvyysalue ja esimerkkejä
| Roguelike-oppaan sisältö |
|---|
|
Line of Sight (suom. näkyvyysalue) on tämän oppaan viimeisen osion aihe. Kyseessä on tekniikka, jolla kentästä piirretään vain se alue, joka on mahdollista nähdä. Näin ei voida nähdä vaikkapa talojen läpi. Tätä varten joudutaan muokkaamaan funktioita piirra_viholliset() sekä piirra_kartta(), ja myöskin luodaan kaksi funktiota: Toinen tyhjentää näkyvyysalueen joka kerta ennen kuin piirretään mitään ja toinen laskee, mitä nähdään. Aloitetaan muutoksista:
- piirra_kartta()
Muokkaa funktio seuraavanlaiseksi:
void piirra_kartta() { //alla olevaan kahteen muuttujaan säilötään kartan graafiset tiedot per solu char merkki = 0; char vari = 0; //käydään koko kartta läpi for (int x = 0; x < 80; x++) { for (int y = 0; y < 25; y++) { if (peli.kartta.nako[x][y]) //jos on näkyvää aluetta, niin piirretään se. { merkki = peli.kartta.merkki[x][y]; //otetaan merkit talteen vari = peli.kartta.vari[x][y]; } else { //muussa tapauksessa piirretään pisteitä merkki = '.'; //otetaan merkit talteen vari = 0; } mvaddch(y, x, merkki|COLOR_PAIR(vari)); //itse piirto ruudulle } } return; }
- piirra_viholliset()
void piirra_viholliset() { int vari = 0; //väri on oletuksena mustavalkoinen (0). int x = 0; int y = 0; for (int i = 0; i < (int)peli.vihollinen.size(); i++) //käydään kaikki viholliset läpi { x = peli.vihollinen[i].x; y = peli.vihollinen[i].y; if (peli.kartta.nako[x][y]) //jos on näkyvää aluetta { if (peli.vihollinen[i].moodi == normaali_liike) //jos on normaali vihollinen joka ei hyökkää pelaajan kimppuun.. vari = vihrea; //... tehdään siitä vihreä else //muussa tapauksessa, koska vihollinen on agressivinen, tehdään siitä punainen vari = punainen; //lopuksi itse piirto mvaddch(y, x, '$'|COLOR_PAIR(vari)|A_BOLD); //vihollinen on $-merkki, COLOR_PAIR määrittelee, mitä väriä käytetään ja A_BOLD luo merkistä paksumman } } }
Nyt jos ajaa ohjelman, on koko ruutu täynnä pisteitä. Mutta sitä varten tarvitaan Line of Sight -funktiota.
Sen idea on, että valituista aloituskoordinaateista piirretään ympärille pisteitä joista muodostuu viivoja. Jos viivan nykyisessä päässä on este, lopetetaan merkitseminen. Muussa tapauksessa merkataan, että alue on näkyvä.
Lisää ohjelman alkuun otsikkotiedostojen listauksen jälkeen seuraava rivi:
//luodaan radiaanista vakio, sillä luvun ei tarvitse muuttua sen jälkeen kun sille on annettu arvo. const double rad = atan(1) * 4 / 180; //tarvitaan radiaaneja sini- ja kosini-funktioita varten (line of sight)
Tämä siksi, koska C ja C++ palauttaa sinistä ja kosinista arvon radiaanina. Tämä tulee seuraavassa funktiossa esille:
//Tämän tarkoitus on laskea alue, joka voidaan nähdä. void laske_los(int x, int y, int pituus) //parametrit ovat seuraavat: x ja y on koordinaatteja joista jana alkaa. Pituus on, että montako askelta janaa piirretään. { double px; //apumuuttujat, joiden avulla tiedetään, missä solussa liikutaan. double py; double lx = 0; double ly = 0; for (int i = 0; i < 360; i++) //käydään 360 astetta läpi. { /* 0.5 lisätään, jotta funktio toimii oikein. Jos lisäystä ei olisi, jäisi oikea- sekä yläreuna kokonaan pois. Nyt lisäämällä "puolikkaan solun" verran arvoa saadaan funktio toimimaan oikein. */ px = x + 0.5; py = y + 0.5; lx =sin(i*rad) * 0.5; //otetaan talteen, jottei tarvitse aina uusiksi laskea ly = cos(i*rad) * 0.5; for (int a = 0; a < pituus; a++) //luodaan niin pitkä viiva kuin on määritelty funktion parametreissa. { px += lx; //lisätään joka kierros hieman arvoa, jolloinka syntyy lopulta viiva joka menee johonkin tiettyyn suuntaan. py += ly; //Cmath-kirjaston sini- ja kosini-arvot ottavat sisälleen radiaaneja lähinnä. Sen vuoksi on kerrottava radiaanilla (joka on alussa määritelty (const double rad...)) //tehdään tarkistus, että pysytään kartan sisällä. Muuten voidaan kirjoittaa alueisiin, joihin ei saa kirjoittaa tietoa. if (px >= 0 && py >= 0 && px < 80 && py < 25) { peli.kartta.nako[(int)px][(int)py] = true; //merkataan alue nähtäväksi. //solun kohdassa on este. Hypätään pois silmukasta ja aloitetaan uusi jana lähtökoordinaateista (x,y). if (peli.kartta.seina[(int)px][(int)py]) break; } } } }
Funktiota täytyy kutsua jostain, ja paras paikka sille on ennen grafiikan piirtämistä. Tässä esimerkissä se on lisätty main-funktion while-silmukkaan kohtaan, ennen kuin piirretään yhtään mitään. Parametriksi se ottaa pelaajan koordinaatit ja itse säteen pituuden.
laske_los(peli.pelaaja.x, peli.pelaaja.y, 25);
Mutta nyt jää vanhat arvot talteen, eli funktion tuottama tulos ei ole aivan sitä mitä siltä vaaditaan. Tämän vuoksi kutsutaan tyhjenna_los()-funktio ennen kuin laske_los()-funktiota kutsutaan.
tyhjenna_los()-funktio on seuraavanlainen:
//apufunktio, joka tyhjentää näkökentän täysin. Jos tämän jälkeen ei lasketa uusiksi näkyvää aluetta, jää ruutu pimentoon. void tyhjenna_los() { for (int x = 0; x < 80; x++) { for (int y = 0; y < 25; y++) { peli.kartta.nako[x][y] = false; //tyhjennetään jokainen solu. } } }
Nyt kun pelaaja painaa näppäintä, tyhjennetään Line of Sightin luoma näkyvyys ja lasketaan se uusiksi.
Tämän oppaan mukana tulee muutama esimerkki tästä hyvin pienestä moottorista:
- Tämän oppaan koodi kokonaisuudessaan
- Kyliin sijoittuva peli
- Luolastoihin sijoittuva peli
Esimerkeissä on mukana niin lähdekoodit kuin ajettavat exe-tiedostot. opaskoodit