Tasohyppelypeli 2: Kentän piirtäminen ja seiniin törmääminen
Tästä tuleekin aika hurja osa tähän tutoriaalisarjaan: ensin rakennetaan kenttä ja koodataan sen piirtäminen, sitten toteutetaan törmäystunnistus, jota varten kaikki kenttään piirretyt tiilet kerätään sijainteineen taulukkoon.
Kentän piirtäminen
Lisää main.h-otsikkotiedostoon alla olevat rivit. Kentästämme tulee siis toistaiseksi 20 tiiliä leveä ja 15 korkea, jolloin täytetään koko ruutu (20 × 16 = 320 ja 15 × 16 = 240).
Lisää tietue Tile ja sen jäseniksi neljä kokonaislukumuuttujaa x1, y1, x2 ja x2; näiden muuttujien arvoksi annetaan myöhemmin tiilien kulmien sijainti. Luo lopuksi tietuemuuttuja tile.
Seuraavaksi tehdään mielenkiintoinen mutta mälsä toimenpide, nimittäin rakennetaan kenttä. Koska käytössämme ei ole mitään hienoa kenttäeditoria, kenttä kirjoitetaan suoraan main.h-otsikkotiedostoon ns. kaksiulotteisena taulukkona, jossa alkion arvolla 0 ei piirretä mitään ja arvolla 1 piirretään tiili. Ole tarkkana, että taulukossa on vaakasuunnassa täsmälleen 20 ja pystysuunnassa 15 alkiota. Tämän kokoisena kenttä siis täyttää ruudun just eikä melkein ja on aika pieni. Mutta mitä jos pitäisi tehdä isompi kenttä? Silloin kameran pitäisi seurata pelaajahahmoa hänen liikkuessaan, mutta älä huoli, hoidetaankin sekin myöhemmin.
Tee sellainen kenttä kuin haluat main.h-lähdekooditiedostoon; tässä vaiheessa kenttä toimii eräänlaisena testihuoneena, jossa kokeillaan koodaamiamme ominaisuuksia.
Muuta tarvittaessa pelaajahahmon aloitussijaintia SetupGame-funktiomäärittelyssä muuttamalla muuttujien player.x ja player.y arvoja; sen lisäksi, että pelaajahahmon aloitussijainnin on oltava ruudun sisäpuolella, se ei saa olla tiilen sisässä.
Lisää main.h-otsikkotiedostoon vakio COLOR_TILE, jolla määritetään piirrettävän tiilin väri; tässä tapauksessa tiilit piirretään vaalean harmaiksi (191,191,191).
Lisää myös funktion DrawMap esittely parametreinaan sen alkion arvo, jolla tiili piirretään (int mapArrayIndex) sekä piirrettävän tiilin väri (int paletteIndex).
Lisää main.c-lähdekooditiedostoon funktion DrawMap määrittely. Ko. funktiossa käydään rakentamamme kentän (map[i][j]) alkiot läpi, ja aina, kun alkion arvo on kokonaislukumuuttujan mapArrayIndex arvo, piirretään yhden tiilin kokoinen neliö funktiokutsulla rectfill ja määritetään sen väri kokonaislukumuuttujalla paletteIndex.
Lopuksi lisää DrawGame-funktioon kutsu funktiolle DrawMap parametriarvoilla 1 ja COLOR_TILE; ensimmäisen parametrin arvolla 1 tiili siis piirretään aina, kun kenttätaulukon alkioita läpikäydessä alkion arvo on 1 ja se väritetään vakioon COLOR_TILE määritellyllä RGB-väriarvolla.
Nyt kun käännät koodin ja käynnistät pelin, pelitilaan piirretään pelaajahahmon lisäksi myös kenttä.
Seiniin törmääminen
Kuten peliä testatessasi ehkä huomasit, keltainen pelaajahahmomme sujahtaa tiilien läpi. Seuraavaksi koodataankin törmäystunnistus, jonka avulla pelaajahahmon törmätessä tiileen sen liikkuminen estetään yhteen tai useampaan suuntaan riippuen siitä, mistä suunnasta pelaajahahmo tiileen törmää. Samaa logiikkaa käytetään myöhemmin myös muissakin törmäyksissä eli silloin, kun pelaajahahmo osuu punaiseen neliöön ja kuolee sekä kun pelaajahahmo kerää sinisen neliön. Luvassa on siis suht tärkeää koodia!
Ensin määritellään funktio GetPositions, joka tallentaa tilePositions-taulukkoon kenttään piirrettyjen tiilien sijainnin. Sitten määritellään funktio Collided, jota kutsutaan aina, kun pelaajahahmo liikkuu ja jossa taulukkoon kerättyjen tiilien avulla tarkastellaan, mihin tiileen pelaajahahmo on osunut.
Tiilien sijainnin ja määrän tallentaminen
Luo Tile-tyyppinen taulukkomuuttuja tilePositions[MAP_WIDTH * MAP_HEIGHT], jonka alkioiden arvoksi määritetään kunkin tiilin kulmien sijainti.
Huomaa, että aiemmin muuttujan tile perässä oli puolipiste, joka nyt muuttuu pilkuksi, kun sen perään luodaan uusi muuttuja.
Lisää main.h-otsikkotiedostoon kokonaislukumuuttuja numTilePositions sekä GetPositions-funktion esittely. Muuttujan numTulePositions arvoksi määritetään myöhemmin kentän tiilien määrä.
Lisää sitten main.c-lähdekooditiedostoon GetPositions-funktion määrittely.
Funktiossa käydään jälleen läpi tekemämme kentän alkiot. Tällä kertaa aina, kun kentän, eli map-taulukon, alkion arvo on kokonaislukumuuttujan mapArrayIndex arvo:
- tallennetaan tiilen kulmien sijainti positions[count]-rakenteen muuttujiin x1, y1, x2 ja y2
- lisätään count-muuttujan arvoa yhdellä.
Lopuksi, kun kaikki map-taulukon alkiot on käyty läpi, annetaan *numPositions-muuttujan arvoksi count-muuttujan arvo; lasketaan siis, kuinka monta mapArrayIndex-alkiota kentästä on löytynyt.
Lisää sitten main.c-lähdekooditiedostoon GetPositions-funktion kutsu;
- Ensimmäisen parametrin arvolla 1 map-taulukon sijainti tallennetaan aina, kun taulukon alkion arvo on 1.
- Toisen parametrin arvolla määritetään, mihin muuttujaan map-taulukon alkioiden sijainti tallennetaan; tässä tapauksessa tilePositions-muuttujaan.
- Kolmannen parametrin arvolla määritetään, mihin muuttujaan kentästä löytyneiden alkioiden määrä tallennetaan; tässä tapauksessa &numTilePositions-muuttujaan.
Collided-funktio
Seuraavaksi toteutetaan varsinainen törmäystunnistus.
Lisää main.h-otsikkotiedostoon Collided-funktion esittely. Parametreja on hurjat viisi kappaletta:
- Parametria struct Player *player käytetään pelaajahahmon nykyisen sijainnin tarkastelemiseen.
- Parametria struct Tile *positions käytetään pelaajahahmon tulevan sijainnin sekä kentän tiilien sijaintien vertailemiseen.
- Parametrilla int numPositions selvitetään, kuinka monta kertaa törmäystarkistus tehdään; se tehdään yhtä monta kertaa kuin kentästä on löytynyt tiilejä.
- Kahta viimeistä parametria int dx ja int dy käytetään pelaajahahmon tulevan sijainnin tarkastelemiseen.
Lisää sitten main.c-lähdekooditiedostoon Collided-funktion määrittely.
Ensin määritellään kokonaislukumuuttujat newX ja newY, joiden alkuarvoksi annetaan pelaajahahmon nykyinen sijainti, johon on lisätty kokonaislukumuuttujien dx ja dy arvot; tämä on pelaajahahmon tuleva sijainti hänen liikkuessaan.
Sitten käydään for-silmukassa läpi kerätyt tiilet (numPositions) ja aina, kun:
- pelaajahahmon tulevan vaakasijainnin vasen reuna on tiilen sisäpuolella sen oikeasta reunasta (newX < positions[i].x2)
- pelaajahahmon tulevan vaakasijainnin oikea reuna on tiilen sisäpuolella sen vasemmasta reunasta (newX + player->width > positions[i].x1)
- pelaajahahmon tulevan pystysijannin yläreuna on tiilen sisäpuolella sen alareunasta (newY < positions[i].y2)
- pelaajahahmon tulevan pystysijainnin alareuna on tiilen sisäpuolella sen yläreunasta (newY + player->height > positions[i].y1)
...törmäys tapahtuu. Tällöin lopetetaan funktion suorittaminen palautusarvolla 1 (return 1). Jos taas for-silmukka suoritetaan loppuun ilman törmäyksiä, lopetetaan funktion suorittaminen palautusarvolla 0 (return 0).
Lisää sitten main.c-lähdekooditiedoston MovePlayer-funktion määrittelyyn kunkin nuolinäppäinpainalluksen tarkistukseen Collided-funktion kutsu, jolloin tarkistetaan onko pelaajahahmon tuleva sijainti tiilin sisällä; jos ei ole, sallitaan pelaajahahmon liikkuminen.
Eli aina, kun:
- pelaaja painaa esim. nuolinäppäintä ylös (key[KEY_UP])
- ja pelaajahahmon sijainti on ruudun yläreunan alapuolella (player.y >= 2)
- ja Collided-funktion palautusarvo on 0 eli epätosi (!Collided(&player, tilePositions, numTilePositions, 0, -2)
sallitaan pelaajahahmon liikkuminen ylöspäin. Jos taas Collided-funktiokutsun palautusarvo on 1 eli tosi, pelaajahahmoa ei liikuteta ylöspäin. Ja samat tarkistukset tehdään myös muita nuolinäppäimiä painettaessa.
Huomaa kunkin Collided-funktiokutsun kaksi viimeistä parametriarvoa, jotka ovat joko 0, 2 tai -2. Nämä parametrit määrittävät pelaajahahmon tulevan sijainnin kahden pikselin päähän nykyisestä joko ylös, alas, vasemmalle tai oikealle, mikä riippuu painetusta nuolinäppäimestä. Esim. nuolinäppäintä ylös painettaessa pelaajahahmon tuleva sijainti vaakasuunnassa on sama kuin nykyinen, jolloin funktiokutsussa toiseksi viimeisen parametrin arvo on 0. Pystysuunnassa pelaajahahmon tuleva sijainti taas asetetaan kaksi pikseliä nykyisen sijainnin yläpuolelle ja katsotaan, onko tuleva sijainti tiilen sisässä vai ei.
Kommentit
Lähetä kommentti