Svgalib |
|||||||
Üdvözlök mindenkit a linuxos programozás
világában. Ez a cikk(sorozat) nem elsősorban a profikhoz szól, hanem
azokhoz, akik most kapizsgálják a programozás örömeit(és szenvedéseit). A
kezdőket, amilyen és is vagyok még, sem az általános dolgokkal akarom
traktálni, hanem inkább érdekességképpen minden hónapban egy-egy C
könyvtárral ismerkednénk meg, egy-egy játék programon belül. Ebben a hónapban a fogás az svgalib lenne. Ez egy alacsony szintű grafikus könyvtár, amely az X-től
teljesen függetlenül fut és sokkal gyorsabb nála. Elsősorban a
parancsait szeretném bemutatni, valamint azt, hogy hogyan lehet egy szép
grafikus programot csinálni egy mintaprogramon keresztül. Az svgalib beállítását most
helyhiány miatt nem taglalom, de a linkek között találhatnak egy nagyon jó
oldalt, ami pontosan ezzel foglalkozik. Az svgalib nagyon sok
felbontást támogat, számszerint 49-cet (a teljes lista az svgalib man oldalán
megtalálható). Mi a 10-zes felbontást használjuk, ez 640*480 képpontot
jelent, melyek mindegyike 256 színben pompázik (amiből mondjuk én csak
16-t használtam). A parancsoknak általában mindig van manual oldala, csak be
kell írni egy konzolba például a man vga_init parancsot
és rögtön megjelenik a hozzá tartozó szépen tördelt súgó. Amit leírok
valahol, az rendszerint minden helyen érvényes lesz, pl. az egér esemény
kezelését ugyanúgy oldottam meg mindenhol. Nos akkor
ennyi kertelés után bele is fogok a program leírásába. Először elindítom
a grafikus módot, aminek megértéséhez javaslom, hogy nézzék meg a
forráskódot.
Az egér
beállítása: vga_setmousesupport(1) (No Comment, ezt így kell:). gl_setfont(8, 8,
gl_font8x8); - az általános betűtípust és méretet
ezzel állítjuk be, az első kettő paraméter a méret, a harmadik egy
mutató egy betűkészletre (akit érdekelnek a betűtípusok és tud
angolul, menjen el erre az oldalra: http://www.svgalib.org/jay/font_tutorial/fonts.html gl_setwritemode(FONT_COMPRESSED
+ WRITEMODE_MASKED); - itt állítjuk be a
betű kinézetét, a FONT_COMPRESSED azt jelenti,
hogy egy pixel egy bájtnyi információ lesz. Helyette írhattunk volna FONT_EXPANDED -t is, de az lassabb. A WRITEMODE_MASKED azt jelenti, hogy írásnál a szöveg
háttérszíne semmi sem lesz, vagyis megmarad az eredti (így pl. írhatunk egy
kép elé stb.).Az ellentétje a WRITEMOD_OWERWRITE. gl_setfontcolors(0,
vga_white()); - itt lehet
beállítani a szöveg színét, az első a háttérszín, a második a betűk
színe. A gl_write(0, 0, "hello
world!") függvénnyel írhatunk a
képernyőre, az első kettő paraméter a szöveg elejének az X, Y
a koordinátái. Ennyi volt
az elmélet egy rövid ideig, most jöjjön a gyakorlat. Először kiírtam a
menüt és kirajzoltam az egeret. Aztán egy ciklusban történik a
tulajdonképpeni futás. Itt két dolgot vizsgálok: azt, hogy az egér vagy a
billentyűzet küldött-e eseményt.
Billentyű lenyomáskor a program bezárja az egeret (mouse_close()), a grafikus módot
(vga_setmode(TEXT)), és kilép
a programból. Ha az egeret
megmozdítottuk (vagy kattintottunk) és ha valamilyen szöveg fölött van az
egérkurzor, akkor az egeret törlöm, a szöveget kiírom pirossal, és az egeret
újra kirajzolom. Ezt mutatja be ez a kódrészlet: gl_circle(X, Y, 3, 0); gl_setpixel(X, Y, 0); - még a
régi helyen törlöm az egeret. Ez azért kell, hogyne maradjon ott a helye. X = mouse_getx(); Y =
mouse_gety(); - itt adom
meg az új helyet if ( (257 < mouse_getx())
&& (343 > mouse_getx()) && (160 > mouse_gety())
&& (147 < mouse_gety())) - megnézem,
hogy a menüpont felett van-e az egér gl_setfontcolors(0, 4); - ha igen, akkor a
színt pirosra változtatom gl_write(260, 150, "New
Object"); - itt írom
ki a szöveget gl_setfontcolors(0,
vga_white()); - és visszaállítom a színt fehérre. Ha
kattintottunk, akkor ugyanez megtörténik, de utána megnézem, melyik menü
felett volt az egér és az annak megfelelő függvényt hívom meg. A Making függvényben alkalmazott technika kicsit
eltér az előbbiektől. Az egér és a billentyűzet kezelése
ugyanúgy történik, de itt már nem szövegek, hanem gombok (Button) vannak.
Kezdjük az elején. Először a gombokat állítom be (virtuális
függvényekkel szórakoztam, mert az elején mást is akartam, de aztán az
kimaradt, tudom, hogy sokkal egyszerűben is meg lehetne ezt csinálni, de
már nincs kedvem újraírni a forráskódot) és a SetValues függvényel adom meg az elhelyezkedését, a színét és a
szöveget, ami rajta van. A ciklus előtt még 2 dolgot csinálok.
Először a gombokat rajzolom ki és aztán egy nagyon érdekes dolgot
művelek. Erről még nem beszéltem, de úgy adom meg a
"rajzot" amit csinálok, hogy van egy 192 négyzetből álló
négyzetrács. Mindegyik négyzet(ecske) egy-egy objektum, amik egyenlőre
csak a szint tárolják. Ilyen objektumokból áll a Parts[192] tömb. Egy ilyen tömb tartalmazza a rajzot. Az Objects
fájl 3*192 számot tartalmaz. Nem azért írtam így mert nem tudok számolni,
hanem, hogy jelezzem: 3 rajzot tartalmaz (a számok a rajz színei). A Making paramétere azt adja meg, hogy hányadik
rajzot töltse be az Objects fájlból. Ha 0 a paraméter (vagyis az
alapértelmezett lesz), akkor értelemszerűen új rajzot szeretnénk, így
feketével töltöm fel a négyzeteket. Ezek után akkor jöjjön megint egy kis gyakorlat.
Ha az egér egy Button felett van, akkor az alatta/felette lévőt és a mellette
lévőket is kirajzolom, hogy ne maradjon nyom azokon a gombokon (a kódban
bejelöltem a nyomtisztítást, ha meg szeretnék nézni, hogy milyen nélküle,
egyszerűen töröljék ki). /*nyomtisztítás*/ for (i=0; i <
16; i++) if ((objects[i]->getX()
< X) && (objects[i]->getX2()
> X) &&
(objects[i]->getY() < Y) &&
(objects[i]->getY2() > Y)) { DrawButton( objects[i]->getX(),
objects[i]->getY(), objects[i]->getX2(),
objects[i]->getY2(), objects[i]->getW(),
objects[i]->getH(), objects[i]->getTextX(),
objects[i]->getTextY(), objects[i]->getColor(),
objects[i]->getText() ); if (i!=0) DrawButton( objects[i-1]->getX(),
objects[i-1]->getY(), objects[i-1]->getX2(),
objects[i-1]->getY2(), objects[i-1]->getW(),
objects[i-1]->getH(), objects[i-1]->getTextX(),objects[i-1]->getTextY(),
objects[i-1]->getColor(),
objects[i-1]->getText() ); if (i!=15) DrawButton(objects[i+1]->getX(),
objects[i+1]->getY(), objects[i+1]->getX2(),
objects[i+1]->getY2(), objects[i+1]->getW(),
objects[i+1]->getH(), objects[i+1]->getTextX(), objects[i+1]->getTextY(),
objects[i+1]->getColor(),
objects[i+1]->getText() ); if (i<8) DrawButton( objects[i+8]->getX(),
objects[i+8]->getY(), objects[i+8]->getX2(),
objects[i+8]->getY2(), objects[i+8]->getW(),
objects[i+8]->getH(), objects[i+8]->getTextX(),
objects[i+8]->getTextY(), objects[i+8]->getColor(),
objects[i+8]->getText() ); if (i>=8) DrawButton(objects[i-8]->getX(),
objects[i-8]->getY(), objects[i-8]->getX2(),
objects[i-8]->getY2(), objects[i-8]->getW(),
objects[i-8]->getH(), objects[i-8]->getTextX(),
objects[i-8]->getTextY(), objects[i-8]->getColor(),
objects[i-8]->getText() ); break; } A ciklus
azért kell, mert mindegyik gombot megnézem, hogy melyik fölött van. A gomb
bal felső sarkának a koordinátái: X,Y. Ezeket az
object getX() és a getY() függvénnyel kapom meg. Ezeknél az egér X és Y
koordinátáinak nagyobbnak kell lenni. (Az svgalibben a (0,0)
koordináta a képernyő bal felső sarka.) Értelemszerűen a
Button jobb alsó koordinátáinál (X2 és Y2 ezeket a getX2()-vel és a getY2()-vel kapom
meg) kisebbnek kell lennie az egér koordinátáinak, ha az felette van. Ha i!=0 igaz akkor rajzoljuk ki az előző
Buttont (ügye az elsőnél nincsen "előző"), ha pedig
az utolsó gomb felett van az egér, nincsen "következő". Az
utolsó kettő feltétel dönti el, hogy az adott gomb a felső vagy az
alsó sorban van, és ennek megfelelően a fölötte vagy az alatta
lévőt frissíti. A gombokat
a DrawButton függvény
segítségével rajzolom ki. Amúgy ez is egy külön történet. Először nem
vezettem be a DrawButton és ButtonPress függvényeket és mindig kiírtam az egész
kódot. Így az egész forrás teljesen átláthatatlanná vált, legfőképpen az
utasítás blokkok miatt, mivel nem tudtam melyik hova tartozik és mit hova
kell írni (szerk megj: modularizálunk,
modularizálnunk? J Nagyon helyes...). Kb 5-ször kezdtem
újra íni a forrást egy régebbi verziótól és utána vezettem be ezeket, mert
már annyira ideges voltam. A ButtonPress arra való, ha kattintottunk az egyik
gombon, akkor tényleg azt a hatást keltse, mintha benyomódna. Ezt a hatást
egyszerűen úgy érem el, hogy a gombon nem a felső és a baloldali
vonal lesz fehér hanem az alsó és a jobboldali. Szinte mindenhol így van, a KDE-ben
is (azért ott szebben), nézzék meg. Akkor most nézzük a kódot: if (mouse_getbutton()) for (i=0; i <
16; i++) if ((objects[i]->getX() < X) &&
(objects[i]->getX2() > X) &&
(objects[i]->getY() < Y) &&
(objects[i]->getY2() > Y)) { ButtonPress( objects[i]->getX(),
objects[i]->getY(), objects[i]->getX2(),
objects[i]->getY2(), objects[i]->getTextX(),
objects[i]->getTextY(), objects[i]->getColor(),
objects[i]->getText() ); Color = i; break; } Ez
szerintem az előzőekhez képest nagyon egyszerű, viszont egy
dolog még hátravan. A Color = i; sor. Pont
úgy van a sorrend, hogy a gombok indexe egyenlő az adott színnel. Az
első gomb fekete és a sorszáma 0 stb. Ezzel állítom be a rajzoláshoz a
színt. Ezzel el is érkeztünk a tényleges munkához. Ha az egér a négyzet fölé
megy, a négyzetet kirajzolom az eredeti színekkel. Ha a négyzeten belül
kattintok, akkor az adott négyzetecske színe a Color változó értéke lesz, amit előzőleg beállítottunk. Ezután, ha
az egeret megmozgatjuk, már láthatjuk is az új szint, mivel, csak a mouse_update()-nél frissíti a négyzetet. if (mouse_getbutton()) { for (i=0; i <
16; i++) if ...; for (i=0; i<16; i++) for (i2=0; i2<12; i2++) if ( (280+i*5 < X) && (288+i2*5 < Y) && (280+(i+1)*5
> X) && (288+(i2+1)*5 > Y) ) Parts[i*12+i2].Color =
Color; } Billentyűnyomásra
megjelenik egy új ablak, egy elmés felirattal. Itt lehet elmenteni
művészi rajzunkat, 3 helyre. A wall[], floor[] és a player[] ugyanolyanok, mint a Parts[]. Amikor az egyik gombra rákattintottunk, a fájlból
kiveszem a régi rajzokat. Ezekből 2 ugyanaz marad, de az egyiket ki
fogjuk cserélni. file.open("objects", ios::in | ios::out ); //megnyitom a
fájlt POO wall[192], floor[192], player[192]; int i3 = i; //kiveszem a fajból a rajzokat: for (i=0; i<16;
i++) for (i2=0; i2<12; i2++) file >>
wall[i*12+i2].Color; for (i=0; i<16;
i++) for (i2=0; i2<12; i2++) file >>
floor[i*12+i2].Color; for (i=0; i<16;
i++) for (i2=0; i2<12; i2++) file >> player[i*12+i2].Color; file.close(); file.open("objects",
ios::out | ios::trunc); Itt a végén bezárom, és újra megnyitom a
fájlt. Kérdezhetik az Olvasók, hogy miért? Figyelmesek észrevehették, hogy
most másodszorra a paraméter nem ios::in | ios::out volt hanem ios::in | ios::trunc. Ez azt jelenti, hogy az egész fájlt töröljük
és egy újat hozunk létre. Mivel az előbb már kiolvastuk a fájl
tartalmát, így ez már nem gond. Csak ugyanabban a sorrendben kell
visszarakni, és még azt is amit mi rajzoltunk. Nézzük a kódot. switch (i3) { case 0:{ for (i=0; i<16; i++) for (i2=0; i2<12; i2++) file <<
Parts[i*12+i2].Color << '\n'; // ha az első Buttonra kattintunk, akkor
a rajzot menti el előszőr for (i=0; i<16; i++) for (i2=0; i2<12; i2++) file << floor[i*12+i2].Color
<< '\n'; for (i=0; i<16; i++) for (i2=0; i2<12; i2++) file <<
player[i*12+i2].Color << '\n'; break;} case 1:{ for (i=0; i<16; i++) for (i2=0; i2<12; i2++) file <<
wall[i*12+i2].Color << '\n'; for (i=0; i<16; i++) for (i2=0; i2<12; i2++) file <<
Parts[i*12+i2].Color << '\n'; //a másodiknál a
másodikként menti el for (i=0; i<16; i++) for (i2=0; i2<12; i2++) file <<
player[i*12+i2].Color << '\n'; break;} case 2:{ for (i=0; i<16; i++) for (i2=0; i2<12; i2++) file <<
wall[i*12+i2].Color << '\n'; for (i=0; i<16; i++) for (i2=0; i2<12; i2++) file <<
floor[i*12+i2].Color << '\n'; for (i=0; i<16; i++) for (i2=0; i2<12; i2++) file <<
Parts[i*12+i2].Color << '\n'; //itt meg a
player lesz a mű:) break;} } return ; Ha jól
sejtem itt a vége a Making függvénynek. A
rajzunk kész és el is mentettük, úgyhogy vége a munkának. Az Open() ezek után már gyerekjáték, hiszen ugyanezeket
a módszereket használom. Megjelenítek 3 rajzot, és fölöttük 3 Buttont, aztán
az egérrel választok. A számot, hogy melyik rajzot akarjuk betölteni, pedig
visszaadom a függvény visszatérési értékeként. A kódot GPL
alatt adtam ki. A GPL szerződést, ha minden jól megy, a programmal
együtt megkapják az olvasók. A hozzá tartozó
játék már készül(get). A honlapomon megnézhetik a legfrissebb információt. |
|||||||
Varga
Viktor |