Játékfejlesztés Dark Basic nyelven - 1. rész

 

Üdvözöllek, olvasó!

 

Ez a cikk/segédlet a Dark Basic programozásról fog szólni. Az írásakor azt tartottam szem előtt, hogy a programozáshoz semmit sem értő olvasók is könnyedén el tudják sajátítani a nyelvet. Tessék bátran tovább olvasni ;)

 

Tartalomjegyzék

---------------

I.   Előszó - általános tudnivalók

II.  A Basic nyelv ismertetése - a Dark Basic "basic" része. A nyelv ismertetése egyszerű programok írásával.

      1. Intro

      2. A PRINT parancs

      3. Változók használata

      4. Elágazások

      5. Számlálós ciklusok

      6. Végtelen ciklusok

      7. A GOTO parancs

      8. A GOSUB alprogram

      9. A FUNCTION alprogram

      10. Összetett változók: a tömbök

      11. Data sorok

      12. A DBPro újdonságai

            a, Select ... Case szerkezet

            b, Változódeklarálás

            c, Változók hatásköre: LOCAL és GLOBAL

            d, Saját típusok deklarálása

            e, Új parancsok a tömbök kezelésére

      13. Fájlkezelés

      14. Szöveg minden mennyiségben

      15. Bevitel kezelése

      Összegzés

III. Folyt. köv... Majd ide jönnek a grafikával foglalkozó fejezetek

 

*********************************************

***************** I. Előszó *****************

*********************************************

 

A Dark Basic (röviden: DB) egy programnyelv, amelyet kifejezetten játékok írására fejlesztettek ki. Gondolom, mindenkinek eszébe jutott játék közben egy csomó ötlet, hogy mit változtatna, vagy az, hogy milyen játékot szeretne. Akinek van elég kitartása, Dark Basicben olyan játékot készíthet, amilyet csak akar.

 

Természetesen ehhez programozási ismeretek szükségesek, ugyanis a Dark Basic nem játékkészítő program. (Tehát lehet mondjuk adatbázis-kezelőt is írni benne, csak pl. Visual Basicben egyszerűbb.) Nem kell kétségbe esni, a Basic nyelvek a legegyszerűbb programnyelvek, a Dark Basic meg rengeteg speciális paranccsal rendelkezik, ami megkönnyíti a 2D-s és 3D-s játékok írását! Nézzük csak mit is tud:

 

- 3D objektumok kezelése (.x, .3ds formátum)

- 2D sprite kezelés (bmp, jpg)

- wav, midi, mp3, avi lejátszás

- fények kezelése

- 3D terepek (mátrixok) kezelése

- multiplayer támogatás

- és még sok minden

 

A Dark Basicnek két fajtája van:

 

Dark Basic 1.x - A "sima" DB, 2000-ben jelent meg. Az angolok DBC-nek (Dark Basic Classic-nak) szokták rövidíteni, itt DB-nek nevezzük. Magyar nyelvű változata is létezik. Nem fordítja le teljesen a benne írt programot. Az exébe egy parancsértelmezőt (compiler) rak a kód mellé, és futásidőben fordítja, (mint amikor a szerkesztőben teszteljük a programot, írás közben) ezért lassabb. DirectX 7 szükséges a vele készült játékok futtatásához. Utolsó verziója az 1.13, már nem fejlesztik tovább.

 

Dark Basic Pro - Ő a DBPro, 2002-ben jelent meg, a Dark Basic leváltása céljából. Teljesen új kódszerkesztő, több mint 100 új parancs, új 3D motor (sokkal nagyobb sebesség 3D-ben és 2D-ben is). Egy kicsit elhamarkodottan adták ki - folyamatosan jelennek meg hozzá a patchek, és egyre fogynak a hibák benne. Elméletileg kompatibilis a sima DB-vel, de 90% az esély arra, hogy változtatni kell a DB-ben hibátlan kódon. Viszont ha benne kezdünk el írni egy programot, nem lesz problémánk. Futtatáskor először exét készít, majd azt futtatja. A sima DB-vel ellentétben gépi kódot készít. DirectX 9 (régebbi verzióknál Directx 8) kell a benne készült programokhoz.

 

Ezentúl, ha DBpro-ról lesz szó, azt külön kiemelem, a DB, esetleg a DBC rövidítés mindig a sima Dark Basicet jelöli.

Az eddig leírtakból lehet sejteni, hogy a DB egy Basic nyelv és egy 3D motor keveréke. A szerkezete a nem objektumorientált Basicekre (Qbasic, C-64 Basic) hasonlít a legjobban.

 

A szerkesztő:

A DB-nek eléggé egyedi kódszerkesztője van. A programot (mint általában a Basic nyelvekben) F5-tel futtathatjuk. A szerkesztőbe az F12-vel léphetünk vissza (F11-gyel a súgóba). A program végén - meg ha futás közben Esc-et nyomunk, előjön a parancssor. Ide parancsokat írhatunk be, ami a hibajavításban sokat segíthet. Ha esetleg nem tetszik a helye, arrébb is húzhatjuk. Újabb esc nyomására eltűnik - ha nem ért véget a programunk. A DBPro szerkesztője teljesen más. Jobboldalt (vagy baloldalt, ha úgy állítjuk be) található a Project manager, ami kiírja a funkciók, címkék, változók listáját, programunk fájljait, stb. Megkönnyíti a kód átlátását. Az F12-t sajnos nem használhatjuk, esc nyomásával léphetünk ki programunkból. (Ami persze letiltható)

 

A DB Magyarországon boltban megvásárolható, a DBPro külföldről rendelhető csak meg. A kipróbálható verziók letölthetők itt a www.thegamecreators.com oldalról.

 

A sima DB-nél csak néhány parancs (memblock és multiplayer parancsok) van letiltva a demóban, a DBpro-nál 30 napos időlimit van, a programunk indulásakor és bezárásakor kiírja hogy DBpro-val készült, ráadásul a képernyő jobb alsó sarkában egy DBpro logót láthatunk, amíg fut.

 

Természetesen a lehetőségek nem végtelenek, tehát ne számítsunk arra, hogy az iD Software aktuális 3D motorját földbe döngöli a játékunk. A DBpro-nak sokkal nagyobb teljesítménye van mint a DB-nek, szép dolgokat lehet benne csinálni, akár kiadható 3D-s játékot is. A grafika a DB-n IS múlik (hogy mikor kezd el akadozni), de a játékmenet csak a programozón. A Terep2 sem a csúcs grafikája miatt lett népszerű...

 

Néhány szó még erről az írásról: Ezt a segédletet (tutorial-t) a teljesség igénye nélkül írom. 1.052-es verziójú DBPro-t használok, és 1.13-as DB-t (Ha esetleg egy általam jelzett hiba nem lenne benne a programnyelvben, újabb a DBPro-d). Néha csak megemlítek egy parancsot, nem részletezem. Ez általában azért van, mert a parancsot iszonyat egyszerű használni. Ha nem, akkor a paramétereit meg lehet nézni a súgóban. Be kell írni a parancsot a kódszerkesztőbe, és F1-et kell nyomni. Mint írtam, a kezdők igényeit tartottam szem előtt, gyakorlott programozóknak néhol fölöslegesen részletezőnek és szájbarágósnak tűnhet a szöveg. Úgy érzem, valamennyire szükség van erre (meg hát nem kötelező minden szót elolvasni, aki tud már Basicben programozni, nyugodtan ugrálhat a szövegben! Van nekik szóló rész is :-). Egyébként a Basic nyelvről szóló rész olvasása közben is előre lehet lapozni a 2D vagy a 3D grafika ismertetéséhez, nem szükséges az első grafikus programhoz az egész Basic nyelv ismerete.

 

Ennyit bevezetőnek. Most kezdjünk el programozni!

 

*****************************************************************

***************** II. A Basic nyelv ismertetése *****************

*****************************************************************

 

1. Intro

 

Senki sem szereti az elméleti részeket, így hát röviden le is rendezem. A programozást képzeljük el úgy, mint egy építőkocka-játékot, vagy LEGO-t. Adva vannak építőkockák, a programnyelv parancsai, és ezekből lehet építeni. Akármit építhetünk, akármekkora méretben, csak a gépünk teljesítménye szabhat határt. Ha programot írunk, először döntsük el, mit akarunk írni. Mert az a legkellemetlenebb, amikor egy félkész programnál eszébe jut az embernek, hogy "jaj, kihagytam ezt és ezt", és át kell írni program nagy részét, hogy bele lehessen rakni a nem tervezett funkciókat. Az ilyen eseteket a program megtervezésével, dokumentáció írásával lehet kivédeni. Ez persze nem kötelező! Csak tudja az ember, hogy mit akar. Egyébként a programtól elvárt funkciók listája csak egyre nő és nő, az ember elveszik a részletekben, és az eredeti célkitűzések nem teljesülnek. Ilyenkor érdemes csak az eredeti célokat teljesíteni, de a programunkat írjuk úgy meg, hogy jól bővíthető legyen.

A programozás problémamegoldás. Van egy nagy probléma: pl. Hogyan írjam meg, hogy tudjak lőni egy fpsben? Ezt a nagy problémát kisebb problémákra kell bontani, és azokat külön-külön kell megoldani:

