Roguelike-opas:Tekoälyn asentaminen ja liikuttaminen

Merriki
Loikkaa: valikkoon, hakuun

Pelissä on jo valmis kartta, mutta pelaaja on siinä yksin. Se tarvitsee muutamia olentoja ja tätä varten tarvitaan muutamia funktioita ja muutoksia jo olemassa olevaan koodiin:

  • luo_olentoja()
  • liikuta_vihollista()

Aivan ensimmäiseksi on luotava vektoreilla struktuuri viholliselle. Tämän vuoksi projektiin on sisällytetty vector-kirjasto alussa.

Lisää PELI-struktuurin sisälle seuraava koodi:

std::vector <OLENTO> vihollinen;

Eli struktuuri näyttää nyt tältä:

struct PELI
{
    KARTTA kartta; //pelikartta
    OLENTO pelaaja; //pelaaja
    std::vector <OLENTO> vihollinen; //tekoälyt
} peli;


Ja koska vihollisia on muutamaa erilaista tyypiltään, kirjoitetaan ennen funktioita taas muutama #define-rivi:

//tekoälyn kaksi eri muotoa
#define normaali_liike 0
#define seuraa_pelaajaa 1


Mutta tämä ei tietenkään riitä vielä. Seuraavaksi asennetaan itse viholliset kartalle: Kirjoita ennen luo_kartta-funktiota seuraavanlainen aliohjelma:

//funktio joka mahdollistaa olentojen luomisen. Parametri 'maara' kertoo, montako olentoa luodaan karttaan.
void luo_olentoja(int maara)
{
    OLENTO temp; //tehdään väliaikainen muoto tulevalle olennolle
    for (int i = 0; i < maara; i++)
    {
        while(1) //poistutaan silmukasta vasta kun on sopivat koordinaatit löydetty.
        {
            temp.x = rand() % 80; //satunnaisesti etsitään jotain paikkaa
            temp.y = rand() % 25;
            temp.target_x = temp.x;
            temp.target_y = temp.y;
 
            if (!este(temp.x, temp.y)) //ei ole este, jotenka voidaan poistua while-silmukasta
                break;
        }
        temp.energia = 10; //annetaan energiaa olennolle
        temp.moodi = rand() % 2;
        peli.vihollinen.push_back(temp); //lopuksi lisätään se muiden joukkoon.
    }
}


Funktio luo niin monta olentoa kuin parametrissä on päätetty. Jotta funktio saadaan toimimaan, lisätään sen kutsu kartan rakentamiseen. Eli mene luo_kartta()-aliohjelman loppuun ja ennen funktion lopetussulkua lisää seuraavanlainen rivi:

luo_olentoja(2 + rand() % 20);


Tämä luo olentoja väliltä 2..20. On tärkeää, ettei luku ole 0, koska pelissä siirrytään seuraavaan karttaan kun vihollisten määrä on nolla. Sen vuoksi tehdään muutama ylimääräinen vihollinen.

Ja jotta näemme viholliset ruudulla, on vihollisille tehtävä oma piirtofunktio, jota kutsutaan kartan piirtämisen jälkeen.

Piirtofunktio voi olla seuraavanlainen:

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.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
    }
}


Kun funktiota kutsutaan kartan piirtämisen jälkeen, tulostuu hirviöt oikein. On tärkeää, ettei funktiota kutsuta ennen kartan piirtämistä, sillä muuten kartan piirto pyyhkii vihollisten merkit ruudulta.

Viholliset eivät vielä liiku ja niiden läpi pääsee menemään. Tarvitaan siis taas muokkaus este-funktioon, joka on seuraavanlainen: Lisää este-funktion sisälle ennen ehtolausekkeita seuraava:

 
//käydään kaikki viholliset läpi
for (int i = 0; i < (int)peli.vihollinen.size(); i++)
{
    if (peli.vihollinen[i].x == x && peli.vihollinen[i].y == y) //jos vihollisen koordinaatit täsmää koordinaattien kanssa josta kysellään, että onko este, niin palautetaan arvoksi true, sillä vihollinen on este.
    {
        return true;
    }
}


Tekoäly rakentuu nyt oikein ja se osataan piirtää ruudulle, mutta se ei osaa vielä liikkua, jotenka siihen tarvitaan aliohjelma. Aliohjelmaa kutsutaan main-funktion silmukassa joko ennen kuin luetaan näppäimistöstä nappia tai ennen piirtämistä. Piirtäminen jätetään tässä oppaassa silmukan viimeisiksi tehtäviksi.

  • Ensimmäinen liikkumistapa:

