A megvalósítás lépései

Könyvtár szerkezet kialakítása

Első lépésként, mint eddig bármikor, hozzunk létre egy alkönyvtárat „ASM_027_WinGraphicsComplex” néven, a solution …\ITM_Asm_Sources\VS2010_Asm_Solution alkönyvtár alatt, ahova egy PSD alkönyvtárba másoljuk a korábban létrehozott garfikus elemeket.

17-10. ábra -

kepek2/5fejezet_17-10.jpg


Ebben az alkönyvtárban kaphat helyet a Photoshop-os fejlesztés is a kimenetként keletkező png és bmp fájlokkal. Fontos, hogy az egyes korábban elkészített rajzi elemek bmp formátumú változatát is előállítsuk, mert programunkban ezeket fogjuk erőforrásként bejegyezni. Ha ezekkel ez előkészületekkel elkészültünk, akkor hozzunk létre egy hagyományos assembler projektet a ..\ITM_Asm_Sources\VS2010_Asm_Solution\ASM_027_WinGraphicsComplex alkönyvtár kiválasztásával, ahol a Psd alkönyvtárunk is található. Talán most válik érthetővé, hogy eddig miért készítettünk mindig egy látszólag felesleges alkönyvtárat, melynek a projektnevet megelőző „Win” előtagja volt. A valódi fejlesztések ugyanis nem csak a projektből állnak, hanem számtalan más típusú de a projekthez szorosan csatlakozó állományból is, mint pld. a Psd fájlok, a megrendelések, specifikációk, levelezések stb. Ha készítünk a projekt fölé egy ilyen „hosztoló” alkönyvtárat, akkor módunk lesz ezeket a projekt kódolásával „egységbe zárva” kezelni.

A projekt induló állományai és a modularitás

Mint eddig, most is a projekt létrehozásakor készítünk egy induló állomány „garnitúrát”, amit később a jelenlegi projekt sajátosságai szerint módosítunk. Azok a szempontok, melyek a template állományhoz képest létrehozott változtatásokat indokolják, elsősorban a projekt várható mérete és bonyolultsága. Ha egy projekt üzleti logikája egy bizonyos szintnél összetettebb, akkor már az indulásnál érdemes törekedni a lehetőség szerint az üzleti logika funkcionalitásait, nagyobb elvi egységeit modulokba gyűjtő megközelítésre. Hogy mi az bonyolultság, ami után indokolt funkcionális és moduláris szervezés, azt nem könnyű meghatározni.