Először is kell rajzolni egy fegyvert

Azután készíteni kell egy 3D-s lövedék objektumot.

Mozgatni kell a lövedéket

Ha ütközik, meg kell nézni, hogy mibe.

Ha ellenfél 3D objektumába, akkor törölni kell.

Ha falba, akkor rajzolni kell lövésnyomot.

Az egyes kis problémákra meg kell keresni a megfelelő módszert, Dark Basic parancso(ka)t, amivel megoldjuk.

 

2. A PRINT parancs

 

Indítsuk el a DB-t, és ismerkedjünk meg az első Basic paranccsal, a Print paranccsal! A Print a képernyőre írja ki a megadott szöveget vagy számot.

Másoljuk be ezt a programot:

 

print 7

print 8+8.8

print (8*8)-4

print "Szia!"

print "bla"+"bla"

print "blabla";22-2

 

Futtassuk! [F5 gomb]

Az eredmény:

 

7

16.8

60

Szia!

blabla

blabla20

 

A print parancs formája:

print [kifejezés]

A kifejezés lehet szöveg, művelet, változó (erről később lesz szó), akármi. Ha nincs kifejezés, üres sort rak.

 

Nézzük csak hogy reagál a különböző dolgokra a DB és a print:

- Ki tud írni számot és szöveget is

- Törtszámoknál tizedespontot kell használnunk: a Basicben a vesszőnek elválasztó szerepe van két adat között

- Automatikusan elvégzi a matematikai műveleteket

- A szövegeket macskakörmök közé kell írni (Shift-2)

- Két kiíratandó adat közé pontosvesszőt vagy vesszőt kell tenni

- Két szöveget + jellel lehet összefűzni

- Minden print parancs új sorban kezdi a kiírást

- A képernyő nem scrollozódik, ha a print az aljára ér (DBPro-ban igen)

 

Eddig gondolom érthető. A Print után írjuk a kiíratandó számot/szöveget, és ő szépen kiírja.

 

Programozás közben gyakran előfordulhat, hogy több rövid parancs nagyon megnyújtja a programot. A BASIC nyelv lehetőséget ad több parancs egy sorba írására. Két parancs közé kettőspontot kell rakni. Nem szükséges szóköz a kettőspont mellé, de így átláthatóbb:

parancs1 : parancs2

 

Még egy dolog: megjegyzések.

