Raycaster-pelimoottori 2: Seinien piirtäminen ja pelaajan liikkuminen
Tässä osassa lisätään seinien laskeminen ja piirtäminen sekä pelaajan liikkuminen törmäystunnistuksineen.
Globals.h
Tee globals.h-otsikkotiedostoon seuraavat lisäykset:
- Vakiot MAP_WIDTH ja MAP_HEIGHT määrittävät pelin kartan koon (24 × 24 ruutua).
- Map[MAP_HEIGHT][MAP_WIDTH] on globaali 2D-taulukko, joka kuvaa pelin karttaa. Kartassa arvo 1 määrittää seinät ja 0 tyhjän tilan.
- PlaneX ja planeY määrittelevät kameratason, joka vaikuttaa pelaajahahmon näkökenttään ja säteiden suuntaan.
Main.c
Tee main.c-lähdekooditiedostoon seuraavat lisäykset main-funktiomäärityksen yläpuolelle:
- Map-muuttuja alustetaan kuvastamaan pelin kenttää. Kartta sisältää seiniä ja tyhjiä alueita, jotka määrittävät, miten pelaajahahmo ja säteet liikkuvat.
- Tee kentästä millainen haluat, mutta anna kaikkien reuna-alkioiden arvoksi 1; emmehän halua, että pelaaja voi nähdä tai karata kentän ulkopuolelle.
- PlaneX ja planeY asettavat kameratason, joka vaikuttaa näkökentän leveyteen ja perspektiiviin.
- Huomaa planeY-muuttujalle annettu arvo 158.4 / VIEW3D_HEIGHT. Tällä arvolla 3D-näkymän muoto piirretään oikein.
Seinien laskeminen ja piirtäminen
Draw.h
Lisää draw.h-otsikkotiedostoon seuraavat funktioesittelyt parametreineen:
- HandleWalls laskee 3D-näkymän seinät.
- DrawWallsAsFlatColors piirtää seinät yksinkertaisilla väreillä.
int drawEnd);
Draw.c
Sisällytä draw.c-lähdekooditiedostoon matematiikkakirjasto math.h.
Lisää sitten HandleWalls-funktion määrittely, jossa toteutetaan raycasting-algoritmi. Tiivistetysti pelaajan sijainnista lähetetään yhtä monta sädettä kuin ruudun vaakasuunnassa on pikseleitä (eli 320), ja aina, kun säde kohtaa karttaruudun reunan, katsotaan, onko ruutu tyhjä (map-taulukon arvo 0) vai onko ruutu määritetty seinäksi (map-taulukon arvo 1). Jos ruutu on tyhjä, säde jatkaa matkaa seuraavan ruudun reunaan asti, ja sama tarkistus tehdään uudestaan. Tämä jatkuu niin kauan, kun tarkasteltava ruutu on tyhjä. Kun seinä sitten jossain vaiheessa tulee vastaan, lopetetaan säteen eteneminen ja piirretään ruutuun pystyviiva; mitä kauempana pelaajasta seinäosuma tulee, sitä lyhyempi viiva piirretään.
Kattavampi selitys ja alkuperäinen koodi löytyy osoitteesta https://lodev.org/cgtutor/raycasting.html#Untextured_Raycaster_.
Lisää sitten draw.c-lähdekooditiedostoon DrawWallsAsFlatColors-funktion määrittely, jossa määritellään seinien väri ja piirretään pystyviiva.
- Seinät piirretään punaiksi rgbColor[3]-funktion arvolla {255, 0, 0}.
- Jos seinä piirretään y-suunnassa (if (side == 1)), sen väri tummennetaan jakamalla alkuperäinen väri kahdella.
- Tämä tekee 3D-näkymästä selkeämmän, sillä kun y-suunnan seinien väri on tummempi, ne ovat ikään kuin varjossa, mikä tuo niiden muodon esiin.
- Lopulta piirretään pystysuora viiva kutsumalla funktio vline.
Tee seuraavat lisäykset draw.c-lähdekooditiedoston DrawGame-funktioon.
- Luo muuttuja zBuffer[SCREEN_WIDTH].
- Lisää kutsu HandleWalls-funktiolle.
- Lisää kutsu vsync-funktiolle, jolla synkronoidaan ruudunpäivitys.
Seinien piirtämiseen liittyvät lisäykset on nyt tehty. Jotta seinät saadaan pelitilassa näkyviin, täytyy vielä määritellä pelaajahahmo ja sen ominaisuudet.
Alkuvalmistelut pelaajan liikuttamiseksi ja törmäyksien havaitsemiseksi
Pelaajahahmo laitetaan liikkumaan nykyaikaisella WASD-näppäinten ja hiiren yhdistelmällä. Jos haluaisimme toteuttaa ihan oikean vanhan koulukunnan buumeriammuskelun, liikkuminen eteen ja taakse sekä kääntyminen vasemmalle ja oikealle tapahtuisi nuolinäppäimillä. Entäs sitten liikkuminen sivulle eli streiffaaminen? Painettaessa nuolinäppäimiä vasemmalle ja oikealle pidettäisiin ALT-näppäintä pohjassa. Yksinkertaista!
Pelaajahahmon liikkumisen ja kääntymisen lisäksi toteutetaan myös törmäystunnistus, jotta seinistä ei päästä läpi.
Types.h
Määrittele types.h-otsikkotiedostoon Player-rakenne, joka sisältää tiedot pelaajan sijainnista, suunnasta,
nopeudesta ja fyysisestä säteestä.
Globals.h
Sisällytä siten globals.h-otsikkotiedostoon types.h-otsikkotiedosto.
Tee myös seuraavat lisäykset:
- Määrittele player-muuttuja globaaliksi, jotta sitä voidaan käyttää kaikissa tiedostoissa.
- Määrittele globaalit muuttujat mouseX ja mouseY, joita käytetään hiiren sijainnin seuraamiseen.
Main.c
Alusta main.c-lähdekooditiedostoon player-rakenteen arvot, jotka määrittelevät pelaajan alkutilan:
- posX ja posY ovat pelaajahahmon sijainti
- dirX ja dirY ovat pelaajahahmon suunta
- moveSpeed on pelaajahahmon liikkumisnopeus, jonka arvo määritettiin vakioon P_MOVE_SPEED
- radius on pelaajahahmon säde.
Alusta myös muuttujat mouseX ja mouseY alkuarvoilla 0.
Setup.h
Esittele setup.h-otsikkotiedostossa SetupGame-funktio.
Setup.c
Määrittele setup.c-lähdekooditiedostossa SetupGame-funktio:
- asennetaan hiiren toiminnallisuus funktiokutsulla install_mouse
- laitetaan pelaajahahmo tiettyyn paikkaan ja sen katse tiettyyn suuntaan antamalla arvot muuttujille player.posX, player.posY, player.dirX ja player.dirY.
Main.c
Nyt kun koodin kääntää ja GAME.EXEn suorittaa, piirretään seinät yksivärisinä pystyviivoina. Vielä ei voida kuitenkaan liikkua.
Pelaajan törmäystunnistus
Seuraavaksi tehdään kaikki pelaajahahmon törmäystunnistukseen liittyvät lisäykset.
Utils.h
Lisää utils.h-otsikkotiedostoon Collided-funktion esittely.
Utils.c
Tee sitten seuraavat lisäykset utils.c-lädekooditiedostoon:
- sisällytetään Allegro-kirjasto.
- sisällytetään globals.h, jotta päästään käsiksi globaaleihin muuttujiin.
- sisällytetään tämän tiedoston otsikkotiedosto utils.h.
Lisää sitten utils.c-lähdekooditiedostoon Collided-funktion määrittely.
Collided-funktio tarkistaa pelaajahahmon sijainnin ja säteen perusteella, osuuko pelaajahahmo ympärillään oleviin seinäruutuihin. Se laskee seinäruudun lähimmän pisteen etäisyyden pelaajahahmon sijaintiin ja vertaa tätä säteeseen. Jos etäisyys on pienempi kuin säde, funktio havaitsee törmäyksen ja palauttaa arvon 1; muuten palautetaan 0.
Pelaajan liikkuminen ja kääntyminen
Seuraavaksi toteutetaan pelaajahahmon liikkuminen WASD-näppäimillä ja sen kääntyminen hiirellä.
Player.h
Lisää player.h-otsikkotiedostoon MovePlayer- ja RotatePlayer-funktioiden esittely.
Player.c
Tee sitten seuraavat kirjasto- ja otsikkotiedostosisällytykset player.c-lähdekooditiedostoon:
- matematiikkakirjasto math.h, jota käytetään esim. kulmien laskemiseen
- Allegro-kirjasto allegro.h, jonka avulla päästään käsiksi näppäimistön ja hiiren syötteisiin
- globals.h, player.h sekä utils.h, joista saadaan käyttöön globaalit muuttujat, pelaajahahmon tietue sekä Collided-funktio.
Lisää sitten player.c-lähdekooditiedostoon Moveplayer-funktion määrittely.
Tämä funktio vastaa pelaajahahmon liikkumisesta eteen, taakse ja sivuille. Pelaajahahmon liike on riippuvainen seuraavista:
- näppäinpainallukset W (eteen), S (taakse), A (vasemmalle), D (oikealle).
- tarkistetaan törmäykset ja estetään pelaajahahmoa menemästä seinien läpi
- jos pelaajahahmo törmää seinään, liukumaominaisuus sallii hänen jatkaa liikkumista toista akselia pitkin
- estetään pelaajahahmon jumiutuminen seinien kulmiin.
Funktio käyttää törmäyksen havaitsemiseen Collided-funktiota ja muokkaa pelaajan sijaintia muuttujien posX ja posY avulla.
Funktiomäärittelystä tulee aika hirviömäinen, sillä kaikissa neljässä suunnassa, joihin pelaajahahmo voi liikkua, katsotaan, törmääkö pelaajahahmo seinään liikkuessaan diagonaalisesti kaikkiin neljään suuntaan: vasempaan ja oikeaan ylä- ja alaviistoon. Tämä sen takia, jotta pelaajahahmo ei jää jumiin seinien kulmiin. Toimiipahan ainakin.
Lisää sitten player.c-lähdekooditiedostoon RotatePlayer-funktion määrittely.
Tämä funktio vastaa pelaajan suunnan muuttamisesta hiiren liikkeen perusteella:
- lasketaan hiiren liikkeen määrä x-akselilla ja muutetaan sitä herkkyyskertoimen avulla
- päivitetään pelaajan suunta (dirX ja dirY) pyörimismatriisilla trigonometristen funktioiden kosini (cos) ja sini (sin) avulla.
- päivitetään kamerataso (planeX ja planeY), joka vaikuttaa näkymään pelimaailmassa.
Lopuksi hiiren sijainti nollataan, jotta pelaajan ohjaaminen on sujuvaa.
Main.c
Lisää lopuksi main.c-lähdekooditiedoston main-funktioon kutsut funktioille MovePlayer- ja RotatePlayer.
Nyt kun pelin kääntää ja suorittaa, pelimaailmassa voi liikkua ja kääntyä, eikä seinistäkään pääse läpi. Täydestä menee, vaikka ruutuun piirretään vain eripituisia pystyviivoja. Pienestäpä on kolmiulotteinen illuusio kiinni!
Kommentit
Lähetä kommentti