Durva közelítéssel azt lehet mondani, hogy ha az üzleti logika legalább két rétegű, (például tartalmaz vezérlő réteget, mely hív ez alatti szervezésű kódot, (mondjuk rutin gyüjteményt) akkor már minden réteg elemet célszerű a kódolásban elkülöníteni. Ebben a projektben felismerjük, hogy a megjelenítési réteg és a játék szabályainak kezelése, valamint a játék adatainak kezelése legalább három réteget jelent a kódolás számára. Nagyvonalúan úgy dönthetünk, hogy a megjelenítés, a szabályok és az adatok kezelése átláthatók még azok include csoportokba szervezésével és ún. „beszélő fájl nevekkel”. Ezért nem víve túlzásba a modularitást, a fenti utat fogjuk követni.

17-11. ábra -

kepek2/5fejezet_17-11.jpg


A létrejövő üres projektet „Build Customisation” menüponttal és a szeinti jelölő négyzettel masm források fordításához állítjuk be.

17-12. ábra -

kepek2/5fejezet_17-12.jpg


17-13. ábra -

kepek2/5fejezet_17-13.jpg


Most, mint eddig mindig, létrehozunk egy induló „template állományt” a projekthez, egy a projekt nevéből képzett ASM_027_GraphicsComplex.asm nevű asm kiterjesztésű állományt adva.

17-14. ábra -

kepek2/5fejezet_17-14.jpg


Ez lesz az alkalmazás fő programja, és ebbe másoljuk a ASM_021_WinBaseConsoleHosted.asm állomány tartalmát.

Forráslistánk nagyon hasonlít az eddig használt ASM_021_WinBaseWinMain.asm állomány tartalmára, ahol egy Windows program elindítását valósítottuk meg, de itt láthatóan van egy _Start címkével kezdődő kódrészlet, melyben az inicializációk után meghívjuk a _WinMain eljárást. Ha a programunkat konzolos paraméterekkel állítjuk be, akkor először egy konzolos ablak indul el, és ebből hívjuk majd meg a windows-os alkalmazást, vagyis ablakot. Ehhez a tulajdonság lapon a SubSystem és az EntryPoint tulajdonságokat a következők szerint kell beállítanunk.

17-15. ábra -

kepek2/5fejezet_17-15.jpg


17-16. ábra -

kepek2/5fejezet_17-16.jpg


Az első fordítás és futtatás

Ezt követően az első fordítás és futtatás azt eredményezi, amit a korábban már jeleztünk, hogy a konzolos alkalmazásból indul a windows ablak, vagyis lesz egy állandó lehetőségünk, hogy a konzolra afféle WinDbg szerűen logolási-futás közbeni üzeneteket küldjünk, hiszen minden printf hívásunk elsődleges kimenete a konzol ablak lesz.

17-17. ábra -

kepek2/5fejezet_17-17.jpg


Mivel grafikus ablakunk méretét még nem állítottuk be, ezért a főprogram (ASM_027_GraphicsComplex.asm) CreateWindowsExA paraméterezését változtassuk meg úgy, hogy a szélesség és a magasság (824+10) pixel, illetve (620+35) pixel legyen, valamint „remeljük” ki a WS_SIZEBOX stílus konstanst az alábbiak szerint:

17-18. ábra -

kepek2/5fejezet_17-18.jpg


A WndProc leválasztása include fájlba

Mivel most nagyobb méretű programot készítünk, szükséges, hogy az alkalmazás callback függvényét szintén kiemeljük a főprogram forráslistájából, és a továbbiakban egy külön include fájlban szerkesszük. Ennel neve legyen: WndProc.inc , és a szokásos include fájlok között hozzuk létre.

17-19. ábra -

kepek2/5fejezet_17-19.jpg


A főprogram forráslistájából tehát kivágjuk a _WndProc eljárást, majd azt bemásoljuk az újonnan létrehozott WndProc.inc include fájlinkba ügyelve arra, hogy az include fájl elején szerepeljen egy .code direktíva.

Ezzel egyidőben a főprogramban bejegyezzük ez új include fájlt a megfelelő helyen.

A projekt induló állományai és a modularitás fejezetcím alatti elveknek megfelelően mostantól kezdve az egyes modulokat azok funkcionális egységei szerint elnevezzük, és az elnevezések szerinti include fájlokban kódoljuk. Tekintsük most meg, mintegy előretekintve, hogy a fenti meggondolások a megvalósuló projektben milyen tényleges fájlokat fognak eredményezni.

17-20. ábra -

kepek2/5fejezet_17-20.jpg


Az egyes include fájlok elnevezésénél bevezetünk egy GP_xx_ előtagot, mely egy, az elkészítés sorrendjét nagyjából követő sorszámot is fog tartalmazni. Ezek közül már néhány létezik, hiszen korábban már kiemeltük a WndProc eljárásunkat. A következőkben létre kell hoznunk a minden modulból elérhető adatokat tartalmazó GP_00_GlobalisAdatok.inc include filie-t. Ennek és következőkben elkészítendő állománynak persze elérhetőnek kell lennie a főprogram számára is, ezért minden egyes új include fájl esetében, annak hivatkozását az ASM_027_GraphicsComplex.asm, a főprogramot tartalmazó modulban is el kell helyeznünk. Tekintsük ezt is most meg úgy, ahogyan ez majd a már kész projektben szerepelni fog.

További igen fontos szerkesztési szempont, hogy ezeknek az include fájloknak a fordítási, feldolgozási sajátosságai miatt egyáltalán nem mindegy a sorrendje. Ha egy hivatkozás például egy későbbi sorrendű include fájlban található, akkor a fordító rigorózus méltatlankodással hiba üzeneteket küld, ami megkeserítheti a programozás amúgy izgalmas élményét.

A globális adatok include fájl

Az következő lépés tehát a globális adatok include fájljának megszerkesztése. Mivel azt tűzzük ki célul, hogy ezeket az adatokat minden réteg és modul egyaránt „látni” fogja, ezért közvetlenül a runtime (masm32tr.inc) és a korábban készített ASM_025_WinMacro.inc makró gyüjteményünk után helyezzük el. Milyen adatokat is helyezünk el a globális adatok között? Hozzunk létre egy új include állományt a fentiek szerinti GP_00_GlobalisAdatok.inc néven, és helyezzük el ebben az eredetileg a főprogram adat szekciójában lévő adatokat, melyek az alábbiak:

Ezt megelőzően egy a játéktér koordinátáit meghatározó WORDPOINT struktúrát hozunk létre melyet a későbbiekben fogunk használni.

Már most törekszünk arra, hogy a projekt jellemzően összetartozó adatait lehetőleg egy adatstruktúrában fogjuk össze, ezért létrehozunk egy, az alábbi definíciónak megfelelő struktúrát.

Logikailag úgy képzeljük el a játékteret, mint egy cellákból álló sor és oszlop kombinációt, ahol egy cellát több tulajdonsága jellemez. Nyilvántartjuk a cella logikai koordinátáit, CellXPos,CellYPos ezekkel a játék üzleti logikájában számolunk majd, és nyilvántartjuk a cella megjelenítésének (grafikus) koordinátáit CellLeftPos,CellTopPos is, hogy azokat ne kelljen az alkalmazás során a megjelenítésnek mindig újra kiszámolni. A legfontosabb jellemzője pedig egy cellának az aktuális tartalma, a Content, mely az éppen a cellában lévő állapotot mutatja.

Űzleti logikánk úgy fog működni, hogy a játéktér adott celláinak logikai tartalmát frissítjük az új és új állapotokkal, (például egy cellára lépés felülről, alulról, jobbról vagy balról), majd egy másik eljárásban frissítjük az összes logikai cella tartalma alapján a megjelenítést egy gyors hurokban. Az előzőekből talán már világos, hogy a fenti CA_Structure-ből „fel kell építenünk” az egész játékteret, olyan sor és oszlop számmal, mint amit korábban a grafikai tervben elhatároztunk. (25x25-es mátrix). Az egész játéktér logikai adatainak egy struktúra lenne a legmegfelelőbb adattípus, mert nem csak a cellákat, hanem az aktuális cella koordinátát, sőt a megelőzőt is tárolnunk kellene. Tehát a játéktér struktúrája az alábbi:

Itt sajnos szükséges egy kis „csalás”, mert a korábban már említett híres A2177 hiba, ami a bennfoglalt struktúrák megadását teszi lehetetlenné, arra kényszerít minket, hogy a cellák mátrixát ne a CA_Structure többszörös dup egymásba ágyazásával oldjuk meg, hanem egy azonos adattartamú, de explicite megadott bájt sorozattal helyettesítsük. Emellett még felvesszük a CurrentPoint, valamint az utolsó előtti pontok koordinátáit, mégpedig nem egy struktúrában, hanem egy - egy külön tagban. Az előbbiekben három definíciót adtunk meg, és most van itt az idő, amikor a tényleges helyfoglalást is (vagyis az adatváltozókat) létrehozzuk.

A játékteret logikailag megvalósító helyfoglalás tehát tartalmaz egy GA_Structure-t, ami a korábbi definicók szerint tartalmazza a GameMatrix-ot és az adminisztrációhoz szükséges koordináta pontokat. A GP_00_GlobalisAdatok.inc állomány jelenlegi tartalma tehát:

Ne feledkezzünk meg a .code direktíva elhelyezéséről az adatokat tartalmazó include fájl végén.

Természetesen, ha itt megadjuk ezeket a változókat, akkor a főprogramban már nem szerepelhetnek, viszont szükséges a főprogramban ezt az új GP_00_GlobalisAdatok.inc include fájlt is megadnunk. Vagyis a főprogram most a kiemelt WndProc és a GP_00_GlobálisAdatok.inc létrehozása és a beillesztett ASM_026_WinMacro.inc után a következő:

A próba fordítás után látszólag nem változott semmi, de mi már tudjuk, hogy mostantól a globális adataink helye a GP_00_GlobalisAdatok.inc fájl.

A prototípusok include fájl

A projekt megvalósítása során az egyes logikailag elkülöníthető feladatokat, részfeladatokat, illetve az egyes ismétlődő alkalmazású funkciókat eljárásokba szervezzük. Azt már korábban is láttuk, hogy ezek sikeres befordításához, ha az eljárások „teste” nem előzi meg a hívást, akkor ún. „prototípus deklarációt” kell alkalmazni. Ez, mint azt korábban már láttuk, tulajdonképpen az eljárás fejléce, a paraméterekkel úgy is szokták mondani, hogy „szignatúrája” és a függvény kezdetétől az különbözteti meg, hogy a PROC direktíva helyett a PROTO direktiva szerepel.

Mivel az egyes include fájlokban esetenként több eljárás is helyet kaphat, ezért maguknak az eljárásoknak a szignatúráit egy külön include fájlban helyezzük el. Vagyis mostantól valahányszor bővítjük az include fájlok számát, akkor azonnal a főprogramban ezt az include direktívával és a fájl névvel jelezzük, valamint, ha az include fájlon belül egy új eljárást készítünk, akkor arról egy sorban a prototípus include fájlban készítünk egy proto bejegyzést. Legyen ennek a prototípusokat tartalmazó fájlnak a neve GP_00_GlobalisPrototípusok.inc. Ha létrehoztuk, akkor legyen ennek két jól elkülönített része, amelyben a saját prototípusainkat, illetve a c runtime könytvtár prototípusait adjuk meg.

Lélekben készüljünk fel, hogy az idegen gyártású eljárások használata esetén lehetséges, hogy olyan eljárás nevet választunk, mely már szerepel az alkalmazni kívánt statikus könyvtárban. Ilyenkor jön a „redefinition error”, de ez minket nem fenyeget, mert, mint látható, a mi eljárásaink mind GP_xx_ előtaggal elnevezve készülnek (egyébként éppen ezért is).

Persze ennek megfelelően a főprogramban az új include fájlt, mint korábban is, szükséges bejegyezni, mint azt a főprogram aktuális részletén láthatjuk.

Az erőforrások befoglalása

A projektünk számos előre definiált és létrehozható adatot és adatelemet tartalmaz majd, melyeket a windows koncepciójának megfelelően egy külön erre a célra létrehozott szoftver technológiai entitás, az erőforrás kezelés támogat. Az elegáns elnevezés ellenére nagyon egyszerű dologról van szó, valójában a képeket, a sztringeket, az ikonokat, stb. egy külön, a tervezéskor rendelkezésre álló nyilvántartási rendszerben szerkeszthetjük,és a program kódolásában ezekre egyszerűen hivatkozhatunk. Mi most azt az egyszerű módszert alkalmazzuk, hogy a Resource mappához hozzáadunk egy rsrc.rc nevű, egyelőre üres erőforrás fájlt a projekt Add menüpontjával, melynek eredményeképpen a Resource mappában létre jön a szerkeszthető erőforrás fájl.

17-21. ábra -

kepek2/5fejezet_17-21.jpg


17-22. ábra -

kepek2/5fejezet_17-22.jpg


Vegyük sorra, milyen erőforrásokba szervezhető adatokat szeretnénk használni a projekt során. Amikor egy lépéssel a lehetséges irányokban egy cellába érünk, azt az adott irány szerinti érkezés képével jelezzük, úgy, hogy cella területére „berajzoljuk” az adott bitmap-et. Attól függően, hogy milyen irányból érkezünk, ez lehet fentről-le, jobbról-balra, alulról-fel, illetve balról jobbra.

17-23. ábra -

kepek2/5fejezet_17-23.jpg


Ezeknek az érkezési irányoknak a szemléltetésére korábban már legyártottuk a megfelelő képeket, melyeket egy külön alkönyvtárban tárolunk. Ha egy kicsit alaposabban átgondoljuk, akkor rájöhetünk, hogy a jobbról-balra, illetve a balról-jobbra irányjelzések képei lehetnek ugyanazok, hiszen csak a pozicionálásukban térnek el (a szerint sárgával jelezve).

17-24. ábra -

kepek2/5fejezet_17-24.jpg


Ugyanígy a fentről-le és lentről-fel irányok is egy bitmap-et igényelnek, ezért az irányok jelzésére mindössze két képet fogunk használni, melyek rendre a következők: HJB.bmp (haladás jobbra-balra) és a HFL.bmp (haladás fel le). Szükségünk lesz még a kezdőpont és a végpont rajzi elemeire, valamint a teljes játékteret adó háttérképre.

17-25. ábra -

kepek2/5fejezet_17-25.jpg


A program használatát úgy terveztük meg, hogy legyen lehetséges véletlenszerű akadályokkal nehezíteni a célbaérést, ezért még egy rajzi elem szükséges, ez pedig az akadály (obstacle.bmp). A grafikus erőforrások tehát rendelkezésre állnak, és létrehoztuk az erőforrás fájlt is. Az erőforrások windows-os kezelése azon alapszik, hogy ezeket a linker egy erőforrásfordítás után összefűzi a keletkező .exe programmal, de nem tölti be az indításkor, csak ha kifejezetten hivatkozás történik rájuk. Ahhoz, hogy az erőforrás fordító és a linker megtalálja, ezeket a fejlesztés forráslistákat tartalmazó alkönyvtárában célszerű elhelyezni. Másoljuk a Psd alkönyvtárban előkészített grafikus erőforrásainkat a forráslistákat tartalmazó alkönyvtárunkba.

17-26. ábra -

kepek2/5fejezet_17-26.jpg


Nyissuk meg az rsrc.rc fájlunkat,és másoljuk bele az alábbiakat.

Hogy lássuk, mit is ténykedik az erőforrás fordító, nyissuk meg a projekt tulajdonságok szerkesztését, és Resource csomópont alatt állítsuk be a Show Progress és a Suppress Startup Banner-eket a következő ábrán látható módon.

17-27. ábra -

kepek2/5fejezet_17-27.jpg


Az ezt követő fordítás és futtatás után azt látjuk, hogy a projekt feldolgozta az erőforrás fájlunkat, mert ha rákattintunk az rsrc.rx fájlra a projektfában, akkor megmutatja, hogy az egyes erőforrások milyen azonosítókkal érhetők el.

17-28. ábra -

kepek2/5fejezet_17-28.jpg


Amit ilyenkor látunk, az az ún. erőforrás „nézőke” vagy szakszerűbben ResourceView. Mostantól lesz lehetőségünk az erőforrásainkat elérni, de csak akkor, ha az egyes IDBM_ előtaggal ellátott képazonosítóknak egy egy egyedi „kulcsot” adunk. Vagyis például az IDBM_BACKGROUND azonosítójú erőforrást egy az IDBM_BACKGROUND EQU 5000 hozzárendelés tesz használhatóvá. Ennek az Erőforrás azonosító <-> Numerikus azonosító kulcs-érték pának a megadására több lehetőség is van. Mi most a korábban létrehozott GP_00_GlobálisAdatok.inc fájlunkat fogjuk használni, és rögtön kibővítjük az összes, még később szükségessé váló konstans definícióval. Nyissuk meg tehát a GP_00_GlobálisAdatok.inc fájlt és másoljuk bele a következőket.

A tervezett konstans deklarációk mellett az utolsó sorba becsmepésztünk egy RandSeed nevű globális hatókörű változót, melyet majd a véletlenszám generálásnál használunk. Eredeti terveink szerint a motorháztető alatti folyamatok követésére (úgy mondjuk: logolására) a konzolos ablakban folyamatosan szöveges kijelzést valósítunk meg. Ezeknek a sztringeknek a hozzáadásához nyissuk meg a beépített erőforrás szerkesztőt és az

17-29. ábra -

kepek2/5fejezet_17-29.jpg


Add Resource menüponttal adjunk hozzá egy String Table erőforrást a New menüponttal az erőforrásainkhoz a képen látható módon.

17-30. ábra -

kepek2/5fejezet_17-30.jpg


Ezután a felkínált ID – Érték – Tartalom mezőket töltsük ki a következő ábrán látható módon.

17-31. ábra -

kepek2/5fejezet_17-31.jpg


Ezt követően több drámai változás következik be a projekt fájl szerkezetében. Lesz egy új fájl, aminek resource.h a neve, és az include csoportban tűnik fel. Ennek tartalmát módosítanunk kell az alábbiakra. Erre azért van szükség, mert a sztring táblák azonosítóit a szintén módosuló rsrc.rc fájl első sorának megfelelően a következő szerint veszi az erőforrás fordító.

Emellett még megjelennek az erőforrás (Resource Files) fülön belül a korábban felvett grafikus erőforrás elemek is.

Itt azonban az erőforrás fájl tartalmában megjelenik két olyan sor, melyet ki kell gyomlálnunk(sárgával jelölve).

Mivel ez egy kényes része, elengedhetetlen feltétele a sikeres továbbhaladásnak, a teljesség kedvéért az alábbiakban megadjuk a helyes erőforrás fájl (rsrsc.rc) és a resource.h fájl tartalmát.

Az rsrc.rc fájl az erőforrásokkal

A resource.hu fájl a sztring erőforrás konstansokkal

Persze ezeknek a sztring erőforrásoknak a kezelése is ugyanazon az elven valósul meg, mint képeké, vagyis a szimbolikus név mögé konstans definció tartozik. Ha konzekvensen használjuk az előtagokat, akkor látható, hogy az IDS_ előtagok sztring erőforrásokat, az IDBM_ előtagok pedig bittérképeket, illetve az IDB_ előtagok nyomógombokat azonosítanak.(később még lesz szó róla).

A GP_00_HatterKirajzolas.inc fájl

Ha most lefordítjuk és futtatjuk a projektet, akkor úgy látjuk, mintha nem történt volna semmi, és csak mi tudjuk, hogy ezért korábban milyen keményen megdolgoztunk. Cserébe viszont minden készen áll arra, hogy az első, már valóban „hasznos” kódunkat megírjuk, és bár az eddigi előkészületek szinte semmilyen látszattal nem jártak, bőségesen megtérülnek majd projektünk könnyű áttekinthetőségében és az erőforrások egyszerű kezelésében. A GP_00_Hatterkirajzolas.inc fájl hagyományos létrehozása után az alábbi kódot írjuk.

Ebben a kódrészletben egyenlőre két eljárás szerepel, melyek ugyanazt csinálják, csak a paraméterezésükben különböznek, vagyis kirajzolják a grafikus ablakba az alkalmazás hátterét. Itt is láthatjuk a saját függvények előtagos elnevezési módszerét a nem kívánatos ütközések elkerülése érdekében. A programban elöször „logolni” fogunk,amikor is kijelezzük a konzolos ablakban, hogy mi is történik éppen.

Ezt követően technikailag ugyanazt csináljuk, amit a következőkben részletezettekben kielemzünk. Vagyis betöltünk egy bittérképet, melyre az erőforrás azonosítójával hivatkozunk, és visszakapunk egy erre vonatkozó kezelőt, melyet elmentünk. A következő lépésben „legyártatunk” a windows-al egy olyan eszközkapcsolatot, mint amilyen az éppen aktuális ablakhoz kell. Ennek leíróját szintén mentjük, majd ebbe az újonnan létrehozott eszközkapcsolatba beemeljük a bittérkép leírójának alkalmazásával a korábban betöltött képet. Végül, mivel most a teljes hátteret rajzoltatjuk ki, ezért a 0,0 kezdőpozicióba és az ablak korábban meghatározott méretével másoljuk.

Ha ezzel készen vagyunk, akkor érdemes kipróbálni, hogy a megfelelő eredményt produkálja-e az újonnan létrehozott eljárásunk. Ehhez először is a korábbiak szerint az új include fájlt be kell jegyeznünk a főprogramba, az új eljárást pedig a GP_00_GlobalisPrototípusok.inc fájlban kell magadnunk.

A GP_00_GlobalisPrototípusok.inc fájlban:

Az ASM_027_GraphicsComplex_.asm főprogramban:

Felmerülhet a kérdés, hogy miután minden hívási előkészületet megtettünk, hol fogjuk ráadni a vezérlést a háttérkirajzoló eljárásunkra. Ennek helye a WndProc.inc fájlban lesz, amit korábban gondosan kiemeltünk a főprogram moduljából éppen azért, hogy külön szerkesztve áttekinhetőbb kódot kapjunk. Kiegészítjük a WndProc.inc fájlunkat egy új üzenet kezelésével, az alábbiak szerint. Mint emlékszünk rá, a callback mechanizmus segítségével az alkalmazások a kapott üzeneteik kezelésére a saját üzenetkezelő eljárásukat használják. Vagyis ha az alkalmazásunk kap egy WM_PAINT üzenetet, akkor módunk van azt a saját kódunkban lekezelni. Ezt az üzenetet akkor kapja az alkalmazás, ha valamiért újra kell rajzolni a kliens területet. Ezenkívül mi magunk kérjük a főprogramban amikor a következő sorokra kerül a vezérlés.

Mivel a WM_PAINT üzenet lekezelésekor rajzolni szeretnénk a képernyőre, ezért a WndProc eljárásunkban lokális hatókörrel létrehozunk két változót. Az egyik az ablak eszközkapcsolatának leírója, a másik egy PaintStruct típusú változó, amit a BeginPaint eljárásunk tölt fel és később az EndPaint használ.

A fordítás és futtatás eredménye, hogy a korábban „üres” ablakunkban a háttér megjelent. Ugyanakkor azt is megfigyelhetjük, hogy a konzolos ablakban a logolási üzeneteink sorra megjelennek.

17-32. ábra -

kepek2/5fejezet_17-32.jpg


Ha ez a háttér látható, akkor csak úgy gyakorlatképpen helyezzük el a startpont és a végpont rajzi elemeket is, bár ezek a háttérképen is láthatóak. Látszólag feleslegesen dolgozunk, de valójában ugyanilyen „kódblokkal” valósítjuk meg a haladás jelzését is, ezért „próbáljuk ki” ennek a két képnek a kirajzolását is. Készítsük el ennek a függvénynek egy „túlterhelését” is, ahol nincsenek paraméterek. Ez jól jön majd akkor, amikor nem áll rendelkezésünkre érvényes hdc, amit a BeginPaint eljárás most rendelkezésünkre bocsájtott. Mindezekkel a GP_00_HatterKirajzolas.inc fájlunk és eljárásunk a következőként áll jelenleg.

A GP_01_NyomogombKeszito.inc fájl

A következő lépés, hogy a meglévő hátterünkre vezérlőket varázsoljunk. Elöször a nyomógombokat helyezzük el a vezérlések számára kijelölt baloldali területen.

Ennek a modulnak kell elkészítenie az irány billentyűket (F,J,L,B), a programmód és iránymód között váltó radiobutton-t, valamint a hagyományos Prg Ellenőrzés,Prg Futtatás, és Akadályok nyomógombot. Úgy ellenörizzük a sikeres létrehozást, hogy egy ITMMO_GetErrorStr makrót illesztünk a CreateWindowsEx hívások mögé, amit később „kiremelünk”. Ennek a rutinnak a helye természetesen szintén a WndProc-ban van, és éppen a WM_CREATE esemény feldolgozásában.

A futási képen látható, hogy a nyomógombok a helyükre kerültek.

17-33. ábra -

kepek2/5fejezet_17-33.jpg


A nyomógombok mindegyike kap egy szimbólikus konstans azonosítót, mellyel az eseményvezérlőben hivatkozunk rá. Ezeket már jó előre definiáltuk a globális adatok include fájljában.

A GP_02_EditorKeszito.inc fájl

Ugyanígy készítsük el az editort létrehozó és azt az ablakban megjelenítő eljárásunkat és include fájlunkat.

Az editor készítés egy külső dll betöltésével kezdődik melynek helye a System32 alkönyvtár, neve pedig RICHED20.dll. Miután betöltöttük a dll-t, hivatkozhatunk a vezérlőre a „RichEdit20A” típus megjelöléssel ugyanúgy, ahogy a „button” esetében. A kapott leírót elmentjük, és figyeljük meg, hogy a létrehozáskor adunk ennek is egy konstanst, (IDE_EDITOR) mellyel majd az eseményvezérléskor hivatkozhatunk rá.

17-34. ábra -

kepek2/5fejezet_17-34.jpg


Felhívjuk a figyelmet az editor buffer deklarációjára, ami valójában két részből áll. Az egyik egy EditorBufferOrig nevű változó, ami a „programnyelv” szabályait írja le, és úgy tervezzük, hogy ez, mint megjegyzés mindig megjelenik az editorban. A másik az EditorBuffer nevű, mely tulajdonképpen a későbbiekben szerkesztett tartalomnak ad helyet. Csak a szemléltetés kedvéért helyeztük el az EditorBufferEnd változót, hogy a dump ablakban lássuk az editor buffer végét. A végén pedig kiszámíttatjuk a teljes buffer méretét a későbbi blokkos adatmozgatáshoz.

A GP_03_JatekterDefault.inc fájl

Mivel a játék adatait az előtervezés során egy struktúrákból álló sor/oszlop mátrixba szerveztük, szükséges, hogy ezt a még kitöltetlen mátrixot az alapállapotnak megfelelően kitöltsük. Szükség lesz erre a funkcióra akkor, amikor egy-egy játékot újra szeretnénk kezdeni „tiszta lappal”, akarom mondani tiszta játéktérrel. Hozzuk létre a szokásos módon az új include fájlunkat, és írjuk meg az alábbi kódot.

A programban a játéktér minden cellájának struktúrájába elhelyezünk egy IDBM_FREEPLACE tokent, kivéve a kezdő és végpontokba, ahol a megfelelő IDBM_STARTPLACE és IDBM_ENDPLACE értékeket helyezzük el. Hogy a játéktér logikai mátrixának állapotváltozásakor nagyobb kényelmünk legyen, elhelyezzük a cella tulajdonságai között azok logikai és grafikus koordinátáit is. Igy amikor a megjelenítési rétegben sorra vesszük a cellák állapotát, akkor rögtön a grafikus megjelenítés koordinátái is rendelkezésre állnak. A határvizsgálatnál állítjuk a logikai koordinátákat és a pixelben mért grafikus koordinátákat pedig minden alkalommal ezekből számítjuk ki ( de csak most az egyszer). Adósak vagyunk még ennek az eljárásnak a meghívásával, melynek helye a WM_CREATE üzenet feldolgozása, amivel bővítjük a WndProc eljárásunkat, amely most így fest:

A WM_CREATE üzenet feldolgozását egy SetFocus eljárás meghívásával zárjuk, hogy a későbbi egér és billentyű inputokat rögtön fogadni tudjuk. Természetesen a főprogramban ASM_027_GraphicsComplex.asm és a GP_00_GlobalisPrototípusok.inc include fájlokban is fel kell venni a szükséges bejegyzéseket.

Az ASM_027_GraphicsComplex.asm főprogramban:

És a prototípusokat leíró GP_00_GlobalisPrototípusok.inc fájlban:

Természetesen, ha fordítjuk és futtatjuk, látjuk, hogy a grafikus felületen semmi változás, de a konzolos logolás jól mutatja, hogy a WM_CREATE megelőzi az első WM_PAINT-et.

17-35. ábra -

kepek2/5fejezet_17-35.jpg


Az is látható, hogy az egyes beillesztett eljárásaink rendre lefutnak.

17-36. ábra -

kepek2/5fejezet_17-36.jpg


A GP_04_LogikaiCellaCimKeres.inc fájl

A játék működése során sokszor kell majd megnéznünk egy-egy cella állapotát több okból is, ezért szükségünk lenne egy olyan eljárásra, mely a logikai x,y koordináták paramétereinek megadásával a játéktér adott cellájának struktúra eleme címével tér vissza. További előtervezési igény ezzel a rutinnal szemben, hogy ezt a címet az ebx regiszterben adja vissza. Az ebx regiszter használata, mint azt a címzési módoknál láttuk, kifejezetten javallott, amikor összetett adatok tagjait akarjuk elérni. Ilyenkor az ASSUME direktívával lehetővé tesszük, hogy ha az adott adatelem kezdőcímét az ebx-ben tároljuk, akkor a tagoperátorral elérjük a tagokat. Csak az érdekesség kedvéért, ezt a metódust úgy valósítjuk meg, hogy most kivételesen a paramétereket nem stack-en, hanem regiszterben adjuk át, ezzel is növelve a hatékonyságot, amit kicsit lerontunk azzal, hogy ezeket a paramétereket pakoltan helyezzük el az eax-ben. (csak így fér el mivel a logikai koordináták word méretüek). Vagyis az ax-ben lesz az Y koordináta, a felső szó pedig az X-et tartalmazza. Nézzük a megvalósítást.

Elkészítjük ennek egy „túlterhelését” is, ahol két szóban paraméterezhetően adjuk meg a koordinátákat. Az eljárás lelke egy, az adott X és Y értékekkel, mint tömbindexszel végzett sor és oszlopkombinációs szorzás. Mindkét rutinban lokális változókat hozunk létre, hogy a tesztelés idején a a watch ablakban láthassuk az eredeti bemenő paramétereket. Ezt a technikát gyakran használjuk, amikor valamilyen okból bonyolultabbá válik a feladat. Ilyenkor jön jól e feltételes fordítási opció (mi most nem használjuk), amikor is a kitesztelt változatba már nem fordítjuk be a „felesleges” kódrészletet. Ebben a rutinban felhasználunk egy macrót, amit korábban készítettünk.

Talán emlékszünk még rá, hogy a saját makróinkat ITMMO_ előtaggal láttuk el, szintén a név-ütközések elkerülése miatt.

A GP_05_LogikaiRndXYKeres.inc fájl

Amikor a játéktéren véletlenszerűen akadályokat helyezünk el, akkor szükségünk van egy olyan eljárásra, amelyik az adott játéktér sorainak és oszlopainak számával határolt véletlen koordinátákat állít elő. Természetesen ezt is a már megszokott módon, include fájlban hozzuk létre.

Ebben a rutinban használunk egy nem általunk írt könyvtári függvényt, amire azért hivatkozhatunk, mert az inlcude-oket tartalmazó fájlunk tartalmazza a

sort. A meghívása egy tárolt cím alapján indirekten történik, és az eredményt, mely 0-32768 között van, osztjuk a sorok és az oszlopok számával, majd pakoltan adjuk vissza az eax-ben.

A GP_06_GrafikaiXYKeres.inc fájl

Az előzöek mellett a megjelenítésnek szüksége lesz az adott cella grafikus koordinátáira is, amelyet ez az eljárás szolgáltat. A bemeneti logikai koordinátákat pakoltan kapja az eax-ben, és ugyanitt adja vissza szintén pakoltan a pixelben mért grafikai koordinátákat, a kliens területére értve.

Ebből is két változatot készítünk. A második hívja az elsőt mint „nucleont”, csak előbb elvégzi a pakolást.

A GP_07_RndAkadalyBerendezes.inc fájl

Ebben az eljárásban az „Akadály” nyomógomb megnyomására a játéktér logikai mátrixában véletletszerűen helyezünk el akadályokat, aminek grafikus erőforrását korábban már előkészítettük. A megvalósítás kódjában láthatjuk, hogy az erőforrás betöltése után, melyet egy hdc-t előállító makró után hívunk, mentjük az erőforrás leíróját a szokásos módon.

Ezt követően a korábban már elkészített GP_05_LogikaiRndXYKeres, és a GP_04_LogikaiCellaCimKeres, valamint a GP_06_GrafikaiXYKeres felhasználásával elhelyezzük a véletlen koordinátájú akadályokat a logikai játéktérre, és azonnal ki is jelezzük azt. Ennek, és a korábban megírt GP_04, GP_05, GP_06 előtagú eljárásainknak a tesztelésére „életre kell keltenünk” az „Akadály” nyomógombot. Ez azt jelenti, hogy a nyomógomb által keltett eseményeket kezelnünk kell a WndProc függvényünkben.

Mint tudjuk, a nyomógomb események a WM_COMMAND üzenettel érkeznek, és csak akkor, ha az lParam nem NULL, mert lParam==NULL esetén egy menü kattintásé a WM_COMMAND. Ezért a wParam eax-be töltése után (itt lesz az esemény forrásának konstansa) vizsgáljuk az lParam értékét, és ha az NULL, akkor nem csinálunk semmit. Egyébként, ha az eax ax tartalma éppen az Akadály nyomógomb létrehozásakor kapott IDB_NEW konstans, akkor elágazunk és feldolgozzuk/reagálunk az eseményre. Ha már itt vagyunk, felvesszük a többi lehetséges nyomógomb eseményt is, de csak az eset szétválasztást írjuk meg. Visszatérve az „Akadály” nyomógomb kattintás eseményére, felülírjuk az ax-et az akkumulátor felső szavával, majd vizsgáljuk, hogy az nem BN_CLICKED-e, vagyis egy kattintás esemény. Ha igen, akkor logoljuk a nyomógomb kattintás eseményét, reszeteljük a játékteret, és hívjuk az akadályberendezést, majd fókuszba emeljük a játékteret. A fordítás és futtatás után a nyomógomb megnyomása előtt a következőt látjuk:

17-37. ábra -

kepek2/5fejezet_17-37.jpg


Az „Akadály” nyomógomb megnyomása után pedig az akadályokkal teleszórt játékteret látjuk:

17-38. ábra -

kepek2/5fejezet_17-38.jpg


Mivel a nyomógomb eseménykezelésében nem töröljük a korábban létrehozott akadályokat, ezért ha a nyomógombot többször aktivizáljuk, akkor „betelik akadályokkal a játéktér”. Ezért helyezzünk el a WndProc-ban egy emlékeztetőt arra, hogy később ezt még meg kell csinálnunk. Vagyis kellene egy, a játéktér grafikus tartalmát frissítő/törlő külön rutin? Ha alaposabban végiggondoljuk, akkor mégsem, hiszen a játéktér logikai mátrixát töröltük, vagyis csak a háttér akadályok nélküli újrarajzolására van szükség, amit már a GP_00_HatterKirajzolas eljárásunkkal megoldottunk. Helyezzük el ezt a hívást az alábbi kódrészletben, és máris kész vagyunk.

Itt a HáttérKirajzolás egy változatát használjuk, mert nem áll rendelkezésre az eszközkapcsolat, mint a WM_PAINT üzenet kezelésekor a BeginPaint után. Ha most próbáljuk, bár már helyesen fog működni, de a háttér újrarajzolás miatt eltűnnek a vezérlések a kliens területről, ezért további beavatkozásra van szükség. Hogy ott vannak a vezérlők, azt abból is láthatjuk, hogy ha rákattintunk, azonnal előtűnnek, csak nem frissültek az újrarajzolás után. Két lehetőség közül választhatunk, vagy a háttérkirajzoló eljárásunkból készítünk egy, csak a játéktér felületét újrarajzoló változatot, vagy készítünk egy nyomógomb és editor frissítőt, ami újrarajzolja a vezérléseket. Az előbbi lenne a célszerű, és egyszerűbb is, mert semmi nem indokolja, hogy a vezérlések hátterét is újra rajzoljuk, de most mégis a másik megoldást alkalmazzuk, mert bemutathatunk vele egy életszerű fejlesztési helyzetet, amikor „visszanyúlunk” egy már létező include fájlba, és bővítjük egy új eljárással. Mivel a nyomógombok láthatósági frissítéséről van szó nyilvánvaló, hogy a GP_01_NyomogombKeszito.inc fájlban kell elhelyezni az új eljárást. Legyen ennek neve konzekvensen betartva saját szabályunkat, miszerint az include fájl számozását megtartjuk ez eljárások nevében is, GP_01_NyomogombFrissito, melynek listája az alábbi.

Az egyes nyomógombok láthatóságát úgy állítjuk vissza, hogy egy BM_SETSTYLE üzenetet küldünk, és az lParam értékét TRUE-ra állítjuk. Figyeljünk arra, hogy az option típusú nyomógomb osztályok esetében a BS_AUTORADIOBUTTON stílust is be kell állítanunk. Persze, mint eddig mindig, az include fájl és az eljárások bővülését adminisztrálnunk kell, vagyis az új GP_01_NyomogombKeszito.inc fájl az alábbi:

Ugyanilyen okokból szükséges az editor frissítése is, amit az előzőekhez teljesen hasonlóan valósítunk meg, vagyis az új editorkészítőt kiegészítjük egy GP_02_EditorFrissito nevű új eljárással.

A GP_00_GlobalisPrototípusok és a WndProc módosítása után a GP_00_GlobalisPrototípusok.inc fájl az alábbi tartalmú:

Míg a WndProc.inc az alábbiakban néz ki:

Az projekt fájlainak listája pedig a következő:

17-39. ábra -

kepek2/5fejezet_17-39.jpg


Mindezekkel a változtatásokkal az „Akadály” nyomógomb használata után az alábbi már elfogadható futási képek kapjuk.

17-40. ábra -

kepek2/5fejezet_17-40.jpg


Külön érdemes megfigyelni, hogy a logolásunk milyen „szépen” mutatja a keletkező eseményeket és a meghívott eljárásokat.

17-41. ábra -

kepek2/5fejezet_17-41.jpg


Csak megemlítjük, hogy súlyos hiba lenne ha a WndProc-ban a frissítésre a már kész GP_01_NyomogombKeszíto-t és a GP_02_EditorKeszitot használnánk. Elsőre talán észre sem vennénk e hibának a következményeit, hogy minden új akadályberendezésnél új leírót hozunk létre, miközben a meglévőt nem szüntetnénk meg, vagyis a helyfoglalását nem szabadítanánk fel. Ez minden alkalommal csökkentené a memória méretét a „hanging” erőforrások miatt, ami előbb utóbb „meglátszana” programunk működésén. (Ki lehet próbálni és a procexprolerrel ellenőrizni.)

A GP_08_EditorFormatter.inc fájl

Editorunk kezelése sok munkát fog adni nekünk. A formatterrel egy olyan tulajdonságot adunk az editorunknak, hogy a kis programnyelvünk elemei szerinti egyéni színezéssel és tabulálással formázza újra az editor tartalmát. Az editorban az operációs kódokat mindig kékkel, az operandusokat feketével, a sorlezáró karaktert mindig pirossal jelezzük, míg a megjegyzéseket zölddel.

17-42. ábra -

kepek2/5fejezet_17-42.jpg


Ennek a megoldására több apró eljárást kell fejlesztenünk, mert az editor buffertartamának színezése, tabulálása, stb. az rtf (rich text fájl) követés miatt elég „nehézkes”.

Az első két eljárásunk, amit használni fogunk az a GP_08_EditorClear, mely az editor tartalmát törli és a GP_08_EditorFormatter, mely az editorba másolja a buffer előtti állandó megjegyzés-tartalmat, majd formázza. Ennek a két eljárásnak a helye szintén a WndProc-ban van, ahol először a WM_CREATE feldolgozásában hívjuk őket.

A kötelező GP_00_GlobalisPrototípusok és az ASM_027_GraphicsComplex.asm főprogramban történő adminisztráció után a futási képen az editor már a fejléccel kitöltött állapotban látszik.

17-43. ábra -

kepek2/5fejezet_17-43.jpg


A GP_09_VizualitasFrissites.inc fájl

A játék működésének egyik legfontosabb feladata, hogy a játéktér aktuális állapotát vizualizálja, vagyis a felhasználó lássa, hogyan áll a játék. Ebben az include fájlban lévő eljárások ezért az átlagnál többször futnak le. Mivel önnállóan kell grafikai feladatokat, jellemzően rajzolást végrehajtania, célszerű azokat a részfeladatokat, melyek egy rajzolást megelőznek, illetve bezárnak, külön metódusokba rendezni.

A GP_09_VizualitasFrissites eljárásunkat később tudjuk csak használni, amikor a játék állapota megváltozik valamilyen felhasználói beavatkozás hatására. Most inkább írjuk meg a WndProc-ban azt a részt, amikor az egér-kattintásra töröljük a játéktér állapotát, vagy amikor fókuszba emeljük a játékteret, illetve azt az elágazást, amikor töröljük az utolsó lépést. (ez utóbbit nem készítjük el, csak megmutatjuk, hogy hogyan kell hozzálátni, mert az adminisztrációja és forrásban történő követése egyenlőre túl bonyolult lenne. Az a tervünk tehát, hogy ha a játéktér balfelső színes négyzetére kattintanak, akkor a játéktér törlését valósítjuk meg a GP_09_VizualitasFrissites eljárással. Azt az eseményt kezeljük le, amit a WM_LBUTTONDOWN jelent, és a kattintás koordinátái alapján döntjük el, hogy mit kell csinálnunk. Ha már itt vagyunk ,akkor egy logolással és egy várakozással egészítjük ki a WM_DESTROY eseményt.

A GP_10_Fel_Jobbra_Le_Balra_Btn_Kezeles.inc fájl

Előtervezésünk szerint a vezérlési felületen lévő rajzolt iránynyilakra helyezett nyomógombokkal lehet majd vezérelni a haladást az akadályok között. Mivel ezeknek a nyomógomboknak a kezelése nagyon hasonló kódokat igényel, célszerű őket egy include fájlban kezelni. Ezek az operációk végre a játék állapotának megváltozásával járnak, majd ezért szükégünk lesz az éppen az imént befejett GP_09_VizualitasFrissites eljárásunkra.

Ebben az include fájlban helyett kapott az irányvizsgálat is, ami azt hivatott eldönteni, hogy az adott célcella tartalma megengedi-e, hogy „rálépjünk”, vagyis nem foglalt, vagy akadályt tartalmazó-e. Egyedül a sárgával jelzett sort nem tudjuk még hívni, amit később készítünk el, és ami az automatikus programsor-generálást valósítja meg. Ezt most csak „remeljük” ki.

A funkcionalitás bekapcsolása, vagyis az a képesség, hogy a nyomógombok hatására haladás beírás történik, és az látjuk is, nagyon egyszerű, mert csak a WndProc-ban kell kiegészítenünk a korábban már leprogramozott esetszétválasztást.

Fordítás és futtatás után az irány-billentyűkkel tetszőleges úton bejárhatjuk a játék területét:

17-44. ábra -

kepek2/5fejezet_17-44.jpg


A GP_11_Fel_Jobbra_Le_Balra_Key_Kezeles.inc fájl

Azt terveztük, hogy a játéktér fókuszában a billentyűzet iránymozgató billentyűivel is lehessen irányítani a haladást. Vagyis kell az előzöhöz nagyon hasonló eljárás, mely ezt megvalósítja. Alkalmazása/hívása pedig szintén a WndProc-ban kap helyet a WM_KEYDOWN üzenet kezelésekor. Persze a kötelező include és proto adminisztrációt nem felejtjük az új eljárások beillesztésénél.

Természetesen itt is megjegyzés mögé zárjuk a tervezett, későbbiekben megvalósítandó GP_12_ProgramSorGenetator eljárás hívást (sárgával kiemelve).

A WndProc aktuális módosítása pedig az alábbi:

Mindezek után most már a nyomógombokkal és a tasztatúra iránybillentyűivel is lehetséges a haladás, sőt kipróbálhatjuk, hogy az akadályokat is helyesen ismeri fel, vagyis nem enged akadály-mezőre lépni.

17-45. ábra -

kepek2/5fejezet_17-45.jpg


A GP_12_ProgramSorGenerator.inc fájl

Azt terveztük, hogy minden a nyomógombokkal vagy az iránybillentyűkkel történő operáció esetében az editorunkban generálunk egy automatikus „forrássort”, egy kis „bemenő” nyelven, aminek a feldogozásával majd visszafelé is végre lehet hajtatni a labirintus bejárását. Erre az eljárásra már a mozgatásokkor szükség volt, emlékszünk rá, hogy a hívásokat előre beírtuk, és megjegyzés mögé rejtettük. Itt az idő, hogy megírjuk az automatikus programsor-generátorunkat.

Ne felejtsük el a két haladást eredményező eljárásban,

a GP_10_Fel_Jobbra_Le_Balra_Btn_Kezeles-ben,

és a GP_11_Fel_Jobbra_Le_Balra_Key_Kezeles-ben a korábban megjegyzés mögé rejtett hívást aktivizálni. Ha megtesszük, az új futáson már látszik az automatikus programsor generálás eredménye.

17-46. ábra -

kepek2/5fejezet_17-46.jpg


Ha alaposan teszteljük a programot, akkor már biztosan rájöttünk arra a hibára, hogy ha egy játékot befejezünk, és a utána kérünk egy játéktér törlést, akkor a játéktér ugyan „tiszta lesz”, de a haladás nem indul a StartPont-tól. Ennek oka az, hogy nem hajtottuk végre a teljes inicializálást, vagyis állítottuk be a JatekVege flag-ünket FALSE értékre, amint ezt a GP_10_HaladásBeiras eljárásunkban az alábbi kódrészletben látható. Erre a hibára úgy találhatunk rá, hogy a GP_10_Fel_Jobbra_Le_Balra_Btn_Kezeles függvényünk elejére teszünk egy töréspontot, majd az adott irány nyomógomb kezelésben átmegyünk a GP_10_IranyVizsgalat eljáráson keresztül a GP_10_HaladasBeiras metódusba, és itt találjuk meg az alábbi kódrészletben a rendellenes működés okát. A beírás azért nem valósul meg, mert a JatekVege flag TRUE állapotban maradt a játék befejezése után, a törlési GP_09_VizualitasFrissites lefutása ellenére.

Ezért a WndProc-ban a törlés után helyezzük el a JatekVege újbóli FALSE-ra állítását.

Persze mindekinek igaza van, aki azt kérdezi, hogy miért nem intéztük ezt el már korábban, a GP_09_VizualitasFrissites eljárásunkban. Sőt bizonyos okból célszerűbb is ezt ott megtenni, de ne szépítsük, lehet, hogy egyszerűen nem jutott eszünkbe (és ez bárkivel előfordulhat), de itt most demonstrációs célokat szolgált, mert ennek a hibának a megkeresése és utólagos javítása jó példája az oly gyakori „kódfoltozásnak”.

A GP_13_atoi.inc fájl

Nagyon nagy segítség, különosen az assembly fejlesztés során, hogy már létező könyvtárakat használhatunk, és ezzel megkíméljük magunkat, és főként az időnket a felesleges fejlesztésektől. És mégis lehetnek esetek, amikor nem választjuk ezt az „ingyen segítséget”, hanem fáradságos munkával újragyártjuk a standard könyvtári rutinokat. Az ok általában az, hogy ezek a rutinok túlságosan általánosak, vagy inkább nem illeszkednek eléggé valamilyen szempontból az általunk alkalmazott fejlesztési „belső” szokásokhoz. Az assembly fejlesztések esetében ez általában a paraméterek eltérő kezelése.

A mi példánkban szükségünk lenne ugyan egy olyan rutinra később, ami egy ASCII literálból numerikus számot állít elő, de jó lenne, ha alkalmazná az ebben a projektben alkamazott buffercímzési szokásunkat, nevezetesen azt, hogy az ebx[esi] ben kapjuk a címet, az eredményt az eax-ben állítjuk elő, és a feldolgozás után az ebx[esi] a feldolgozott sztring végére mutat. Majdnem biztos, hogy ilyen könyvtári rutint nem találunk, ezért két lehetőségünk van. Vagy a létező c runtime atoi függvényt „becsomagoljuk” egy saját eljárásba, amelyben mi gondoskodunk arról, hogy a wrapper biztosítsa az előbbi feltételeket, vagy veszünk egy nagy levegőt és újra írjuk az egészet. Mi most ez utóbbit választjuk, lévén ez egy oktatási anyag, és a cél az assembly minél hatékonyabb elsajátítása.

A program tesztelésére készítünk egy GP_13_AtoiTeszt nevű eljárást is, melynek tesztelésére a WndProc-találhatunk helyet, például a StartPontra való kattintás esetén.

A tesztelés úgy zajlik, hogy a hívásnál a WndProc-ban elhelyezünk egy breakpoint-ot, és onnan nyomonkövetjük először a standard c runtime, majd a saját hívásunk eredményét.

A GP_14_SorSzintaktika.inc fájl

A sorok végrehajthatóságát egyenként vizsgálni fogja ez az eljárás, figyelemmel arra, hogy a nyelv szintaktikai szabályainak megfelel-e. Itt definiáljuk azt a struktúrát, melynek feltöltésével a későbbi programsor-értelmező lehetővé teszi az iránytokenek alapján a programozott kirajzolást.

Az eljárást úgy terveztük meg, hogy a hívás pillanatában már átléptük az összes semleges karaktert, és az első ellenőrzött operációs kódon áll az [ebx][esi]. Ebben a rutinban használjuk „üzemszerűen” a saját atoi függvényünket.

A GP_15_SorKiemelo.inc fájl

További előkészítő eljárásokat hozunk létre annak érdekében, hogy a forrásnyelven írt sorokat is végre tudjuk hajtatni a programmal, vagyis legyen a labirintus bejárása programozható. Ennek fontos része a sorkiemelés, ami a bufferből egy feldolgozóba emeli az aktuális sort.

A GP_16_ProgramListaErtelmezo.inc fájl

Ebben az eljárásban fogjuk össze és használjuk a korábban elkészített eljárásainkat, és mivel azokat elegendően részletesen kidolgoztuk, itt már nem sokat kell kódolni.

Tulajdonképpen kitölti az ObjectCodeList-et és lehetővé teszi, hogy a projekt utolsó eljárása ez alapján újrarajzolja a haladást, a programlista alapján.

A GP_17_ObjectCodeFuttato.inc fájl

A programozott bejárásnak ez a rutin a lelke. Az értelmező által „tokenizált” mozgásokat rajzolja a játéktérre.

A játék programozott végrehajtására az utóbbi néhány funkciót be kell kapcsolni. Persze ezt is a Wndproc-ban tesszük meg, az alábbiak szerint, és ha már idáig eljutottunk, akkor a korábban megírt formázót is aktivizáljuk.

Mint látható, az option buttonok állásától függ, hogy a programozott végrehajtást kérni lehet-e. Ezen, és az editor esetleges automatikus „írhatóságán” érdemes elgondolkozni,de most beérjük ilyen kialakítással.

17-47. ábra -

kepek2/5fejezet_17-47.jpg


A fordítás után láthatjuk, hogy a programozott végrehajtás is működik.