A programba tetszőleges szöveget, megjegyzést be lehet írni, ha írunk elé egy rem szócskát. A rem "parancs" arra utasítja a parancssor-végrehajtót, hogy ne vegye figyelembe az adott sort, ugorja át. Rem helyett írhatunk egyszerűen egy `-t is (AltGr+7). A remmel megjelölt (kiremelt) sorok sárga színűek a kódszerkesztőben. Létezik még remstart és remend parancs is. A két parancs közötti sorokat a DB nem hajtja végre. Ideális hibajavításhoz: A hibásnak vélt sorokat elé rem-et írunk, és megnézhetjük, nélkülük jól működik-e a program. Így nem kell semmit se törölni, a kódból azt a sort kapcsolhatjuk ki, amelyiket akarjuk. Mint említettem, megjegyzések írására is lehet használni. Ha egy programkódot hosszabb szünet után előveszünk, előfordulhat, hogy nem értjük egyes részeit. Segíthet ilyenkor az értelmezésben, ha írtunk 1-2 megjegyzést a kódba még annak idején.

 

3. Változók használata

 

Gépeljük be az alábbi programot:

 

a=3*3

print a

 

Ezt a programot futtatva 9-et kapunk eredményként. Hogyan is működik?

1. sor: Az 'a' nevű "memóriarekesz" (változó) felveszi a 3*3-as értéket - amit 9-ként tárol el, de ezt mi nem látjuk.

2. sor: kiírja az "a" változó értékét, így láthatóvá válik a végeredmény!

 

A változókat képzeljük el úgy, hogy a memória egy bizonyos "része" valamilyen nevet kap (példánkban "a"), és itt a programunk adatokat tárolhat. Ha az "a" változónak új értéket adunk, a régi érték elveszik. A változók nevében csak az angol ABC betűi és számok lehetnek, valamint típusjelző karakterek (lásd lentebb). A változók nevüket onnan kapták, hogy értékük program közben változhat: vannak ugyanis konstansok, amiknek nem változhat az értékük. Rájuk nem térünk ki. Több programnyelvben deklarálni kell a változókat, azaz létrehozni őket a program vagy eljárás elején. Itt nem kell erre figyelnünk, a DB csinál akkor csinál új változót, amikor először találkozik a nevével. Ezért vigyáznunk kell az elírással: a Basic készít az elírt névvel is egy változót. (A DB részéről semmi baj nincs. Már megint új változó kell? Tessék!) Hosszabb programokban ajánlott beszédes változónevet használni, hogy később tudjuk, mit tárol.

 

Értékadás a változóknak a következőképpen néz ki:

változó=érték

Ha matematikailag nézzük, az x=250 és a 250=x ugyanaz, de a DB-nek az utóbbi forma értelmetlen. Igaz, nem okoz hibát; az "x" értéke 0 marad.

 

A Dark Basic alapvetően 3 adattípust különböztet meg:

1, egész szám

2, valós szám (tizedestörtek)

3, szöveg (string)

 

Emiatt 3 változótípus van:

egész: nincs típusjel

valós: # a típusjel

string: $ a típusjel

 

A típusokra vonatkozó szabályok:

- A típusjel mindig a változónév után áll, pl. valtozo# vagy valtozo$.

- Megegyező nevű, de eltérő típusjelű változók külön változónak számítanak. Ugyanabban a programban előfordulhatnak v, v# és v$ változók különböző értékekkel.

- Ha egész változónak tört értéket akarunk adni csak a szám egészrésze tárolódik el (tehát 1.999-ből 1 lesz). (a=b#)

- Valós változónak adhatunk egész értéket. (a#=b)

- Ügyeljünk a tizedespont használatára, a DB nem fogadja el a tizedesvesszőt!

- Ha egy tizedestörtből lehagyjuk az egészrészt, akkor azt 0-nak veszi a DB, (például .115=0.115)

- A string változókba csak szöveges adatot tehetünk (ami macskakörmök között van MINDIG). Nézzünk egy példaprogramot:

 

print duma

print "duma"

 

Futtatáskor a következő szöveg jelenik meg:

0

duma

 

Az első sornál a duma szót változónévként értelmezte a DB, ezért létrehozott egy ilyen változót. Az új változó értéke alapból 0 lett, és ezt ki is írta. A második sorban szöveges adatként írattuk ki a duma szót.

 

Gyakran előfordul, hogy számot szöveggé kell alakítani, vagy fordítva. Ehhez függvények kellenek. A programozásban a függvény egy olyan parancsot jelent, aminek van visszatérési értéke. Adunk neki egy adatot, csinál vele valamit, majd visszaad egy másik adatot, ez a visszatérési érték. A visszatérési értéket változóba rakjuk:

 

szám=val(szöveg)

szöveg$=str$(szám)

 

Példaprogram:

 

a#=1.2345

a$=str$(a#)

a=val(a$)

print "String: ";a$;"  Egész szám: ";a

 

A példaprogramban az a# változóból szöveges változót, készítünk, amit számmá alakítunk. A val() függvény meghagyná a tizedesjelen túli számokat, de levágja, mert egész típusú változóba rakjuk az adatot.

 

A változók alkalmasak műveletek végzésére, a matematikából megszokott módon. Műveletek és jeleik:

+ : összeadás

- : kivonás

* : szorzás

/ : osztás (Érdemes osztásoknál valós változókat használni, különben pontatlan lesz az eredmény. Sok hibát kerülhetünk el így.)

^ : hatványozás

A gyökvonást kétféleképpen oldhatjuk meg: ^0.5-tel vagy az SQRT() függvénnyel. Tehát print 9^0.5 és a print sqrt(9) ugyanazt az eredményt adja vissza. A műveletek sorrendjének megadására zárójelek /() <-csak ezek/ használhatók.

 

A következő programban két változó szorzatát egy harmadikba rakjuk, amit kiírunk:

a=25

b=10

szorzat=a*b

hanyados#=a/b

print "A szorzat: "; szorzat

print "Ez tízzel több: "; szorzat+10

print "A hányados: "; hanyados#

 

Megjegyzés: A szorzat+10 NEM értékadó utasítás, a változó értéke nem növekszik, csak a print ír ki 10-zel többet. Érdemes osztásoknál valós változókat használni, különben pontatlan lesz az eredmény. Sok hibát kerülhetünk el így.

 

Írjunk egy olyan programot, ami kiszámolja egy kör területét! Ehhez először be kell kérni egy adatot, a kör sugarát. Az adatbekérést az INPUT parancs végzi:

 

input "Add meg, mekkora a kör sugara, majd nyomj Entert! ",sugar

print "A kör területe: ",sugar^2*3.14

 

Itt a printnél végeztük el a műveletet, így nem kellett valós típus a törthöz. Természetesen itt sem változik a sugar változó. Az input automatikusan végzi az adatbekérést, nem kell a betűk letiltásáról gondoskodnunk: ha szám (egész, valós) változót használunk, csak a számokra, a minuszjelre és a pontra reagál a program adatbevitelkor. Mellesleg kiírási funkciókat is ellát, de ha akarjuk, ezt elhagyhatjuk. Alakja:

 

INPUT "szöveg",változó

 

A beolvasott szám vagy szöveg a megadott változóba kerül, így azt szabadon felhasználhatjuk.

Ennyit most a változókról. Léteznek tömbök, amik összetett változók. Róluk később lesz szó,

 

4. Elágazások

 

Eddigi programjaink futása lineáris volt, sorban hajtódtak végre az egymás alatti parancsok. A programnak volt valami előre megadott adata, csinált vele valamit, majd kiírt valamit. Mindig ugyanazt. Ez így nem valami érdekes dolog... Azonban programunk futását vezérlési szerkezetekkel tudjuk irányítani. Ezek az elágazások, ciklusok, alprogramok, ugrások. Először ismerkedjünk meg az elágazásokkal!

 

input "Írj be egy számot! ",szam

if szam=0 then print "A szám 0"

if szam<0 then print "A szám negatív"

if szam>0 then print "A szám pozitív"

 

Ez már igazi program. Adatbekérés - feldolgozás - kiírás. Funkciója: Eldönti egy számról, hogy negatív-e vagy pozitív. Az if...then parancsok magyarra fordítása: ha...akkor. Így egész beszédes lesz a programunk: "Ha szam=0 akkor írd ki: A szám 0"

 

Az if és then között a feltétel található: valami összehasonlítása valamivel. Ha az igaz, akkor végrehajtódik a then utáni sor. Ha nem, a DB a következő sort fogja olvasni. Összefoglalásul:

 

IF feltétel igaz THEN parancsok

 

De mit tegyünk, ha a then után több parancsot szeretnénk végrehajtani? Két választásunk van:

1, Kettősponttal még egy parancsot zsúfolunk a then után

2, Endifet használunk:

 

if feltétel igaz

      parancs1

      parancs2

      ...

      endif

 

Endif használatakor nem kell a then-t kiírni. A többi basictől eltérően a DB nem fogadja el az end if alakot.

 

Most vizsgáljuk meg közelebbről a feltételt! A feltételben valami összehasonlításnak kell állnia. Már láttuk, lehet változót értékkel összehasonlítani:

 

if változó=9 then ...

if szoveg$="Szia!" then ...

 

Lehet változót változóhoz hasonlítani:

 

if valtozo1=valtozo2 then ...

 

A feltételben felhasználhatunk függvényt is:

if sqrt(9)=3 then print "Hurrá, ennek így kell lennie!"

 

A két összehasonlítandó adat között relációs jeleket használhatunk:

(a relációs jeleket nem szükséges szóközökkel elválasztani az értékektől)

a = b : a és b egyenlő

a < b : a kisebb mint b

a > b : a nagyobb mint b

a <= b : a kisebb mint b vagy egyenlő b-vel

a >= b : a nagyobb mint b vagy egyenlő b-vel

a <> b : a és b nem egyenlő

 

Több feltételt is alkalmazhatunk:

if (feltétel1) logikai_operátor (feltétel2) then parancsok

A logikai operátor lehet OR vagy AND. OR esetén akkor hajtódnak végre a then utáni parancsok, ha legalább az egyik feltétel igaz. AND esetén akkor, ha az összes feltétel igaz.

Itt van rá egy példa:

 

input a

if (a<10) and (a>0) then print "A szám 0 és 10 között van"

 

Rendben, most írjuk át ezt a programot úgy, hogy ha nincs a szám 0 és 10 között, azt is kiírja!

Ehhez szükség lesz az ELSE (jelentése: különben) parancsra. Az else utáni parancsok akkor hajtódnak végre, ha a feltétel nem igaz.

 

if feltétel igaz

      parancs1

      parancs2

      ...

      ELSE (egyébként)

      parancs1

      parancs2

      ...

      endif

 

Programunk így néz ki:

 

input a

if (a<10) and (a>0)

      print "A szám 0 és 10 között van"

      else

      print "A szám nincs 0 és 10 között"

      endif

 

És így néz ki egy sorba sűrítve az elágazás:

 

input a

if (a<10) and (a>0) then print "A szám 0 és 10 között van" else print "A szám nincs 0 és 10 között"

 

Ennyit az IF...THEN...ELSE...ENDIF szerkezetről.

Létezik egy másik szerkezet, a Select ... Case szerkezet. Mivel ez csak DBpro-ban létezik, a 12-es fejezetben lesz szó róla.

 

5. Számlálós ciklusok

 

A ciklusok olyan programrészek, amik ismétlik a beléjük írt programsorokat végesszer vagy végtelenszer. Így egy parancsot/parancssorozatot nem kell annyiszor beírni, ahányszor szükség lesz rá, elég csak egyszer egy ciklusba. A számlálós ciklusok csak véges számú ismétlésekre képesek:

 

Írjunk programot, ami kiírja 20-ig a számokat!

Ehhez 20 print sor kellene, de megoldhatjuk rövidebben:

 

for c=1 to 20

      print c

next c

 

1. sor: a ciklus eleje. Beállítjuk, hogy a ciklusváltozó, a 'c' 1-től 20-ig nőjön. A ciklusváltozó (c) értéke eggyel nő.

2. sor: kiírjuk a c értékét

3. sor: a ciklus vége. A program futása a ciklus elejétől folytatódik mindaddig, amíg a "c" nem lesz 20

 

A ciklus közepét ciklusmagnak nevezzük, ez a for...to sorban beállított értékek szerint ismétlődik. A ciklusnak van egy ciklusváltozója (itt a "c"), aminek az értéke nő/csökken amíg el nem érí a kívánt értéket. A ciklusváltozót természetesen változtathatjuk a cikluson belül. A ciklus formája:

 

FOR ciklusváltozó=kezdőérték TO végérték STEP lépésköz

      ... (ciklusmag parancsai) ...

NEXT ciklusváltozó

 

Eddig még nem írtam a Step-ről, tehát nézzük meg, mit csinál. Írjunk egy programot, ami 20-tól 1-ig számol! Beírhatjuk a for c=20 to 1 sort, de nem működik. A kezdőérték ugyanis alapból nagyobb, mint a végérték, így a ciklus nem fut le. A STEP paranccsal a lépésközt adhatjuk meg: azt, hogy mennyit nőjön/csökkenjen a ciklusváltozó értéke. Ha -1-et adunk meg lépésköznek, szépen számol visszafele a programunk:

 

for c=20 to 1 step -1

      print c

next c

 

Több ciklust is egymásba lehet rakni. Sokszor szükségünk lesz rá. Egyszerűen annyit kell tenni, hogy a ciklusmagba írunk még egy ciklust. Íme a példa:

 

for a=1 to 10

      b$=""

      for b=1 to 10

            b$=b$+" "+str$(b)

      next b

      print b$

next a

 

6. Végtelen ciklusok

 

Előfordul, hogy azt szeretnénk, hogy egy programrész állandóan ismétlődjön. Ez is gyakran elő fog fordulni, mert a DB programok 95%-ában van egy központi Do...Loop ciklus, ami a program fő ciklusa. Ehhez nem kell sok kommentár: A Do és Loop között állandóan ismétlődnek a parancsok.

 

do

      print "Szia"

loop

 

Mivel itt nincs ciklusváltozó, erről magunknak kell gondoskodnunk. A DB nem ismeri a DO-LOOP-nál a kilépési feltételeket, (mint UNTIL és WHILE parancsok a többi Basic nyelvjárásban) ezért erről is nekünk kell gondoskodnunk. A lenti példában egy számlálóssá alakított végtelen ciklus látható:

 

do

      a=a+1

      print a

      if a=20 then end

loop

 

Ebben a programban az end számít újdonságnak, ami egy alap, minden basicben megtalálható parancs. Nem nehéz kitalálni, hogy leállítja a program futását.

Végtelen ciklusokból nem ártana néha kiugrani. Erre használható az exit parancs. Hatására a program futása a ciklus vége után folytatódik.

Van még két fajtája a végtelen ciklusoknak:

 

Repeat

      blabla

Until feltétel

 

While feltétel

      blabla

Endwhile

 

A while...endwhile ciklus FUT, ha a feltétel igaz, a repeat...until ciklus VÉGET ÉR, ha a feltétel igaz. A zseniális 20-ig számoló programunk így fest ezekkel a ciklusokkal:

 

while a<20

      a=a+1

      print a

endwhile

 

repeat

      a=a+1

      print a

until a=20

 

A ciklusmagot (a ciklus közepe) nem kötelező egy tab-nyival bentebb kezdeni, de sokkal átláthatóbb lesz a program.

 

7. A GOTO parancs

 

Az elágazásokkal és a ciklusokkal már megismerkedtünk, vannak még az ugrások és az alprogramok. Először a feltétel nélküli ugrásról beszélünk. Ő a GOTO parancs. Önmagában azonban semmire se jó, mert meg kell határozni, hova ugorjon a parancsértelmező. Ezt a régi basicekben (pl. C64 Basic - a Qbasicben is lehet, de nem kötelező) a programsorok számozásával adták meg. A DarkBasicben (meg minden többi basicben) címkéket helyezhetünk el a programban. A címke egy kis karaktersorozat, nem lehet benne ékezet, nem egyezhet meg egy utasítás nevével, és ami legfontosabb: a végén kettőspontnak kell lennie. Nem írhatunk utána más parancsot: egyedül kell állnia a sor elején. És hogy miért kell össze-vissza ugrálni a programban ok nélkül? Mindjárt kiderül.

 

Hozzunk össze egy programot, amivel másodfokú egyenletet lehet számolgatni! Ha emlékszünk a másodfokú egyenlet megoldóképletére, eszünkbe jut az is, hogy gyök alatt nem állhat negatív szám. Ha negatív számot kapunk, a programunk kiírja, hogy nincs valós gyök, majd továbbugrik a végére:

 

`Az egyenlethez szükségünk van az a,b,c értékekre:

