2D-s játék készítése

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 :-)

Várkonyi Tibor - vtibor3@freemail.hu