2D-Grafiikan alkeet Allegroa käyttäen
Tässä varsin helppotajuisessa ja yksinkertaisessa tutoriaalissa käydään läpi 2D-ohjelmointiin liittyviä peruskäsitteitä käyttäen apuna Allegro-peliohjelmointikirjastoa. Aiheina pääasiassa viivoja ja muita muotoja.
7.11.2001 julkaistun artikkelin on kirjoittanut jalaine.
Jos olet jo lukenut edellisen tutoriaalini, Helposti C-ohjelmoijaksi, tiedät suurin piirtein mitä alkutasoa tällä kurssilla edellytän. En kovin paljoa, perustietämys C-kielestä riittää. Et tarvitse Windows- tai muita erityisohjelmointitaitoja. Kääntäjänä on edelleen DJGPP, jonka kanssa käytetään Allegro-peliohjelmointikirjastoa. Tavoitteena on, että tämän kurssin lopussa osaat piirtää palloja, viivoja, pikseleitä ja muita hauskoja kuvioita. Grafiikkakurssi kakkosessa siirrytään sitten monimutkaisempiin systeemeihin, sukelletaan spriteihin ja tehdään jotain animaatioitakin. Mutta asia kerrallaan.
Mikä Allegro? #
Allegrosta en muistaakseni siinä C-kurssissa vielä mitään ehtinyt kertoa. Nyt siis lyhyesti jotain Allegron historiasta ja nykypäivästä sekä siitä miksi sitä kannattaa käyttää.
Allegro on alunperin Shawn Hargreavesin, mutta nykyään suuremman ryhmän kehittämä peliohjelmointikirjasto. Se sisältää todella monia grafiikkafunktioita, äänifunktioita, ohjainlaitteiden hallintafunktioita ja vaikka mitä. Tällä hetkellä siitä on olemassa DOS-versio, ja melkein valmis Windows-versio. Myös X-Windows-versio on työn alla. Näin Allegroa käyttävä peli on helppo siirtää ympäristöstä toiseen. Ja Allegro on ilmainen, samoin kuin DJGPP, vapaaehtoistyönä toteutettu.
Allegron asentamisesta on kerrottu kaikki tarvittava noissa asennusohjeissa, joita löytyy DOS-ohjelmointialueelta. Ja Allegron käytöstä kerrotaan seuraavassa. Erittäin hyvä Allegro-tietolähde on Allegron helppi, jota kannattaa ahkerasti selata.
Kun alat tekemään peliä, joka käyttää Allegroa tai jotain sen osaa, ensimmäinen tehtävä on liittää mukaan otsikkotiedosto allegro.h. Se tapahtuu seuraavasti:
#include <allegro.h>
Tuo siis kirjoitetaan ohjelman alkuun muiden includejen jälkeen. Nyt kääntäjä tunnistaa Allegron funktiot osana ohjelmaasi eikä herjaa niistä, mutta vielä on yksi juttu joka on tehtävä. Linkitysvaiheessa tulee ongelmia, jos <tt>liballeg.a</tt> -tiedostoa ei linkitetä mukaan. Sen voi tehdä kahdella tavalla. Jos käännät ohjelmasi komentoriviltä, käytä seuraavaa lausetta:
gcc foo.c -o foo.exe -lalleg
Siinä foo.c on käännettävä tiedosto, ja -lalleg kertoo linkkerille, että liballeg.a täytyy linkittää mukaan exeen. Jos taas käytät Rhideä, on sinun kirjoitettava Options/Libraries-valikosta löytyvään ikkunaan ensimmäiseen tyhjään kohtaan alleg ja merkittävä sen vieressä oleva ruutu. Esimerkki:
[X] 0 alleg
Nyt järjestelmäsi on valmis käyttämään Allegroa! Joten eiköhän tehdä ensimmäinen Allegro-ohjelma!
Ensimmäinen Allegro-ohjelmasi #
No niin. Tässä tulee yksinkertainen ohjelma, joka ei tee paljon mitään, vain sen olennaisimman. Se tekee alustukset, vaihtaa grafiikkatilaa, odottaa, että joku painaa spacea ja sitten lopettaa. Okei, käydään se läpi rivi riviltä.
#include <stdio.h> #include <allegro.h> // allegron otsikkotiedosto
Kuten varmaan jo huomasitkin, tuossa oli se allegro.h, josta hetki sitten tuolla ylempänä puhuttiin. Ja stdio.h:n varmaan tiedätkin, tässä sitä ei olisi tarvittu, mutta laitan sen aina mukaan varmuuden vuoksi - jos vaikka tekee mieli kirjoittaa tekstiä näytölle.
int main(void)
{
Tuosta alkoi sitten pääohjelma, jossa tällä kertaa tapahtuu kaikki. Peleissä ja muissa isommissa ohjelmissa täytyy aina käyttää aliohjelmia!
// Ensin alustetaan Allegro allegro_init();
Allegro_init on sellainen funktio, joka laittaa Allegron käyttökuntoon. Ennen sitä et voi (saa) käyttää mitään muita Allegron funktioita.
// Sitten näppäimistö install_keyboard();
Install_keyboard ottaa näppäimistön Allegron hallintaan. Tarvitsemme tätä, jotta voimme kohta lukea näppistä.
// Sitten laitetaan oikea grafiikkatila set_gfx_mode(GFX_VGA,320,200,0,0);
Tämä on tärkeä rivi erityisesti tässä kurssissa, koska se on ainoa funktiokutsu tässä ohjelmassa, joka jotenkin liittyy grafiikkaan. Se on grafiikkatilan vaihto. Selitän tarkemmin... Eli ensin on tuo GFX_VGA, joka tarkoittaa, että halutaan käyttää VGA-grafiikkaa. Siihen voisi myös laittaa GFX_AUTODETECT tai GFX_VESA tai jotain muuta sellaista. Sitten on numeroita, 320,200 tarkoittaa, että haluamme resoluution 320x200. Ja lopussa olevien kahden nollan kannattaa aina olla nollia (ainakin yleensä).
// Odotellaan näppäimen paimallusta
while(!key[KEY_SPACE]){}
Tässä siis käytetään näppäimistöä. Loopataan While-silmukassa niin kauan kunnes joku painaa SPACEa (KEY_SPACE). Tästä ei tämän enempää, koska tämähän on grafiikkakurssi, ei näppäimistökurssi.
// Lopetetaan Allegro allegro_exit();
Ja jokainen Allegro-ohjelma tulee lopettaa ylläolevaan riviin allegro_exit();, joka lopettaa Allegron ja palauttaa asetukset sellaisiksi kuin ne olivat ennen allegro_init();-kutsua.
return(0); }
Ja tähän ohjelma päättyi. Se oli ensimmäinen Allegro-ohjelma. Kokeile sitä ja yritä saada se käännettyä. Kun ymmärrät miten se toimii, voit siirtyä seuraavaan lukuun!
Värit palettiin #
Valitsimme tuossa äsken grafiikkatilaksi VGA-tilan 320x200 eli kuten me ohjelmoijat sanomme, tilan 13h :), koska sitä on helpompi käyttää kuin useampivärisiä tiloja.. Nimittäin tässä 13h-tilassa käytettävissämme on vain 256 väriä. Se on kuitenkin enemmän kuin tarpeeksi, ainakin tässä vaiheessa.
Värit on tallennettu palettiin, jossa on nuo 256 väriä tallennettuna. Näitä värejä voit itse muuttaa, valita ne jotka sopivat tarkoitukseesi parhaiten.
Paletin värit on tallennettu RGB-structeihin, jotka ovat muotoa:
typedef struct RGB
{
unsigned char r, g, b;
} RGB;
Siinä r tarkoittaa värin punaista komponenttia, g vihreää ja b sinistä niinkuin ne on PC:llä tapana esittää. Monet piirto-ohjelmat kuten Paint Shop Pro 5 osaavat ilmaista värit suoraan tässä muodossa. Kokeilemalla nuo värit kuitenkin selviävät. Tässä kuitenkin muutama esimerkkiväri, jotka voit laittaa esimerkiksi ohjelmasi alkuun ja käyttää niitä myöhemmin värien asettamiseen.
RGB black = { 0, 0, 0 };
RGB white = { 63, 63, 63 };
RGB green = { 0, 63, 0 };
RGB grey = { 32, 32, 32 };
RGB red = { 63, 0, 0 };
Ja PALETTE-tyyppi on 256 RGB-structin taulukko.. Sieltä löytyy siis nuo 256 väriä ylläolevan näköisessä muodossa.
Ennen kuin aletaan piirtämään niin laitetaan muutama väri palettiin. Se tapahtuu allegron funktiolla void set_color(int index, RGB *p);. Siis index on sen värin numero, jota haluamme muuttaa, ja *p osoitin sellaiseen RGB-struktiin, jonka haluamme väriksi sijoittaa. Eiköhän olisi taas esimerkin aika..
// Esimerkki 2: Ohjelma, joka muuttelee värejä..
#include <allegro.h>
#include <stdio.h>
int main(void)
{
RGB vihrea = { 0, 63, 0 };
allegro_init();
install_keyboard();
set_gfx_mode(GFX_VGA,320,200,0,0);
set_color(1,&vihrea);
while(!key[KEY_ESC]){}
allegro_exit();
return(0);
}
Tämä oli muuten aivan samanlainen ohjelma kuin tuo edellinenkin, mutta siinä on yksi uusi funktiokutsu, tuo set_color, josta äsken puhuimmekin.. Se muuttaa tässä siis paletin värin numero 1 vihreäksi..
Tehtävä #
Tutki tuota set_color-funktiota! Muuta yllä oleva ohjelma sellaiseksi, että se muuttaa väriä 0, jolloin taustan väri muuttuu. Kokeile erilaisia värejä! Tutki!
Ja pisteitä ruudulle #
Äsken muutimme palettia, mutta emme vielä piirtäneet mitään. Niinhän taiteilijatkin tekevät; ensin laitetaan palettiin värit ja sitten vasta aletaan maalaamaan. Katso hetken ajan alla olevaa kuvaa, joka esittää tietokoneen näytön koordinaatistoa.
0,0--------------->n,0 | | | V 0,n
Siis, Vasemmassa ylänurkassa on piste 0,0 ja x-koordinaatti kasvaa oikealle mennessä. Esimerkiksi resoluutiolla 320x200 oikean ylänurkan koordinaatit ovat 320,0. Vastaavasti y kasvaa alaspäin mentäessä, joten oikea alanurkka on tuolla samalla resoluutiolla 320,200.
Nyt, kun tiedät miten tietokone käsittää koordinaatiston, voitkin piirtää pisteen vaikkapa keskelle ruutua! Mitkä olisivat silloin x- ja y-koordinaatit? (miettimistauko) Nehän olisivat tietystikin 160,100. Jotta voisit piirtää tuon pisteen, tarvitset pisteenpiirtofunktion. Se on nimeltään putpixel. Huom! Pisteitä sanotaan tietokonekielessä pikseleiksi.
putpixel toimii niin, että ensin annetaan pinta, jolle halutaan piirtää pikseli, tässä tapauksessa se saa olla screen, joka tarkoittaa näyttöä, sitten koordinaatit (x ja y) ja lopuksi väri (paletin arvo väliltä 0-255). Esimerkki tulee tässä:
putpixel(screen,0,0,1);
Tuo siis piirtää vasempaan ylänurkkaan pikselin, joka on väriä numero 1. Jos liittäisimme tämän tuohon edelliseen ohjelmaamme, olisi väri vihreä. Jos emme ole väriä etukäteen asettaneet, se voi olla ihan mikä vain.
Nyt voitkin kokeilla tehdä ohjelman, joka piirtää pikseleitä, vaikkapa satunnaisesti arvottuun paikkaan. Jaa, mutta tuohan olisikin hyvä tehtävä ennen kuin aletaan piirtämään viivoja!
Toinen tehtävä #
Tee ohjelma, joka piirtää pisteen näytöltä arvottuun paikkaan! Satunnaisluvuista puhuimme C-kurssissa, katso sieltä, jos et muista.
Pisteistä eteenpäin #
No niin. Pisteet ovat pitemmän päälle aika tylsiä, mutta on tärkeää muistaa, että kaikki mitä tästä eteenpäin teemme perustuu niihin, huomaamme me sitä tai emme. Kuitenkaan ei meidän onneksi tarvitse kuvia piirtää piste kerrallaan vaan voimme käyttää apufunktioita. Tässä kurssissa käsittelemme seuraavat piirtofunktiot:
- putpixel - tämä käytiinkin jo läpi
- line - piirtää viivan
- triangle - kolmio
- rect ja rectfill - nelikulmio
- circle ja circlefill - ympyrä
- floodfill - täyttö
Ja eiköhän käydä heti töihin. Siispä viivanpiirtoon! Viivan piirtämiseen on Allegrossa ainakin 3 erilaista funktiota, joista kaikkein kehittynein on line. Tästä eteenpäin esittelen funktiot siinä muodossa kuin ne esitellään yleensä ohjelmointidokumenteissa, jotta totut lukemaan sellaistakin tekstiä. Siis tässä tulee line-funktio "oikeassa" muodossa esitettynä:
void line(BITMAP *bmp, int x1, int y1, int x2, int y2, int color);
Siitä näemme seuraavat asiat: funktio ei palauta mitään arvoa (alussa void), sen nimi on line, se ottaa syötteikseen osoittimen pintaan, johon piirretään, tässä vaiheessa se on screen, joka siis tarkoittaa näyttöä, int-tyyppiset alku- ja loppukoordinaatit sekä värin numeron, joka on välillä 0-255. Kannattaa opetella tulkitsemaan näitä funktioiden prototyyppejä, koska niiden avulla on aika helppoa ymmärtää uusia funktioitan, esimerkiksi niitä allegron funktioita, joita en tässä kurssissa esittele.
Annan yhden piirtoesimerkin, jonka voit liittää alussa tekemäämme värinvaihto-ohjelmaan.
line(screen, 5, 5, 56, 100, 1);
Tehdään laatikko #
Kokeilepa tehdä funktio, joka piirtää laatikon line-käskyllä. Funktio ottaa syötteenä ylänurkan ja alanurkan koordinaatit ja laatikon värin.
No niin, nyt kun olet tehnyt oman laatikkofunktion, voin kertoa, että Alegrosta löytyy sellainen ihan omasta takaakin:
void rect(BITMAP *bmp, int x1, int y1, int x2, int y2, int color);
Siis, tuo piirtää laatikon pinnalle *bmp (screen) alkaen pisteestä x1,y1 (vasen ylänurkka) ja päättyen pisteeseen x2,y2 (oikea alanurkka). Esimerkki tulee tässä:
rect(screen,3,6,100,107,3);
Tuo siis piirtää laatikon, joka alkaa pisteestä 3,6 ja loppuu pisteeseen 100,107. Väri on 3.
Rectistä on vielä toinen muunnos, rectfill, joka tekee täytetyn laatikon. Sitä käytetään samalla tavalla kuin rect. Se vain toimii hieman eri tavalla. Esimerkki:
rectfill(screen,10,10,50,50,4);
Tuo siis piirsi laatikon pisteestä 10,10 pisteeseen 50,50 ja väritti sen värillä 4.
Ja sitten tehdäänkin jo ensimmäinen kuva #
Teepä seuraavaksi ohjelma, joka piirtää punaisen mökin käyttäen laatikko- ja viivafunktioita. Tee myös pari ikkunaa ja ovi. Voit myös keksiä muuta hienoa, vaikkapa ikkunalaudan.
Ja mökin pihallahan voisi olla hyvä olla puita, näyttää kodikkaammalta. Mutta puut eivät ole neliön muotoisia, ainakaan yleensä... Runko ehkä, mutta latvan tulee ehkä olla pyöreä tai kolmion mallinen, jos se on kuusi.
Siispä nyt tarvitaan ympyrän- ja kolmionpiirtotyökalut. Ympyrä piirretään funktiolla:
void circle(BITMAP *bmp, int x, int y, int radius, int color);
Muuten tuon funktion kutsuminen tapahtuu samalla tavalla kuin edellä esiteltyjen, paitsi että koordinaattien (keskipiste) jälkeen on ympytän säde kokonaislukuna. Toinen ympyräfunktio circlefill ottaa samat syötteet kuin circle, mutta piirtää täytetyn ympyrän.
Kolmio piirretään funktiolla:
void triangle(BITMAP *bmp, int x1, y1, x2, y2, x3, y3, int color);
Tuo piirtää täytetyn kolmion, jonka kärkipisteet ovat nuo x1,y1 jne.
Ja jos haluat tehdä puihin vinoja oksia, täytyy ne tehdä viivoilla. Tällöin ne kuitenkin jäävät pelkiksi ääriviivoiksi. Siispä tarvitaan vielä väritysfunktio. Se on seuraavanlainen:
void floodfill(BITMAP *bmp, int x, int y, int color);
Floodfill toimii siten, että annetusta pisteestä x,y niinkauan kunnes osuu seinään, se värittää annetulla värillä. Esimerkiksi, jos haluaisin värittää kuvion, jonka sisällä on piste 10,5 värillä 5, kirjoittaisin seuraavasti:
floodfill(screen,10,5,5);
Nyt noilla tiedoilla voit tehdä puihin niitä vinojakin oksia.
Kirjaimista muodostuu sanoja #
Tämä kurssi lähenee loppuaan: osaat jo lähes kaikki grafiikkakäskyt, joita tässä kurssissa käymme läpi, ainoastaan tekstin kirjoittaminen puuttuu.
Eiköhän siis katsota nyt tuota kirjoittamista! Se tapahtuu Allegron funktioilla:
void text_mode(int mode); void textout(BITMAP *bmp, FONT *f, unsigned char *s, int x, int y, int color);
Siis ensin (ohjelman alussa) määritetään tekstin kirjoitustapa funktiolla text_mode. Jos haluat läpinäkyvän taustan, laita parametriksi jokin negatiivinen luku. Jos taas haluat, että tausta on jotain tiettyä väriä, anna sen värin numero. Siispä kaksi esimerkkiä, yksi kummastakin tapauksesta:
text_mode(-1);
Kirjoitetaan läpinäkyvällä taustalla. Tai:
text_mode(15);
Tekstin taustaväri on nyt sitten väri numero 15 paletista. Mutta joo.. Kun tekstinpiirtotapa on valittu, on aika kirjoittaa. Kirjoittaminen tapahtuu funktiolla textout. Otetaan tähän ensin esimerkki ja sitten sen selitys:
textout(screen,font,"Jee, nyt tulee tekstiä!!",10,150,10);
Siinä siis kirjoitetaan näytölle (screen) Allegron perusfontilla (font) - fontteja voit myös itse tehdä, mutta siitä ehkä jotain joskus myöhemmin. Näytölle siis tulee pisteestä 10,150 oikealle värillä 10 teksti: "Jee, nyt tulee tekstiä!!".
Kokeile liittää tekstiä vaikka siihen talo-ohjelmaasi.. Esimerkiksi oveen voisi kirjoittaa jonkin nimen.
Yksinkertainen piirto-ohjelma #
Enempää piirtofunktioita en tässä kurssissa anna, mutta tehdäänpä pieni piirrustusohjelma, sellainen, jossa nuolilla liikuttamalla pystyy piirtämään. Tehdään ensin yhdessä pohja ja sitten päästän sinut kehittelemään ohjelmaa mieleiseksesi.
Mietitäänpä hetki ohjelman rakennetta:
- Allegron alustukset
- Grafiikkatilan vaihto
- Näppäimistön luku
- Pisteen piirtäminen
Noista 1, 2 ja 4 ovat sellaisia, jotka jo osaat tehdä, joten niihin ei tässä sen kummemmin puututa. Näppäimistöstä kuitenkin pari sanaa.
Idea on siis se, että aina kun painetaan nuolinäppäintä, muutetaan x:n ja y:n arvoja ja piirretään uusi pikseli niiden näyttämään kohtaan. Ja näppäimistön lukeminenhan onnistuu helposti Allegron funktioilla:
if(key[KEY_LEFT]){
x--; // Liikutetaan vasemmalle
putpixel(screen,x,y,vari);
}
if(key[KEY_RIGHT]){
x++; // Liikutetaan oikealle
putpixel(screen,x,y,vari);
}
if(key[KEY_UP]){
y--; // Liikutetaan ylös
putpixel(screen,x,y,vari);
}
if(key[KEY_DOWN]){
x--; // Liikutetaan alas
putpixel(screen,x,y,vari);
}
Oikeastaan olisin voinut antaa sinun kehitellä tuonkin systeemin, mutta nyt se on tuossa, joten olkoon. Joka tapauksessa näppäimistöä voi Allegron avulla lukea tuolla tavoin. Kerron tästä muissa kursseissa enemmän, nyt riittää kun osaat käyttää nuolinäppäimiä.
Ja loppu jätetään sitten tehtäväksi. Annan vihjettä sen verran, että muistathan laittaa muuttujille alkuarvot, eli määrittää sen paikan mistä piirtäminen aloitetaan.
Loppukaneetti #
Nyt on tämä kurssi loppu ja toivottavasti olet oppinut tässä käsitellyt asiat ja ennenmuuta saanut lisää varmuutta ohjelmointiisi. Tähän ei kuitenkaan missään tapauksessa kannata lopettaa, olemme vasta pääsemässä alkuun 8^). Nimittäin nyt on aika siirtyä kohti Grafiikkakurssi kakkosta ja hienompia grafiikkajuttuja! Siispä, onnittelut kurssin suorittamisesta ja onnea jatkossakin!