eleje:

input "Az A értéke:",a

input "A B értéke:",b

input "A C értéke:",c

`És átírjuk a megoldóképletet a basic nyelvére.

`Legelőször vizsgáljuk meg, hogy van-e valós gyök.

if ((b^2)-(4*a*c))<0 then print "Nincs valós gyök!" : goto vege

`Számolás! A két gyököt külön-külön kiszámítjuk.

x1=((0-b)+sqrt((b^2)-(4*a*c)))/(2*a)

x2=((0-b)-sqrt((b^2)-(4*a*c)))/(2*a)

`Majd kiírjuk.

print "X1= ";x1

print "X2= ";x2

vege:

print "Új számoláshoz nyomj egy billentyűt! ESC-kel kiléphetsz."

`Egy billentyű lenyomásáig felfüggesztjük a program futását.

`Esc amúgy is kilépés, nem kell vizsgálnunk.

suspend for key

cls

goto eleje

 

A goto-val ciklust is lehet csinálni, de a DO-LOOP szerkezet átláthatóbb. Példa erre egy ősrégi közismert C64 (QB) program:

 

10 PRINT "SZIA"

20 GOTO 10

 

Dark Basicben ez így néz ki: (Sajnos a DB nem scrollozza printnél a képernyőt. Csak a DBPro az 1.05-ös verziótól kezdődően. De az se olyan szép, mint régen a DOS képernyő-scrollja :-)

 

start:

PRINT "SZIA"

goto start

 

Ennek a programnak nem volt sok értelme, de jólesett bepötyögni :)

 

8. A GOSUB alprogram

 

Mire jók az alprogramok? És egyáltalán mit jelent az hogy alprogram? Nézzük csak:

Gyakran előfordul, hogy egy programrészt többször fel kell használni. Megtehetjük, hogy bemásoljuk 100 helyre, de ettől csak fölöslegesen boinyolult, hosszú és átláthatatlan lesz a programkód. Ezért egy alprogramba rakjuk a többször használandó sorokat, és meghívjuk őket. Erre az egyik módszer a Gosub-Return szerkezet. A Gosub olasmi, mintz a Goto: meg kell adni neki egy címkét, és onnantól folytatódik a programunk. Abban különbözik csak, hogy megjegyzi, honnan ugrottunk el: ha Return paranccsal találkozik a parancsértelmező, visszaugrik oda. Itt egy példa, hogy érthető legyen:

 

a=8 : b=6

gosub szamol

a=10 : b=20

gosub szamol

a=4 : b=24

gosub szamol

end

 

szamol:

print "A érték:";a;" B érték:";b

print "Összeg:";a+b

print "Különbség:";a-b

print "Szorzat:";a*b

print "Hányados:";a/b

return

 

Így a szamol eljárás többször, több helyről is meghívható, rövidebb és átláthatóbb a program. Returnt akár feltételesen is rakhatunk az alprogramba, csak mindig legyen egy a végén.

 

9. A FUNCTION alprogram

 

Ezek a felhasználó által készített függvények. Hogy mi köze van a függvényeknek az alprogramokhoz?

 

A QBasicben az alprogramok lehettek Subok (eljárás) vagy Functionok (függvény). A DB összemosta a két típus között a határt.

A programozásban a függvény egy olyan utasítás, aminek visszatérési értéke van. A functionnak LEHET visszatérési értéke. De nem KÖTELEZŐ, hogy legyen. Ugyanígy lehet paramétere (bemenő értéke) is. A Function szerkezet sokkal önállóbb, mint a gosub.

 

Ha ez zavaros volt, akkor egyszerűbben fogalmazok: A FUNCTION-ok teljesen önálló alprogramok, amik visszaadhatnak értéket is, mint egy függvény.

 

Előnyei:

-Akár külön fájlban tárolható

-A DBpro szerkesztőjében "összecsukható"

-Úgy lehet használni, mint egy parancsot

-Nem kell címkékkel szenvedni

Hátránya:

-Át kell adni neki a változókat

 

Ajánlom ennek a szerkezetnek a használatát, a gosubhoz csak végveszélyban nyúljunk, ha túl sok bemenő változót használnánk (Mellesleg a funcion alprogramnak is lehet Gosub alprogramja!). A hátránya (át kell adni neki a változókat) előnnyé válhat, mert lokális változókat használ. A lokális változó azt jelenti, hogy csak a functionban létezik, a program többi részén nem. Tehát a főprogramban is lehet ugyanolyan nevű változó, nem fog megegyezni az értékük. Vannak azért itt is kiskapuk - a főprogramban létrehozott tömbökhöz hozzáférnek az alprogramok is - erről majd a 12. fejezetben esik bővebben szó.

Most már nézzük meg, miről van szó! Az alprogram maga:

 

Function név

(parancsok)

....

...

....

...

endfunction

 

Úgy hívjuk meg, hogy a parancsok közé egyszeren beírjuk az alprogram nevét. A DBpro kötelezővé teszi, hogy a funkciónév után legyen két zárójel: () . Sima DB-ben ez akkor kötelező, ha van visszatérési érték (Később még szó lesz róla). Nézzünk egy egyszerű példát:

 

randomize timer()

 

do

    veletlenszoveg

loop

 

function veletlenszoveg

`Szín meghatározása

ink rgb(rnd(255),rnd(255),rnd(255)),0

`Hely meghatározása

set cursor rnd(640),rnd(480)

print "Ez a véletlen!"

endfunction

 

A program egy szöveget ír ki a képernyőre véletlen színnel a véletlenszerűen megadott koordinátákra. Ami új, az a véletlenszám számítása. Véletlen számot az RND() függvény ad, a szám 0 és a zárójelek között megadott maximális érték között lesz. A probléma az, hogy minden futáskor ugyanazokat a "véletlen" számokat számolja ki a DB. Ezért a véletlenszám-generátor kezdőértékét kell változtatni. Ezt a Randomize paranccsal tehetjük meg. Utána írjuk kezdőértéknek a Timer() függvényt, ami az időt adja vissza ezredmásodpercekben. Így már tényleg véletlen számokat kapunk. A set cursor-ral a kiírás helyét állítjuk be, képpontokban. Az alapból 640*480-as felbontású képernyőn 0-639-ig és 0-479-ig vannak számozva a képpontok. Az INK-ről és az RGB-ről a 11. fejezetben esik szó.

A másik újdonság a function. Láthatjuk, az alprogram nevét úgy használjuk, mintha egy DB-s parancs lenne. De ha változót is kell használni?

 

x=100 : y=100

kiir

suspend for key

end

 

function kiir

set cursor x,y

print x;";";y

endfunction

 

Ha futtatjuk a programot, azt írja ki, hogy 0;0. Az x és y változót át kell adni az alprogramnak. Mi is történik?

1, Az x és y felveszi a 100 értéket.

2, A kiir alprogram nem törődik a főprogram változóival. Ő is létrehoz egy x és egy y változót 0 értékkel. A főprogram változóinak értéke ettől 100 marad!

3, Ezek után az alprogram kiírja a saját változóit. Nem a főprograméit. A kis önző.

A megoldás: át kell adni az alprogramnak az x és y változót: (az alprogramban megváltoztattam a változóneveket az áttekinthetőség kedvéért)

 

x=100 : y=100

kiir(x,y)

suspend for key

end

 

function kiir(xhely,yhely)

set cursor xhely,yhely

print xhely;";";yhely

endfunction

 

Az alprogram meghívásánál (2.sor) átadjuk a két változó értékét az alprogramnak. Hogy ezek az értékek milyen néven kerülnek be az alprogramba, azt a deklaráció (function kiir(xhely,yhely) sor) után adjuk meg.

És ha az alprogramot függvényként szeretnénk használni, mit csináljunk?

 

egyik=10

masik=30

print kulonbseg(egyik,masik)

end

 

function kulonbseg(a,b)

c=a-b

endfunction c

 