Tämä tapa luo vihreille $-merkeille tavan liikkua satunnaisesti ilman suurempaa päämäärää.

  • Toinen liikkumistapa:

Punaiset $-merkit koittavat mennä pelaajan suuntaan.

Luodaan funktio liikuta_vihollisia(), mikä palauttaa luvun 0 tai -1, koska siinä samassa tarkistetaan myöhemmin, että onko pelaaja enää elossa lyömisen jälkeen (väkivaltaisia olentoja!)

Lisätään funktio main-aliohjelmaan seuraavanlaisella tapaa silmukan while-sisälle:

if (liikuta_vihollisia() < 0)
            break;


Funktion rakenne: Jos vihollisen moodi on normaali_liikkuminen se valitsee kohdekoordinaateiksi minkä tahansa kohdan kartasta. Mutta jos moodina on seuraa_pelaajaa, on kohdekoordinaatit jatkuvasti siinä, missä pelaaja on. Näiden jälkeen tarkistetaan, että onko kohde missä suunnassa verraten viholliseen. Jos on esimerkiksi vasemmalla puolella, niin koitetaan liikkua siihen suuntaan, jos alhaalla, niin koitetaan päästä alaspäin ja niin edelleen.

Ennen kuin luodaan funktio, tarvitsee este-funktioon tehdä vielä viimeinen lisäys. Kirjoita sen sisälle ensimmäiseksi seuraavanlainen ehto:

if (x == peli.pelaaja.x && y == peli.pelaaja.y)
    return true;


Nyt viholliset törmäävät pelaajaan, jos tämä on edessä.

Ja itse liikkumisfunktio voi olla seuraavanlainen:

int liikuta_vihollisia()
{
    int x, y = 0; //apumuuttujat
 
    for (int i = 0; i < (int)peli.vihollinen.size(); i++) //käydään kaikki viholliset läpi
    {
        x = 0; //alustetaan jokaisella silmukkakierroksella
        y = 0;
        //normaali liike: ei seurata pelaajaa, vaan haahuillaan ympäri alueita.
        if (peli.vihollinen[i].moodi == normaali_liike)
        {
            peli.vihollinen[i].target_x = rand() % 80; //asetaan arvot, mihin suunnistetaan
            peli.vihollinen[i].target_y = rand() % 25;
        } else if (peli.vihollinen[i].moodi == seuraa_pelaajaa)
        {
            peli.vihollinen[i].target_x = peli.pelaaja.x; //asetetaan arvot kiinni pelaajaan
            peli.vihollinen[i].target_y = peli.pelaaja.y;
        }
 
            int liikutus = rand() % 10; //tällä luodaan hieman hidastusta liikkumiseen
 
            if (liikutus > 3) //saatiin luku, joka mahdollistaa liikkumisen
            {
                //luodaan arvot, mihin suuntaan aletaan liikkual. Eli jos kohde-arvo on vähemmän kuin lähde, silloin liikutaan vasemmalle esimerkiksi...
                if (peli.vihollinen[i].target_x < peli.vihollinen[i].x)
                    x--;
                if (peli.vihollinen[i].target_x > peli.vihollinen[i].x)
                    x++;
                if (peli.vihollinen[i].target_y < peli.vihollinen[i].y)
                    y--;
                if (peli.vihollinen[i].target_y > peli.vihollinen[i].y)
                    y++;
            }
 
            //apumuuttujat. Helpottaa hieman tekemistä.
            int px = peli.vihollinen[i].x + x;
            int py = peli.vihollinen[i].y + y;
 
            //jos apumuuttujien kohdat eivät ole esteessä, toteutuu ehto ja voidaan sijoittaa viholliselle koordinaatit uudesta sijainnista.
            if (!este(px, py))
            {
                peli.vihollinen[i].x = px;
                peli.vihollinen[i].y = py;
            }
    }
    return 0; //palautetaan onnistunut suoritus
}


Nyt vihollisilla on kaksi liikkumatilaa: satunnainen harhailu tai pelaajaa kohti tuleva vihainen dollarimerkki.

Henkilökohtaiset työkalut
Nimiavaruudet
Muuttujat
Toiminnot
Merriki
Työkalut