Hogy ne hupilila forgó kockákkal álmodjatok, ezért most egy 2D játékot fogunk összehozni. (Ha 3D-ről akarsz olvasni, akkor ugorj a szövegben egy jó nagyot :-)
A játék célja: Egy vírussal kell menekülni a vírusirtó program elől. Ha a program elkap, megesz. 10 életünk van. A pálya kockákra (stílusosan szerktorokra) lesz osztva. Egy szektoron 2* kell átmenni, hogy hibás szektor legyen. Ezeken mi nem tudunk átmenni, a vírusirtó igen, de az ki is javítja őket. A cél 10, a pályán véletlenszerűen elrejtett bináris kód összegyűjtése. Csak ne egyen meg a vírusirtó flopi.
Ezzel meg is vagyunk. A programunk médiát fog használni, először ezeket nézzük:
- A vírus képe
- A vírusirtó képe
- Szektorok (jó/hibás/használhatatlan)
- Bináris számsorok
- Egynéhány hang
- meg még fejlesztés közben eszünkbe jut, hogy mi kell :-)
Ezeket nem lesz nehéz előteremteni. Ja, és hogy mi az, hogy média? A program által fölhasznált kép/hang/zene/3D objektum/videó fájlok összefoglaló neve. Ezen essünk túl gyorsan, vegyük elő a Paintot (lehet mást is, de ez most tökéletesen megfelel a célnak) és szerkesszünk képet. Ügyeljünk arra, hogy a DB a fekete színt átlátszónak veszi, ha feketét akarunk, az mondjuk rgb(5,5,5) legyen! Ha ezzel megvagyunk, gyűjtsünk hangokat. Remélem találsz a merevlemezeden néhány, a célnak megfelelő hangfájlt. Ha nem, ott az internet, a DB CD. Meg én is mellékelem a programhoz a médiát. Gyönyörű, paintban kézzel rajzolt médiát :-)
Na kezdjük el írogatni! Első feladat a pálya megjelenítése:
set display mode 800,600,32
load image "zöld.bmp",1
load image "sárga.bmp",2
load image "piros.bmp",3
for i=1 to 15
for j=1 to 20
paste image 1,50+(j*30),50+(i*30)
next j
next i
do
loop
Először beállítjuk a felbontást (szélesség, magasság, színmélység).
Azután betöltjük a képeket. Először a memóriába kerülnek, utána majd mi eldöntjük, hogy mi lesz velük. Mindenesetre most kapnak egy sorszámot. Ezután beillesztjük őket a képernyőre. (A két egymásba ágazott ciklus határozza meg a koordinátákat.)
A Paste Image parancs kirajzolja a képet a képernyőre, majd magára hagyja: rajzolhatunk rá, törölhetjük. (Ez azért fontos, mert a sprite-ok állandóan frissülnek, ez meg nem) Használata:
paste image képszám,x koord,y koord
Futtassuk remekművünket. Lassú? Az. Az FPS-ekért folytatott harc itt is jelen van. Írogassunk synceket! (Lásd lentebb)
A pálya megrajzolásával már készen vagyunk. Most jöhet a vírusunk és az ellenséges flopi képe. Vírusból kettőt is rajzoltam: animálva lesz.
set display mode 800,600,32 : sync on
load image "zöld.bmp",1
load image "sárga.bmp",2
load image "piros.bmp",3
load image "vírus1.bmp",4
load image "vírus2.bmp",5
load image "flopi.bmp",6
for i=1 to 15
for j=1 to 20
paste image 1,50+(j*30),50+(i*30)
next j
next i
sprite 1,80,80,4
sprite 2,650,500,6
do
sync
loop
A spriteok állandóan frissülnek, a 2D játékok mozgó objektumainak lettek kitalálva. (A spritehoz hasonló a bob parancs (bitmap object). Bobok a DB első verzióiban voltak, majd leváltották őket a spriteok. A súgó tartalomjegyzékéből kiszedték a BOB parancsokat, de a htm linkeletlenül benne van a súgóban.) A sprite parancs a spriteok elhelyezésére szolgál. A sprite-okkal sok dolgot lehet végezni, elégedjünk meg most annyival, hogy 2D objektumok.
Most meg is vagyunk a pálya kirajzolásával. Egész bíztató a kezdet. Mit is csináljunk? Írjuk meg a vírusunk mozgását! A program vége így fog alakulni:
...
load blabla
for i=1 to 15
for j=1 to 20
paste image 1,50+(j*30),50+(i*30)
next j
next i
sprite 2,650,500,6
vx=1 : vy=1
do
if upkey()=1 then vy=vy-1 : if vy=0 then vy=1
if downkey()=1 then vy=vy+1 : if vy=16 then vy=15
if leftkey()=1 then vx=vx-1 : if vx=0 then vx=1
if rightkey()=1 then vx=vx+1 : if vx=21 then vx=20
sprite 1,(vx*30)+50,(vy*30)+50,4
sync
loop
Az 1-es sprite megjelenítését végző parancs átvándorolt a főciklusba. Megjelent 2 új változó: vx (vírus x) és a vy (vírus y). Ezek a vírusnak a pályabeli koordinátáját tartalmazzák kockákban, és nem képpontokban. (A legfelső kockára állítjuk be a ciklus előtt a flopit: vx=1 : vy=1) Aki nem hiszi, írjon a ciklusba egy set cursor 1,1 : print vx;" ";vy sort :-)
Ez szép és jó, csak a flopink túl gyorsan mozog. A mozgatást a számítógép órájához kötjük:
sebesseg=500
vx=1 : vy=1
ido1=timer()
do
ido2=timer()
if ido1+sebesseg<ido2
if upkey()=1 then vy=vy-1 : if vy=0 then vy=1
if downkey()=1 then vy=vy+1 : if vy=16 then vy=15
if leftkey()=1 then vx=vx-1 : if vx=0 then vx=1
if rightkey()=1 then vx=vx+1 : if vx=21 then vx=20
ido1=timer()
endif
sprite 1,(vx*30)+50,(vy*30)+50,4
sync
loop
A sebesseg változó a két billentyűlenyomás közötti késleltetés ezredmásodpercben. A módszer a következő:
Az ido1 a "régi" idő, mindig kevesebb az ido2-nél
Az ido2-t a ciklus elején mérjük.
Ha ido2 nagyobb, mint ido1+sebesség (eltelt 500 ezredmp) akkor engedélyezi a program a billentyűlenyomás érzékelését. Ha mindez megvan, újramérjük az ido1-et, hogy lehessen megint várni fél másodpercet.
Most jön az a feladat, hogy a vírus hibás (és félig hibás) szektorokat hozzon létre ott, ahol jár. Ehhez már kell egy tömb, ugyanis a hibás szektorra nem léphet, és nem ártana tudni, melyik hibás, és melyik nem.
A program elejére került egy dim adatok(20,15) sor.
mozgat=1
do
ido2=timer()
if ido1+sebesseg<ido2
if upkey()=1 then vy=vy-1 : mozgat=1 : if vy=0 then vy=1
if downkey()=1 then vy=vy+1 : mozgat=1 : if vy=16 then vy=15
if leftkey()=1 then vx=vx-1 : mozgat=1 : if vx=0 then vx=1
if rightkey()=1 then vx=vx+1 : mozgat=1 : if vx=21 then vx=20
ido1=timer()
endif
if mozgat=1
if palya(vx,vy)=1 then palya(vx,vy)=2 : paste image 3,50+(vx*30),50+(vy*30)
if palya(vx,vy)=0 then palya(vx,vy)=1 : paste image 2,50+(vx*30),50+(vy*30)
mozgat=0
endif
sprite 1,(vx*30)+50,(vy*30)+50,4
sync
loop
A színezést a következő két sor végzi:
if palya(vx,vy)=1 then palya(vx,vy)=2 : paste image 3,50+(vx*30),50+(vy*30)
if palya(vx,vy)=0 then palya(vx,vy)=1 : paste image 2,50+(vx*30),50+(vy*30)
A tömb elemeinek alapból 0 az értéke. Ha rámegy a vírus, 1 lesz -> sárga kép (a 2-es számú) beillesztése. Ha már egy az érték, akkor 2 lesz -> piros kép beillesztése.
A mozgat változó azért kell, hogy csak akkor rajzolja át a kockát a program, ha mozgatás történt. Mozgatáskor 1 lesz az értéke -> átszínezés -> lenullázódik. A ciklus előtt azért adunk neki 1-es értéket, hogy a kezdő kockát átszínezze. Most már csak annyi kell, hogy a vírusunk ne tudjon piros mezőre lépni. Ehhez úgy kell a mozgató 4 sort módosítani, hogy a mozgatást csak akkor engedélyezzék, hogyha a célterület értéke a tömbben nem 2 (azaz járható):
if upkey()=1
if palya(vx,vy-1)<>2 then vy=vy-1 : mozgat=1 : if vy=0 then vy=1
endif
if downkey()=1
if palya(vx,vy+1)<>2 then vy=vy+1 : mozgat=1 : if vy=16 then vy=15
endif
if leftkey()=1
if palya(vx-1,vy)<>2 then vx=vx-1 : mozgat=1 : if vx=0 then vx=1
endif
if rightkey()=1
if palya(vx+1,vy)<>2 then vx=vx+1 : mozgat=1 : if vx=21 then vx=20
endif
Evvel le is van tudva vírusunk mozgatása. Animáció jöhet? Egy sor az egész:
if ido1+(sebesseg/2)<ido2 then kep=4 else kep=5
(Ez a sor a if ido1+sebesseg<ido2 sor elé jön)
Valamint át kell írni a vírus sprite kirajzolását:
sprite 1,(vx*30)+50,(vy*30)+50,4
helyett
sprite 1,(vx*30)+50,(vy*30)+50,kep
Ennyi! Működése: ha az ido1 és az ido2 közötti különbség a sebesseg fele, akkor 4-es képkockát rajzol a program, ha több, akkor 5-ös képkockát. Így nem kell még 2 új időmérő változó. Minden szép és jó, jöhet a játék céljának a megírása: a 10 bináris kód elhelyezése/összegyűjtése. Először gondolkozzunk el azon, hogy lehetne meglévő tömbünkbe tuszkerálni a bináris kódok tárolásának információját? Adunk egy 3-mas értéket, ha rámegyünk, 1-gyé változik ás kirajzoljuk a kódot? Próbálkozzunk így.
(Na és itt észrevettem, hogy van egy hiba a programban - próbáljunk csak meg a pálya aljára menni! Javított mozgatás jön:)
set display mode 800,600,32 : sync on
dim palya(20,15)
load image "zöld.bmp",1
load image "sárga.bmp",2
load image "piros.bmp",3
load image "vírus1.bmp",4
load image "vírus2.bmp",5
load image "flopi.bmp",6
load image "bin.bmp",7
randomize timer()
for i=1 to 10
x=rnd(19)+1 : y=rnd(14)+1
palya(x,y)=3
sprite 10+i,0,0,0
next i
for i=1 to 15
for j=1 to 20
paste image 1,50+(j*30),50+(i*30)
next j
next i
sprite 2,650,500,6
sebesseg=250
vx=1 : vy=1
ido1=timer()
mozgat=1
do
ido2=timer()
if ido1+(sebesseg/2)<ido2 then kep=4 else kep=5
if ido1+sebesseg<ido2
if (upkey()=1) and (vy-1<>0)
if palya(vx,vy-1)<>2 then vy=vy-1 : mozgat=1
endif
if (downkey()=1) and (vy+1<>16)
if palya(vx,vy+1)<>2 then vy=vy+1 : mozgat=1
endif
if (leftkey()=1) and (vx-1<>0)
if palya(vx-1,vy)<>2 then vx=vx-1 : mozgat=1
endif
if (rightkey()=1) and (vx+1<>21)
if palya(vx+1,vy)<>2 then vx=vx+1 : mozgat=1
endif
ido1=timer()
endif
if mozgat=1
if palya(vx,vy)=1 then palya(vx,vy)=2 : paste image 3,50+(vx*30),50+(vy*30)
if palya(vx,vy)=3 then palya(vx,vy)=1 : paste image 2,50+(vx*30),50+(vy*30) : palya(0,0)=palya(0,0)+1 : sprite 10+palya(0,0),50+(vx*30),50+(vy*30),7
if palya(vx,vy)=0 then palya(vx,vy)=1 : paste image 2,50+(vx*30),50+(vy*30)
mozgat=0
endif
sprite 1,(vx*30)+50,(vy*30)+50,kep
if sprite image(20)=7 then print "Győztél!" : end
sync
loop
Íme az eddigi teljes kód. Változások:
-Betöltjük 7-es számmal a bináris kód képét.
-Véletlenszerűen létrehozunk 10 bináris kórot. Egyelőre csak a tömb jelöli őket. Legyártunk még 10 spritét. Ezek lesznek a kódok spritejai. Azért kell előre legyártani őket, mert a DB a később létrehozott spritét rajzolja fölülre. Az meg nem lenne szép, ha a vírusunkat takarná egy kód.
-A mozgatást átírtam hibátlanra
-A kódok megjelenítéséről a következő sor gondoskodik:
if palya(vx,vy)=3 then palya(vx,vy)=1 : paste image 2,50+(vx*30),50+(vy*30) : palya(0,0)=palya(0,0)+1 : sprite 10+palya(0,0),50+(vx*30),50+(vy*30),7 (talán tagolva átláthatóbb lenne?)
Ha a palya tömbben 3 a kocka értéke, akkor azt 1-re változtatja. Ezután (palya(0,0)=palya(0,0)+1) a palya(0,0) értéket megnöveli: itt tartjuk nyilván a megtalált kódok számát. Ezután a 10+palya(0,0) számú spritét helyére rakjuk, és képet is adunk neki.
-Az utolsóelőtti-előtti sor a győzelemről gondoskodik - ha megtaláltuk az utolsó kódot, a 20-as sprite képének száma is 7 lesz. Ekkor vége a játéknak. Ezt egyelőre egy szerény "Győzelem!" felirattal honoráljuk, amit majd lecserélünk - van fontosabb dolgunk is. (A sprite számát és a kép számát ne keverjük össze! Ha egy képhez egy sprite tartozik csak, nem árt, ha a két szám megegyezik.) És minden jó ha a vége jó - még meg kell írni a mesterséges (un)intelligenciát.
A szerencsétlen vírusunkat a flopi üldözni fogja, azaz mindig felé fog közeledni. Ehhez nem ártana tudni, hogy merre menjen - ugyanis 8 irányba mehet. Először is a flopinak adunk két változót - a vx-hez és vy-hoz hasonlóan ezek fx és fy nevet fogják viselni. Átírjuk a flopi spritejét kirajzoló sort is:
vx=1 : vy=1
helyett
vx=1 : vy=1 : fx=20 : fy=15
sprite 1,(vx*30)+50,(vy*30)+50,kep
után jön a következő sor:
sprite 2,(fx*30)+50,(fy*30)+50,6
Most jön a nagybetűs ellenségmozgatás. Úgy fair, ha az ellenség is csak akkor tud mozogni, amikor mi. Ezért a mozgatást végző rész után, az ido1=timer() sor elé írjuk az ellenségmozgató kódot. A flopi tudja, hol a vírus, felé indul el; azaz: az fx változót mindig a vx-hez, az fy változót mindig a vy-hoz próbáljuk igazítani. Ha az fy nagyobb, mint a vy, akkor csökkentjük, ha kisebb, akkor növeljük. Ugyanígy teszünk a vx és fx változókkal:
if vx>fx then fx=fx+1
if vx<fx then fx=fx-1
if vy>fy then fy=fy+1
if vy<fy then fy=fy-1
Ez a 4 sor lenne csak? Sajnos nem. A flopi túl kemény ellenség, nem hagy túlélési esélyt a vírusunknak. "Butítani" kell egy kicsit.
hiba=rnd(4) : hiba=hiba-2
if vx+hiba>fx then fx=fx+1
if vx+hiba<fx then fx=fx-1
if vy+hiba>fy then fy=fy+1
if vy+hiba<fy then fy=fy-1
if fx<1 then fx=1
if fy<1 then fy=1
if fx>20 then fx=20
if fy>15 then fy=15
A flopi egy kicsit "vak" lesz, ugyanis nem fogja tudni, hol a vírus. A hiba változóba egy véletlen értéket rakunk -2 és 2 között, ami befolyásolja az fx és fy értékének módosítását. Az utolsó 4 sor azért van, hogy ne tudjon a flopi kimenni a pályáról - a hiba változó miatt -2 és 22 között lehetne az fx értéke. Így már van egy szép, mozgó ellenségünk; azonban az, hogy elkap-e minket, nagyrészt a véletlen műve. Mit tehetünk?
- Különböző, vírusunk számára hasznos dolgokat helyezhetünk el a pályán - például egy teleportot, ami véletlenszerűen elteleportálja a flopit addig, amíg rajta állunk. Kétszer mehetünk rá (mert ugye hibás szektorrá válik az is)
- A flopi csinálhatna értelmes dolgokat is - miért ne javítaná ki a hibás szektorokat, hogy mi is ráléphessünk megint?
-Mi lenne, ha vírusunknak lenne mondjuk 10 élete?
Lássunk neki ezeknek a megvalósításához!
A legegyszerűbb dolog az, hogy a flopi kijavítsa a hibás szektorokat - a szektorok kijavításának megírása a hibás szektor készítésének fordítottja lesz. A hibás szektorok létrehozása után írjuk ezt a kódot:
`flopi
if palya(fx,fy)=1 then palya(fx,fy)=0 : paste image 1,50+(fx*30),50+(fy*30)
if palya(fx,fy)=2 then palya(fx,fy)=1 : paste image 2,50+(fx*30),50+(fy*30)
Ez egyszerű volt. Mostmár a piros kockából sárga lesz, a sárgából meg zöld, ha rámegy a flopi. Most rakjuk be a teleportot! A program elejére megy egy
load image "teleport.bmp",8
programsor. Azután elhelyezzük a véletlenszerűen teleportot:
x=rnd(18)+2 : y=rnd(13)+2
sprite 3,50+(x*30),50+(y*30),8
Azért 18 és 13 a két határérték, hogy ne lehessen a koordináta 1 - nehogy véletlenül a kiindulási helyen legyen az a teleport. Most már csak a teleportálás van hátra. Az ellenségmozgató kód végét így írjuk át:
if fy>15 then fy=15
if sprite exist(1)=1
if sprite collision(1,3)=1 then fx=rnd(19)+1 : fy=rnd(14)+1
endif
ido1=timer()
endif
Ha létezik az 1-es sprite (ez azért kell, mert a ciklus első futásakor még nem létezik; a ciklus végén jön létre; enélkül a sprite collisionnál futásközbeni hibát kapnánk (nem létező sprite)) akkor megnézzük, ütközik-e; ha igen, két véletlen koordinátát számolunk a flopinak, amíg a teleporton állunk.
Most jöjjön az élet-kijelző! Ez se lesz bonyolult dolog: 10-szer kirajzoljuk a vírusunkat, és mindig, amikor találkozik a flopival, letörlünk egy képet. A képeket rajzoló sorok a program elejére mennek:
for i=31 to 40
sprite i,50+((i-30)*30),50,4
next i
elet=10
Végül kezdőértéket adunk az elet változónak. Az élet levonása így néz ki:
if (sprite exist(1)=1) and (sprite exist(2)=1)
if sprite collision(1,2)=1
delete sprite 30+elet
elet=elet-1
`játék vége
if elet=0 then end
endif
endif
Ha a két sprite (flopi és vírus) ütközik, akkor csökkentjük az elet változó értékét, meg letörlünk egy spriteot. Csinálunk egy bezárás gombot, meg egy győztél és eggy vesztettél feliratot, és kirajzoljuk őket.
load image "vesztettél.bmp",9
load image "győztél.bmp",10
load image "bezár.bmp",11
Bezárás gomb: (a program elején)
sprite 8,620,55,11 : disable escapekey
Győzelem:
if sprite image(20)=7 then sprite 9,300,275,10 : suspend for key : end
Gémóver:
if elet=0 then sprite 9,300,275,9 : suspend for key : end
A disable escapekey letiltja az esc-kel kilépést - ha már csinálunk bezárás gombot, ne léphessen ki a játékos esc-kel! A főciklusba jön még a következő sor; ez kezeli a bezárás gombot:
if (mousex()<680) and (mousex()>620) and (mousey()>55) and (mousey()<75) and (mouseclick()=1) then end
Mostmár csak hangokat kell a játékba rakni:
load sound "hangfájl.wav",sorszám - betölti
play sound sorszám - lejátssza
Most már nem írom le többször a kódot, a játék fejlesztése ezzel most véget ért, a végleges kódot médiákkal megtaláljátok rarban. Azt még nem lehet mondani, hogy a játék kész, még sok felvehető dolgot, akadályt, stb. lehet belerakni. A leírás jó hosszú, a kód csak 2,5 kB - nagyon rövid! A Dark Basicben ez a jó - kevés kódból egész jól kinéző játékot lehet írni. Ha jók lesztek, a következő részben újra találkozhattok ezzel a játékkal - csak 3D-ben!
Most pedig jöhet egy kis 3D?
Akkor most ismerkedjünk tovább az objektumokkal! Ha megnézzük a súgót, észrevesszük, hogy a Dark Basic több objektumtípust is "alapból" kezel; ezeknek az objektumoknak a létrehozására külön parancsok vannak:
make object cube - kocka
make object box - téglatest
make object sphere - gömb
make object cone - kúp
make object plain - síklap
make object triangle - háromszög
make object cylinder - henger
Ezekkel a primitív formákkal sok mindent tudunk kezdeni, pálya létrehozására megfelelnek, de nem árt, ha a játékunkban 50-nél több a főhős poiligonszáma :-) Hogy szép legyen az élet - no meg a játékunk is - a DB kezeli a .x (DirectX) és a .3ds (3D Studio Max) 3D test formátumokat. Ezeket kétféleképpen tudjuk felhasználni:
load object "fájlnév",objektumszám
Ez a 3D drótvázból (mesh) egyből objektumot készít, amit egyből felhasználhatunk. A másik módszerben először betöltjük a mesh-t, azután objektumot csinálunk belőle:
load mesh "fájlnév",meshszám
make object objektumszám,meshszám,textúraszám
Ezzel a módszerrel a textúrázást is meg lehet oldani. Ha nem akarunk textúrát, a textúraszámhoz írjunk egy elegáns 0-t. Apropó, textúrázás. Az eddig legyártott objektumaink mind fehérek voltak, esetleg színeztük őket, meg a DB rájuk rajzolta az árnyékolást. A textúrázáskor a 3d testre ráhúzunk egy anyagmintát (textúra). Hogy ez hogy néz ki, az alábbi képeken láthatjátok:
(A szenvedő alany egy, az angol Dark Basicesek által gyártott alien modell. Jelenleg van egy programozói verseny: ezzel az ufóval (ami mellesleg animálva van) kell egy jó kis játékot összedobni. Nem a grafika a lényeg, a játszhatóság; és "szerény" :-) kis számítógép a fődíj - részletek a www.thegamecreators.com-on!)
És a kód, ami a fenti képet elkövette:
load image "alien.bmp",1
load mesh "alien.3ds",1
make object 1,1,1
do
loop
Eléggé béna játékot lehetne összehozni, ha az objektumokat nem lehetne animálni - képzeljük el, hogy az FPS-ünkben az ellenségek nem sétálnak, hanem egyenes lábbal csúszkálnak. Az animációt elég egyszerűen tudjuk kezelni a DB-ben:
play object objektumszám
Ha a végtelenségig szeretnénk ismételni:
loop object objektumszám
Ez eddig szép és jó, csak ha a karakter többféle mozgást végez, akkor mit csináljunk? Minden mozgást mentsünk külön fájlba? Nem kell, be lehet állítani, mettől meddig játssza az animációt a program. Pl. ha az első 20 kocka a járás, 20-tól 40-ig a támadás, akkor írjuk be, hogy loop object 1,1,20; és az objektumunk "sétálni" fog. Ha meg azt írjuk be, hogy play object 1,21,40; akkor az objektum lövésanimációját nézhetjük meg.
Egyelőre ennyit az objektumokról. Meg a 3D-ről. Egyelőre. A következő részben szó lesz a mátrixokról, fényekről, és megígérem, nem fogok többet ilyen hosszan tárgyalni egy egyszerű 2D programot :-)