A visszaadandó változót az endfunction után írjuk, és alprogramunkat függvénynek nevezhetjük. Ilyenkor DB-ben is mindig kell zárójel a függvénynév után. Ugyanis ha egy függvénynek nincs bemenő értéke, csak kimenő, és elhagyjuk a zárójeleket, a DB változónévnek nézi. Az end nem véletlenül áll a főprogram végén. Ha a program meghívás nélkül szembetalálkozik egy functionnal, runtime error-t (futásközbeni hibát) kapunk.

 

Apropó, hibák. Hibákból 3 fajta van:

1, elírási hibák, hiányzó ciklusvégek vagy paraméterek, stb.: Ezeket a DB futtatás előtt kiszűri, amikor leellenőrzi a kódot

2, futásközbeni hibák: Ők futás közben jönnek elő, amikor egy parancsnak rossz értékeket adtunk, vagy mondjuk egy fájl nem létezik. Legalább a DB szól, hogy léteznek.

3, bugok: Ez az ősmagyar szó az olyan hibát jelöli, amit a DB nem vesz hibának, csak mi. Nem azt kapjuk, amit várunk. Ők a legalattomosabbak. Jó esetben hamar kiderül, mi okozza őket.

A hibajavítás külön művészet. A programozás leghálátlanabb feladata: senki se veszi észre, ha kijavítjuk a programhibákat, de az mindenkinek feltűnik, hogyha benne maradnak a programban.

Vége ennek a kis kitérőnek, nézzünk valami mást:

 

Ahogy Gosub alprogramokból akármikor kiléphetünk Return-nel, itt ugyanúgy használhatjuk az Exitfunctiont. Az exitfunction annyit tesz, mint az endfunction - kilép - csak az alprogram közepén is használhatjuk.

Van egy fontos különbség a változókezelésben a DB és DBpro között. Ha kilépünk egy függvényből, a függvény saját változói törlődnek a DBPro-ban. Példa:

 

do

print novel()

loop

 

function novel

a=a+1

endfunction a

 

Ez a program DB-ben egymás alá írja az egyre növekvő számokat, DBpro-ban azonban 1-eseket ír egymás alá: az a változót mindig törli és újra létrehozza a program, ezért csak 1 lesz az értéke.

 

A functionokat - mint már írtam - ki lehet exportálni külön dba-kba. Meghívásukhoz az #include parancs kell.

Használata: #include "fájlnév" Ezután használhatjuk a megadott fájlban lévő functionokat. A DBpro támogatja a több fájlra bontott forráskódot, ott nem kell "includeolni" azokat a fájlokat, amik a project managerben a "files" pontnál megjelennek (azaz már benne vannak a projectben).

 

10. Összetett változók: a tömbök

 

Gyakran elő fog fordulni, hogy programunknak sok adattal kell dolgoznia. Ezt a sok adatot tárolni kell valahol. Mindhez külön változónév kellene, és szerintem senki se kezdene 1000 változó értékét pl. egyenként megnövelni. Sok adat együttes kezelésére találták ki a tömböket. Míg az egyszerű változókat nem kell deklarálni (megadni a típusukat a parancsértelmezőnek a program elején), addig a tömböket kell. Ezt a DIM paranccsal végezhetjük el.

 

dim szamok(10)

vagy

dim tortek#(10)

vagy

dim szovegek$(10)

 

Egy ilyen tömbben 11 adatot tárolhatuk el (csak olyan típusút, mint a tömbé!), tehát 11 elemű. Ez a 11 változó a következő: szamok(0), szamok(1), szamok(2) ... szamok(9), szamok(10). Ezeket úgy használhatjuk, mint normál változókat. Azért jó egy tömb, mert az elemnek az indexébe (a zárójelek közötti szám) változót is írhatunk, pl.: szamok(i)=1 A példában szereplő i változó ciklusváltozó is lehet, így több számmal egyszerűen tudunk műveleteket végezni.

 

Többdimenziós is lehet egy tömb:

 

kétdimenziós:

dim szamok(10,10)

háromdimenziós:

dim szamok(10,10,10)

négydimenziós:

dim szamok(10,10,10,10)

ötdimenziós:

dim szamok(10,10,10,10,10)

(Ez a maximum - a dimenziót persze nem fizikailag/matematikailag kell értelmezni)

A típus-előjelekről (#,$) itt se kell megfeledkeznünk. A fent leírt parancsok egy 11 elemű tömböt készítettek. (Tipp: Érdemes a tömb 0. elemét szabadon hagyni, és ide információkat írogatni magáról a tömbről - pl. azt, hogy hány elemben van adat)

 

Adat kiolvasása tömbből: változó=szamok(5)

Ez a sor a számok tömb 5-ös elemének az értékét teszi változóba. Egy tömb kiválasztott elemével mindent meg lehet csinálni, amit egy egyszerű változóval. Adatok beleírása is egyszerű: szamok(5)=34534. El lehet játszadozni a tömbök egymásba pakolgatásával is, pl.

szamok(maximum(x,1),10)=minimum(0,x)

Így is lehet értéket adni...

 

Nézzünk egy példaprogramot:

 

randomize timer()

dim adatok(10,10)

 

`feltöltés adatokkal

for i=1 to 10

      for j=1 to 10

            adatok(j,i)=rnd(9)

      next j

next i

 

`Kiolvasás

for i=1 to 10

      `A számokat egy stringgé alakítjuk, amit az a$ változó tárol.

      for j=1 to 10

            a$=a$+str$(adatok(j,i))

            `Két szám közé + jel, az utolsó után =

            if j<10 then a$=a$+" + " else a$=a$+" = "

            `Nem csak stringként fűzzük egybe a számokat, össze is adjuk őket

            a=a+adatok(j,i)

      next j

      `Az összeadás eredményét az a$ változó végére írjuk

      a$=a$+str$(a)

      `Kiírjuk

      print a$

      `Az összeget eltároljuk a tömbben

      adatok(0,i)=a

      `Lenullázzuk a két változó értékét

      a$="" : a=0

next i

`üres sor

print " "

 

`Most az előbb letárolt ösdszegeket adjuk össze

for i=1 to 10

      `a$-be mennek a szöveggé alakított számok...

      a$=a$+str$(adatok(0,i))

      `...a-ba az összeg

      a=a+adatok(0,i)

      if i<10 then a$=a$+" + " else a$=a$+" = "

next i

`kiírjuk

a$=a$+str$(a)

print a$

 

Amint láthatjuk, a tömbök igen alkalmasak műveletek végzésére. És mivel ez egy játékírásra kifejlesztett basic, sok helyen fogjuk őket használni, pl. mátrix magasságának tárolása, ellenségek életerejének tárolása, stb... A tömbök menthetők és megnyithatók a Save Array és Load Array parancsokkal.

 

(Ilyenkor, a tömböknél jönnek elő a klasszikus algoritmusok leírásai a könyvekben: keresés, logaritmikus keresés, buborékos-rendezés, stb. Nem érzem szükségesnek, hogy ismertessem ezeket, messze áll ez a Dark Basictől.)

 

11. Data sorok

 

A Data sorok adatok elraktározására valók. Olyan adatokat érdemes beléjük pakolni, amik egyediek, és sokáig tartana megadni őket egyenként. Képzeljük el úgy, mint egy programon belüli adatblokkot. A Data sorokba írt adatokat nem tudjuk változtatni, csak változókba olvasni. Törölni, újakat létrehozni nem lehet és nem is szükséges; programozás közben beleírjuk a programba, és kész. A programunk meg kiolvassa a Read paranccsal. Nézzünk egy példát:

 

do

inc y,20

read szin

read a$

if a$="vége" then end

if szin=1 then ink rgb(255,255,255),0

if szin=2 then ink rgb(255,0,0),0

if szin=3 then ink rgb(0,255,0),0

center text 320,y,a$

loop

 

data 2,"A KÉSZÍTŐK NÉVSORA",3,"Programozók:",1,"Kis István",1,"Nagy József"

data 1,"Közepes Károly",1,"Mester Mihály"

data 3,"Grafikusok:",1,"Pixel Pista",1,"Textúra Tamás",1,"Vertex Viktor"

data 3,"Hangok:",1,"Effekt Ferenc",1,"Szinkron Szilvia"

data 2,"Külön köszönet az önkéntes tesztelőknek!"

data 1,"vége"

 

Egy data sorban több adat is eltárolható, ezeket vesszővel kell elválasztani. Mindegy, milyen típusú az adat, békésen megférnek egymás mellett a számok és a szövegek. Az adatok kiolvasását a READ parancs végzi. A kiolvasás sorban (lineárisan) folyik. Először a szin változó kap egy számot, azután az a$ egy szöveget. Így helyezkednek el a data sorokban is az adatok: szám, szöveg, szám, szöveg, ...

 

Több új utasítás is van ebben a programban. Nézzük sorban:

INC: a változó értékét növeli a megadott számmal. Ha nem adunk meg számot, 1-et ad a változó értékéhez. Tehát az inc y,20 sor megegyezik egy y=y+20 sorral. Az inc párja a DEC. A dec csökkenti a változó értékét az inc-hez hasonlóan.

READ: a soron következő adatot a megadott változóba tölti. Ha nincs több adat, hibát jelez. Ennek kivédésére két út van:

1, Számlálós ciklust használunk

2, Az utolsó adat jelzi az adatfolyam végét, és kilépünk a ciklusból. Itt ezt a módszert használjuk.

INK: A rajzolási színt állítja be. Alakja: INK Rajz_szín,Háttérszín . Színt az RGB függvénnyel adhatunk meg, vagy közvetlen színkóddal.

RGB(): Az RGB-vel egy szín vörös, zöld és kék (Red-Green-Blue) összetevőjét adhatjuk meg. Ebből a három színből keveri a monitor a többit. Egyes színek erősségét 0-tól 255-ig állíthatjuk be. Az RGB(255,255,255) a fehér, RGB(0,0,0) a fekete. Színkeveréskor az additív színkeverés szabályai érvényesülnek, tehát pl. vörösből meg zöldből sárga lesz. A függvény visszatérési értéke egy színkód, 0 és 16777215 között.

CENTER TEXT: szövegkiírás, a megadott x és y koordinátákra kerül a szöveg közepe.

 

Ha ugrálni szeretnénk a data sorok között, akkor jól jön a RESTORE parancs. Ez a parancs beállítja, hogy honnan kezdje a READ a kiolvasást. Használata hasonlít a GoTo-éra: Restore /címke/. A címkét meg a data sorok közé kell helyezni. Példa:

 

restore grafikusok

do

inc y,20

read szin

read a$

if a$="vége" then end

if szin=1 then ink rgb(255,255,255),0

if szin=2 then ink rgb(255,0,0),0

if szin=3 then ink rgb(0,255,0),0

center text 320,y,a$

loop

 

data 2,"A KÉSZÍTŐK NÉVSORA",3,"Programozók:",1,"Kis István",1,"Nagy József"

data 1,"Közepes Károly",1,"Mester Mihály"

grafikusok:

data 3,"Grafikusok:",1,"Pixel Pista",1,"Textúra Tamás",1,"Vertex Viktor"

data 3,"Hangok:",1,"Effekt Ferenc",1,"Szinkron Szilvia"

data 2,"Külön köszönet az önkéntes tesztelőknek!"

data 1,"vége"

 

Leggyakrabban pálya és mátrix adatok, betöltendő fájlnevek letárolására használjuk a Data sorokat.

 

12. Újdonságok a DBPro-ban

 

A Dark Basic BASIC alapszavaival már megismerkedtünk. A DBpro több változást hozott e téren is, nézzük a fő újításokat!

 

a, Select ... Case szerkezet

 

A Select ... Case szerkezet egy elágazás-típus. A select paranccsal kezdődik, ami után odaírjuk a megvizsgálandó változót. Ezután jönnek a case-endcase parancsok, amik a tulajdonképpeni elágazások. A case és endcase közti sorokat akkor hajtja végre a gép, ha a case után írt érték egyezik a változó értékével. A szerkezetet az endselect parancs zárja. Egy példán keresztül érthető lesz:

 

do

input szam

select szam

   case 1

      print "egy"

   endcase

   case 2

      print "kettő"

   endcase

   case 3

      print "három"

   endcase

   case 4

      print "négy"

   endcase

   case 5

      print "öt"

   endcase

   case default

      print "A szám nincs egy és öt között"

   endcase

endselect

loop

 

A case defaultnál akkor teljesülnek a parancsok, ha egyik case parancs melletti érték se volt egyenlő a változóéval.

 

b, Változódeklarálás

 

Mint írtam, a DB három változótípust tartalmaz: egész, valós, string; a valós és string számoknál típusjeleket kell használni. A DBpro-ban lehetőségünk van deklarálni, azaz saját kezűleg létrehozni a változókat, és így elhagyni a típusjeleket. A deklarálást az AS paranccsal végezhetjük. Használata:

 

változónév AS típus

 

Nézzük milyen típusok vannak:

1, byte: 1 bájtos változó, ennélfogva csak 0 és 255 közötti számokat tárolhat. Itt álljunk le egy pillanatra! Mi történik, ha nagyobb értéket rakunk bele? Nézzük meg!

 

bajt as byte

bajt=400

print bajt

bajt=-50

print bajt

do : loop

 

A program kiír két számot:

144

206

Hogy lett a 400-ból 144? Vegyük elő a Windows számológépet, állítsuk tudományosra! Gépeljük be 10-es számrendszerbeli számként a 400-at, és váltsuk át binárisra! Az eredmény: 110010000 Igen ám, de ez 9 szám, ami 9 biten fér el, egy bájt meg 8 bites, így nem fér bele. Ezt hívják túlcsordulásnak. A DB a problémát úgy oldja meg, hogy csak az utolsó 8 számjegyet rakja be a változóba, ami 10010000. Írjuk be az 10010000-t binárisan, és váltsuk át decimálisra! Az eredmény 144. (400-256=144) Ugyanez a helyzet a -50-nél.

 

2, word: 2 bájt, 0 és 65535 között.

3, dword: Duplaszó, 4 bájt, 0 és 4 294 967 295 között

4, integer: A jól ismert egész típus is 4 bájtos, csak az első bitjén előjelet lehet tárolni. A számnak 31 bit jut, így - 2 147 483 647 és + 2 147 483 647 közötti értékek mehetnek bele. (Ez ugyanannyi szám, mint amennyit a dword tud, csak a fele minuszos.

5, double integer: 8 bájtos változó, határa 9223372036854775807-ig terjed pluszban és minuszban is.

6, float: A jól ismert # jelű lebegőpontos valós számunk. Az 1.052-es verziójú DBPro-n ezt csinálja:

 

a#=1.01

print a#

do : loop

 

A program eredménye: 1.00999999046 !!! Ez van, ezt kell szeretni. Majd javítják később...

7, double float: A float nagytestvére. Hasonlóképpen pontatlan.

8, string: A szokásos szöveg.

(És végül itt a kakukktojás :)

9, boolean: logikai változó, értéke 0 (hamis) vagy 1 (igaz) ... lenne, de 1 bájton tárolja az adatokat, tehát 0 és 255 közötti számok mehetnek bele, és megegyezik byte típussal... Így lesz egy típusból kettő.

 

Változót deklarálhatunk simán, pl:

valtozo as byte

Függvény elején:

Function blabla(x as word,y as word)

Írhatunk elé local és global parancsokat (lásd lentebb)

global valtozo as byte

Tömböt is deklarálhatunk:

dim valtozok as byte

 

Mire is jó nekünk ez a változódeklarálás?

1, Memóriát spórolhatunk vele. Ha egy változó értéke mondjuk 10 és 20 között mozog, elméletileg fölösleges 4 bájtos integer típus neki, ha elfér egy bájton is. Gyakorlatilag a kis 3 bájtos spórolásunk csepp a tengerben, amikor a programunk több 10 MB-ot foglalhat le a memóriából a betöltött képeknek, hangoknak és 3D objektumoknak. Nagyon nagy tömböknél talán van értelme...

2, Elhagyhatjuk a típusjeleket! Csak tudjuk, melyik változó milyen!

b as string

b=256 <-Na ez már nem tetszik a DBPro-nak

3, Akár műveleteket is végezhetünk a túlcsordulás kihasználásával.

 

c, Változók hatásköre: LOCAL és GLOBAL

 

Eddig úgy volt, hogy a főprogramban létrehozott változók érvényesek voltak a főprogramban, az alprogramok (functionok) változó csak az alprogramokban, a tömbök meg mindenhol. Alapjában véve ez a DBPro-ban is így van:

 

ertekad()

print a(1)

do : loop

 

function ertekad

dim a(1)

a(1)=100

endfunction

 

A tömb értékeihez itt is hozzáfér a főprogram. De ha azt akarjuk, hogy ne férjen hozzá?

 

ertekad()

print a(1)

do : loop

 

function ertekad

local dim a(1)

a(1)=100

endfunction

 

Na ez a program el se indul, mert az alprogramban a local parancs áll a dim előtt, ami így helyi tömböt hoz létre: az a() tömb csak az ertekad alprogramban létezik. Ezért a print semmit sem tudna kiírni, a DBPro compilere felhívja a figyelmet arra, hogy nincs ilyen tömb. Akkor csináljunk egyet!

 

dim a(1)

a(1)=22

ertekad()

print a(1)

do : loop

 

function ertekad

local dim a(1)

a(1)=100

endfunction

 

Mivel az a(1)=100 a későbbi értékadó utasítás, 100-at kellene látnunk, csak hát ott a local. Az alprogram a() tömbje mostmár nem érdekli a főprogramot, mert helyi tömb. Az alprogramot se érdekli a főprogram tömbje, írogat a sajátjába. A program két azonos nevű tömböt tart nyilván ugyanabban az időben.

De tegyük fel, nem akarunk tömbökkel vesződni, egy egyszerű változót akarunk átpasszolni az alprogramunknak. Erre jó a global parancs:

 

global a

a=222

ertekad()

print a

do : loop

 

function ertekad

inc a,10

endfunction

 

A programot futtatva láthatjuk, hogy az alprogram is hozzáfér a változóhoz. Fordítva nem működik:

 

ertekad()

print a

do : loop

 

function ertekad

global a

a=10

endfunction

 

Ennyit a LOCAL és GLOBAL parancsokról.

 

d, Saját típusok deklarálása

 

Megismerkedtünk már a változók deklarálásával. Létezik 8 féle változótípus. Igazán újakat nem tudunk csinálni, de a meglévőket tudjuk kombinálni, ami nagyon hasznos dolog. Új típust a következőképpen lehet létrehozni:

 

TYPE típusnév

      változó1

      változó2

      változó3

      ...

ENDTYPE

 

Ilyenkor jól jöhel a változódeklarálás! Nézzünk egy példát:

 

TYPE ellenseg

      nev as string

      xhely as float

      yhely as float

      zhely as float

      maxelet as byte

      eletero as byte

ENDTYPE

 

De ha ugyanolyan típusú változók vannak, akkor elég csak a nevüket leírni minden típus nélkül. Akár többet is egy sorba:

 

TYPE blabla

      x : y : z : allapot

ENDTYPE

 

A fenti példában mind egész típusú változók. De nézhetne ki így is a középső sor: x# : y# : z as float : allapot <- szóval típusjelek jöhetnek a deklarált változók mellé!

Egymásba is pakolhatjuk saját típusainkat:

 

TYPE koord

      x as float

      y as float

      z as float

ENDTYPE

 

TYPE ellenseg

      nev as string

      hely as koord

      maxelet as byte

      eletero as byte

ENDTYPE

 

Így még nem tudunk mit kezdeni a típusainkkal, mert adatot csak változóba rakhatunk. Nosza, csináljunk is egyet:

fogonosz as ellenseg

 

Értékadáskor ponttal kell elválasztani a változótól az alváltozóját. Pl: (a fenti példánál maradva)

 

fogonosz.nev="Rondella szörnyella"

fogonosz.hely.x=120.4

fogonosz.hely.y=13.2

fogonosz.hely.z=234.6

fogonosz.maxelet=200

fogonosz.eletero=150

 

Mivel a hely változó koord típusú, hivatkozni kellett annak az alváltozóira is még egy ponttal.

Tömbnek is adhatunk saját típust:

dim ellensegek(100) as ellenseg

vagy akár

local dim ellensegek(100) as ellenseg

 

Értékadáskor a pont a tömb indexe (a zárójeles értékek) után kerül:

ellensegek(25).maxelet=100

 

Ajánlom a saját típusok használatát, mert nagyon megkönnyítik az adatok tárolását. A fenti ellensegek() tömb adatszerkezetét sima DB-ben minimum 3 tömbbel lehetne kiváltani.

 

e, Új parancsok a tömbök kezelésére

 

A DBPro-ban lehetőség nyílik a tömbök futásidőben való átméretezésére, így sokkal egyszerűbb a kezelésük. Tegyük fel, van egy számmal feltöltött tömbünk: 10,55,33,66,22,77. Ebből mi ki szeretnénk törölni a 4. elemet, és a többit elcsúsztatni. Dark basicben ez így nézne ki:

10,55,33,0,22,77

És ilyenkor balra kellene tolni eggyel a 22-t és a 77-et, hogy folytonosan legyenek az adatok. A DBPro-s módszer a következő: kitöröljük TELJESEN a 4. elemet (így tömbünk csak 5 elemű lesz), majd hozzáadunk egy hatodik elemet. Nézzük meg ezt:

 

dim tomb(6)

 

`Feltöltés és kiíratás

for i=1 to 6

   tomb(i)=rnd(100)

   print tomb(i)

next i

 

`Üres sor

print

`Törlés

array delete element tomb(),4

`Új elem a végére

array insert at bottom tomb()

 

`Kiíratás

for i=1 to 6

   print tomb(i)

next i

 

do

loop

 

Láthatjuk, a 4. elem eltűnt. Kétdimenziós tömböknél már nem ilyen szép a helyzet, az array delete element parancsot nem sikerült működésre bírnom. Ehhez a témához kapcsolódik a verem és a "sor" (queue), ezekről majd később írok. Ennyit a DBPro újdonságairól. Most kanyarodjunk vissza a DB-hez (is), a következő fejezetek a mindkét nyelvre vonatkoznak.

 

13. Fájlkezelés

 

Gyakran előfordul, hogy programunkkal fájlt szeretnénk létrehozni. A DB-ben ez is nagyon egyszerű. Megnyitjuk - írunk bele - bezárjuk.

 

open to write fájlszám,fájlnév

....

write [típus] fájlszám,változó

close file fájlszám

 

Ha az írandó fájl létezik, hiba van. Ezt kivédhetjük a következő sorral:

if file exist(fájlnév)=1 then delete file fájlnév

 

A fájlkezelő parancsokról majd később.

Fájlszám: Egyszerre több fájlt is megnyithatunk, a fájlszámmal meg tudjuk adni, hogy melyikbe írunk/melyikből olvasunk. A DB általános logikájától eltérően itt a fájlszám az első paraméter - ez Basic hagyomány. A fájlnév szöveges adat, ami elérési utat is tartalmazhat (pl.: adatok\képek\textúra\01.jpg)

A típus alatt a write parancs különböző formáit értem:

write byte

write long

write float

write word

write string

write file (általános adattípus, ha mindegy az adattípus, használhatjuk)

write memblock (Kiír egy memblockot úgy ahogy van. A memblockok lefoglalható és közvetlen elérhető memóriaterületek)

write fileblock (lásd lentebb)

write dirblock (lásd lentebb)

A Close file lezárja a fájlt.

 

Az olvasás sem túlbonyolított:

 

open to read fájlszám,fájlnév

....

read [típus] fájlszám,változó

close file fájlszám

 

A read után állhat byte, long, float, word, string, file, memblock, ugyanúgy, mint a Write parancsnál. Olvasás

A fájlok beolvasása szekvenciális (sorfolytonos), azaz csak egymás után olvashatjuk az adatokat. Azt azért jó lenne tudni, vége van-e a fájlnak. Ezt a File end() fügvénnyel tudhatjuk meg: 1-et ad vissza, ha véget ért.

 

DBPro-ban lehetőségünk nyílik nem szekvenciális fájlelérésre a Write byte to file, Read byte from file és a Skip bytes parancsokkal.

Nézzünk egy egyszerű példaprogramot:

 

print "Írás:"

if file exist("blabla.txt")=1 then delete file "blabla.txt"

open to write 1,"blabla.txt"

for i=1 to 10

      a=rnd(300)

      write word 1,a

      print a

next i

close file 1

 

print "Olvasás:"

open to read 1,"blabla.txt"

for i=1 to 10

      read word 1,a

      print a

next i

 

do:loop

 

Különleges adattípus a fileblock. A fileblockok írásával több létező fájlt lehet összerakni egyetlen nagy fájllá összerakni, így mások nem férhetnek hozzá a programunk adataihoz. A következő program létrehoz egy fileblockot:

 

open to write 1,"fileblock.dat"

write fileblock 1,"1.bmp"

write fileblock 1,"2.bmp"

close file 1

 

Így egy fájlba kerül a két bmp. A kicsomagoló programot egyszerű lesz megírni, csak a write parancsokat kell readra cserélni:

 

open to read 1,"fileblock.dat"

read fileblock 1,"1.bmp"

read fileblock 1,"2.bmp"

close file 1

 

Ha már létezik egy kicsomagolandó fájl, nem írja felül a DB.

A dirblock hasonló ehhez, egy egész könyvtárat lehet fájlba írni vele. Ügyeljünk arra, hogyha csak egy fájlt csomagolunk ki a dirblock egy alkönyvtárából (a read fileblock paranccsal) akkor a DB elkészíti az alkönyvtárakat és azokba rakja a fájlt.

 

A DB fájlkezelő parancsai és függvényei egyértelműek: Copy file, Rename file, Execute file, Set dir, Get dir$(), stb; nem részletezem őket. Problémát csak az okozhat, hogy le kell kérdeznünk a könyvtárszerkezetet. Ezt checklistekkel csinálhatjuk meg. (A checklist parancsok leírását a súgó 'Rendszer' részében találhatjuk meg. Példa:

 

perform checklist for files

for i=1 to checklist quantity()

      print checklist string$(i)

next i

 

A lista első eleme (.) az aktuális könyvtárat jelöli, a második (..) a szülőkönyvtárat. Van egy megkötése a checklisteknek: Max. 255 elem, jelen esetben fájl fér bele. De hát programunk nagy valószínűséggel nem a Windows System könyvtárában fog matatni... Ennyit a fájlokról.

 

14. Szöveg minden mennyiségben

 

Már megint szövegekről lesz szó. Amit eddig tudunk róluk: print-tel kiírhatjuk, set cursorral beállíthatjuk, hova ír a Print, vannak szöveges változók, meg előfordult már itt egy center text parancs. Na itt az idő, hogy írjak a többiről is.

Szöveget - a print mellett - a text paranccsal írathatunk ki:

text x_koord,y_koord,szöveg

Az x,y koordinátákra a szöveg bal felső sarka kerül, a center textnél a szöveg közepe. A koordináták határait a képernyő felbontása határozza meg, ami alapból 640*480.

Vannak még itt mindenféle parancsok, amikkel el lehet játszadozni:

 

SET TEXT FONT Betűtípus beállítás. A Perform Checklist For Fonts paranccsal nem árt ellenőrizni, a rendszerben van-e az adott betűtípus. Lehet, hogy nálunk igen, de másnál nem biztos ez

SET TEXT SIZE Méretbeállítás

SET TEXT OPAQUE Nem átlátszó szövegháttér

SET TEXT TRANSPARENT Atlátszó szövegháttér

SET TEXT TO NORMAL Mormál betűstílus

SET TEXT TO ITALIC Dőlt betűk

SET TEXT TO BOLD Félkövér betűk

SET TEXT TO BOLDITALIC Félkövér dőlt betűk

stb. Példa:

 

ink rgb(255,255,0),rgb(0,0,255)

set text font "tahoma"

set text size 48

set text opaque

set text to bold

text 0,0,"Kék alapon sárga szöveg"

do:loop

 

Most írjunk egy olyan programot, ami egyre nagyobb betűkkel ír ki egy szöveget! Először is függvények kellenek a stringek darabolására:

left$ szöveg,szám - megadott számú karakter beolvasása a szöveg elejéről

right$ szöveg,szám - megadott számú karakter beolvasása a szöveg végéről

mid$ szöveg,hely - egy karakter beolvasása a megadott helyről <-na ez kell nekünk

Meg kellenek még a text width és text height függvények, amik visszaadják a szöveg szélességét és magasságát. Jól jön még a szöveg hosszát visszaadó LEN is:

 

set text font "Verdana"

szovegkiir(100,100,"Nőnek a betűk!")

do:loop

 

function szovegkiir(x,y,sz$)

meret=10 : szamlalox=0

for c=1 to len(sz$)

      a$=mid$(sz$,c)

      set text size meret

      text x+szamlalox,y-(text height(a$)/2),a$

      inc szamlalox,text width(a$)

      inc meret,2

next c

endfunction

 

A program betűnként írja ki a szöveget, ezért minden betűt eltolva kell írni, hogy ne egymás tetejére írjuk őket. Az eltolást a szamlalox változó tárolja. Ezzel be is fejeztük a szövegeket. Van még több szövegkezelő parancs is, de nem írom le, úgyis benne vannak a súgóban.

 

15. Bevitel kezelése

 

Az eddigi programok nagy része elindult, lefutott, és semmi dolga sem volt a felhasználónak (esetleg F12-t nyomni a végén). Azonban a programok 95%-ában a felhasználónak lehetősége van beleavatkozni a program futásába. Játékoknál meg egyenesen kötelező, hogy a játékos irányítson. Eközben a játékos beviteli eszközökön matat (billentyűzet, egér, joystick), tehát kellenek beviteli parancsok és függvények, hogy kezelni lehessen ezeket.

 

Az INPUT paranccsal már találkoztunk, de nem várhatjuk el a játékostól, hogy egy FPS játékot úgy játsszon le, hogy "fordulj balra" vagy "lőjj" parancsokat gépel be folyamatosan. Mostmár nézzük, hogy mit csinálhatunk a DB-ben a billentyűzettel:

 

SUSPEND FOR KEY, WAIT KEY: Ezek a parancsok felfüggesztik a program futását addig, amíg le nem nyomunk egy billentyűt.

Vannak itt még speciális gombokhoz függvények:

Upkey() : felső kurzornyíl

Downkey() : alsó kurzornyíl

Leftkey() : Bal kurzornyíl

Rightkey() : Jobb kurzornyíl

Controlkey() : Ez a Control véletlenül

Shiftkey() : Ez meg a Shift :)

Returnkey() : Enter

Escapekey() : Esc gomb. Alapból ki lehet lépni a programból vele. Hogy programozni tudjuk, le kell tiltani az automatikus kilépést a Disable Escapekey paranccsal.

 

A többi gombra ott van keystate() függvény. Egyet ad vissza, ha le van nyomva a vizsgált gomb, 0-t, ha nem. Az, hogy melyik gombot vizsgáljuk, azt a gomb scan kódjával adhatjuk meg. És scan kódot honnan szüljünk? Háááááttttt... Nézzük meg a lenti példát:

 

x=320

y=240

sync on

 

do

cls

set cursor 0,10

print "Try using ARROW KEYS, CONTROL, SPACEBAR, SHIFT and RETURN"

print "Try pressing the RIGHT CONTROL KEY to flag Keystate 157"

print "Inkey$()=";inkey$();"  Scancode=";scancode();"  Keystate(157)=";keystate(157)

 

if upkey()=1 then y=y-1

if downkey()=1 then y=y+1

if leftkey()=1 then x=x-1

if rightkey()=1 then x=x+1

if controlkey()=1 then ink rgb(255,0,0),0

if shiftkey()=1 then ink rgb(0,255,0),0

if spacekey()=1 then ink rgb(0,0,255),0

if returnkey()=1 then ink rgb(64,0,64),0

if escapekey()=1 then end

circle x,y,20

sync

loop

 

Ez a Dark Basic egyik gyári példája, ami kitűnően bemutatja a billentyűzetkezelést. Ki is írja a lenyomott billentyű scan kódját.

Pl. a Delete billentyű scan kódja 211, ezért a keystate(211) függvény akkor ad vissza egyet, ha a del-t megnyomjuk.

Az Inkey$() a lenyomott billentyűhöz tartozó ascii kódot adja vissza. Betűk figyelésére lehet alkalmazni:

 

do

a$=inkey$()

if a$="g" then print "g"

loop

 

Ha billentyűlenyomáskor egy eljárást szeretnénk hívni, ezt akkor érdemes megtenni, amikor a felhasználó elengedte a billentyűt. Ilyenkor egy változót kell alkalmazni a billentyű figyelésére:

 

do

if ctrlfigyel=0 and controlkey()=1 then ctrlfigyel=1

if ctrlfigyel=1 and controlkey()=0

      ctrlfigyel=0

      print "Lenyomtad a Control-t"

      endif

loop

 

Na és akkor nézzük az egérkezelést! Itt kevesebb gomb van, mint a billentyűzeten, de cserébe tudjuk mozgatni az egeret. A hide mouse parancs elrejti, a show mouse parancs megjeleníti az egérkurzort. Az egér elmozdulását lekérdezhetjük a mousemovex() és mousemovey() függvényekkel. A mousex() és a mousey() az egér koordinátáit adják vissza. A position mouse paranccsal beállíthatjuk az egér helyét. Itt egy újabb példaprogram, egy fordított egérkezelést végez, a kurzor ellentétes irányba megy mindig:

 

sync on : sync rate 0

mx=320

my=240

position mouse mx,my

 

do

x=mousemovex() : x=0-x : inc mx,x

y=mousemovey() : y=0-y : inc my,y

position mouse mx,my

sync

loop

 

A program az egér elmozdulását az x és y változókba gyűjti. A változók értékét kivonja 0-ból, így pluszból minusz lesz és viszont. A "kifordiított" változókat hozzáadja mx-hez és my-hoz, majd az mx;my koordinátákra állítja be az egeret. A sync on, sync rate és sync parancsokról a következő fejezetben lesz szó. A program megvan nélkülük, de gyorsabb velük. Az egérgombokat könnyű programozni, a mousex() függvény visszaadja, melyik van lenyomva. Minden gombnak van egy értéke:

1. gomb: 1

2. gomb: 2

3. gomb: 4

4. gomb: 8 Általában 3 gombos egy egér...

stb.

Ezeket az értékeket össze lehet adni. Ha pl. az első és a harmadik gomb van lenyomva, akkor 4+1=5-öt ad vissza a mouseclick() Példa:

 

do

if mouseclick()=1 then print "Bal gomb"

if mouseclick()=2 then print "Jobb gomb"

if mouseclick()=3 then print "Mindkét gomb"

loop

 

Ezzel megismertük az egér kezelését is, a joystickról nem írok, mivel nincs joystickom.

 

Összegzés

 

Véget ért a segédlet 1. része, ami magával a Basic nyelvvel foglalkozott. Remélem már az is ért valamit a programozáshoz, aki eddig semmit sem értett. Ez a fejezet magát a Basic nyelvet ismertette, a vezérlési szerkezeteket (ciklusok, elágazások, alprogramok, ugrások). Bár van benne néhány témakör, ami eléggé egyedi DB parancsokat tartalmaz, de az ismeretek nagy része hasznosítható a Basic többi "nyelvjárásában" is. Eddig rövid programocskákat írtunk csak, parancsok bemutatása céljából. De már ismerünk annyi parancsot, hogy kicsit összetettebb programokat is tudunk írni... Grafikáról még nem nagyon esett szó. A Dark Basic "basic" része ezennel kitárgyalva, ezután már a "dark rész" következik - grafika 2D-ben és 3D-ben - ha lesz kedvem gépelni. Köszönöm az eddigi kitartást!

 

Írta: Várkonyi Tibor - Minden jog fenntartva
Mindenféle másolás/közzététel csak a szerző engedélyével
Gépcsirke központ, 2004-2005
Web: gepcsirke.atw.hu - E-mail: gepcsirke@atw.hu