« Előző oldal

5. Finomítás a játékon

Számos olyan része van a játéknak, amelyeket tovább kell javítani a jó kinézet érdekében. Például a lövedékek nem az "objJatekos" közepéből jönnek ki, és az ellenségek inkább felbukkannak a képernyőn ahelyett, hogy berepülnének. Ideje kijavítani ezeket.

5.1 Középre zárt sprite-ok

Megnézzük, hogy a lövedékek miért nem a játékos repülőjének közepéből jönnek ki. Nyisd meg az "sprJatekos" sprite-ot.

Az ablak középső részén található az "Origin" (Eredet) beállítás. Ez egy x és y értékből áll. Ezek az értékek határozzák meg a sprite úgynevezett eredetét. Az alapértelmezett értékeik a (0,0). Ami azt jelenti, hogy ha a sprite-ot a (100,100) koordinátájú helyre tesszük, akkor így fog kinézni:

Aztán amikor egy lövedék létrejön ugyanazon a helyen, mint ahol a repülő van, így fog kinézni:

Inkább azt akarjuk, hogy a labda a repülő elején, középről induljon. Amit csinálunk az az, hogy középre igazítjuk mindkét sprite eredetét. Kezdjük az "sprJatekos" sprite-tal.

A spriteablakban láthatod a sprite szélességét (width) és magasságát (height). Figyeld meg, hogy a sprite szélessége 52, a magassága pedig 78. Most gépelj be 26-ot X eredetnek, 39-et pedig Y eredetnek (26=52/2 és 39=78/2). Csináld meg ugyanezt az "sprLovedek" sprite-nál, ahol 8-at kell begépelned X és Y eredetnek is (ez a sprite közepe).

Most ha a játékos sprite és a lövedék sprite azonos helyen van, pl. (100,100)-on, akkor így néz ki:

A lövedék sprite közepe egybe fog esni a játékos sprite közepével. Jó. De azt szeretnénk, hogy pontosan a repülő elejéről induljon. Ez meg lesz oldva egy kis ügyeskedéssel az "instance_create" függvénynél, de először mentsd el és futtasd a játékot, hogy lásd a különbséget.

A lövedékek most a játékos repülőjének közepén tűnnek fel.

Nyisd meg az "sprLovedek" sprite ablakát, és igazítsd az eredetét annak is középre, vagyis (35,50)-nél kell lennie, mivel a sprite 71x100 képpont nagyságú. Aztán nyisd meg az "sprEllenseg1" sprite-ot is, és gépeld be eredetnek a (32,32)-t. Jó. Most minden sprite olyan, amit én középre igazítottnak nevezek.

Nyisd meg az "objJatekos" objektumot, és válaszd a <Space> gomb eseményt. Kattints kettőt az "Execute a piece of code" akcióra az akciósorban a kódablaka megnyitásához. Ez az, ahol az "objLovedek" objektum egy példánya létrejön. Azt akarjuk, hogy egy kicsit magasabb helyen jöjjön létre a képernyőn. A magasabb hely alacsonyabb y-koordinátát jelent. Tehát változtasd meg az "instance_create" sort így:

instance_create(x, y - 15, objLovedek);

Zárd be az ablakot, mentsd el a játékot és próbáld ki. Hmm. Egy kicsit jobb, de a lövedéknek még valamivel föntebb kellene felbukkannia. Rendben, változtassuk meg ismét a kódot, ezúttal erre:

instance_create(x, y - 25, objLovedek);

Aztán próbáld ki megint a játékot. Szerintem jól néz ki. Néha több különböző értéket kell kipróbálnod, hogy a dolgok jól nézzenek ki.

5.2 Simább feltűnése az ellenségnek

Az ellenségek még a semmiből bukkannak elő. Szebb lenne, ha inkább berepülnének a képernyőbe.

Ez megoldható azzal, hogy a képernyőn kívül kell őket létrehozni, és aztán berepülnek onnan. Ez viszont egy kis gondot fog okozni az "Outside" eseménnyel.

Nyisd meg a "LetrehozEllenseg1et" szkriptet. Ez az, ahol az ellenség példányai létrejönnek. Az "instance_create" függvény használt az ellenség egy példányának meghatározott helyre való létrehozására. Ezt akarjuk megváltoztatni, hogy az ellenség föntebb jöjjön létre, ami azt jelenti, hogy alacsonyabbra állítjuk a létrehozási pont y koordinátáját. Írd át erre a szkript első sorát:

instance_create(floor(random(screen_width)), -64, objEllenseg1)

Mentsd és futtasd a játékot. Észre fogod venni, hogy az ellenségek egyáltalán nem bukkannak fel. Miért? Ez az ellenség objektum "Outside" eseménye miatt van. Nyisd meg az "objEllenseg1" objektumot, és válaszd az "Outside" eseményt. Látni fogod, hogy ez az esemény végrehajtja az "EllensegMegsemmisitese" szkriptet. Ez a szkript mindig hívódik, amikor az ellenség példány a képernyőn kívül található. De mi nem akarjuk, hogy az ellenség megsemmisüljön, amikor a példány a képernyő fölött van, hanem csak akkor, amikor a képernyő alatt van. Jól van, próbáljunk ki valamit az "EllensegMegsemmisitese" szkriptben. Nyisd meg a szkriptet, és változtasd meg ilyenre:

if (y > screen_height + sprite_yoffset) then
{
instance_destroy();
}

Ez ellenőrizni fogja, hogy az ellenség a képernyő alatt található-e, vagyis hogy az ellenség y koordinátája nagyobb-e, mint a képernyő magassága plusz az ellenség sprite eredete. A sprite eredet y koordinátája önműködően tárolódik a "sprite_yoffset" változóban, amit itt használtunk. Erre szükség van, mert különben az ellenség rögtön megsemmisülne, amint az eredete a képernyőn kívülre került, ami azt jelenti, hogy az ellenség sprite fele még belül lehet a képernyőn és látható. Nem jó. Ez a szkript viszont meggyőződik arról, hogy a teljes ellenség sprite a képernyőn kívülre esik, mielőtt megsemmisül a példány.

Ha most kipróbálod a játékot, akkor láthatod, hogy az ellenségek berepülnek a képernyőre, majd keresztülrepülnek rajta. De látszódik egy robbanásrész, mivel az ellenségek megsemmisülnek. Ezt javítani kell. A robbanás példány az "objEllenseg1" objektum "Destroy" eseményében jön létre. Nyisd meg és kattints kettőt az "Execute a piece of code" akción a szerkesztéshez.

Ez a kód teljes egészében az "objRobbanas" objektum egy példányának létrehozását végzi. Az ellenség y koordinátáját kell megvizsgálnunk a robbanás létrehozása előtt, hogy a robbanás csak akkor jöjjön létre, ha az ellenség még a képernyőn van.

Ellenőriznünk kell, hogy az y koordináta kevesebb, mint a képernyő magassága plusz a sprite y eredete. Változtasd meg szkriptet erre:

if (y <= screen_height + sprite_yoffset) then
{
instance_create(x, y, objRobbanas);
}

Zárd be a kódablakot és próbáld ki a játékot. Most az ellenségeknek csendesen kell eltűnniük, robbanás nélkül. De a lelövésükkor létre kell jönnie a robbanásnak.

5.3 Globális változók

Úgy gondolom, a globális változókat is fontos megismerni. Ezek azok a változók, amelyek nem tartoznak egy bizonyos példányhoz sem, hanem elérhetők az összes példány számára, bármikor.

A globális változók jók értéktárolásra, mint például pontszám, egészségérték, maximum és minimumértékek és hasonlók. Az első dolog, amihez egy globális változót fogunk használni, az a játékos sebessége. Azt hiszem, a sebesség egy kicsit túl lassú. A sebesség megváltoztatásához most négy különböző helyen kell a kódban megváltoztatni egy számot. A legjobb lenne, ha a sebesség megváltoztatható volna csak egy helyen a kódban. Ez többféleképpen is megoldható, de mi egy globális változót fogunk használni.

Csinálj egy "JatekKezdet" nevű szkriptet, és add hozzá ezt a kódot:

global.jatekosMaxSebesseg = 8;

Figyeld meg a "global" szót és a pontot a változónév előtt. Ez az, ahogyan a globális változókat használni kell. A GM súgójában egy kicsit többet is olvashatsz a globális változókról.

Most végre kell hajtanunk ezt a szkriptet valahonnan. Az "objJatekos" objektum "Create" eseménye jó helynek tűnik. Válaszd ezt az eseményt, add hozzá az "Execute a script" akciót, és válaszd ki a szkriptet, amit már létrehoztunk. Aztán mozgasd az akciót eggyel föntebb, hogy a lista tetején legyen (a "JatekosBeallit" előtt). Ehhez csak a lista tetejére kell húznod az akciót.

Most az "objJatekos" <Left> gomb eseményében változtasd meg a kódot erre:

x -= global.jatekosMaxSebesseg;

A <Right> gomb eseményben erre:

x += global.jatekosMaxSebesseg;

És az <Up> gomb eseményben erre:

y -= global.jatekosMaxSebesseg;

Végül nyisd meg a "MozgatJatekostLe" szkriptet, és változtasd meg benne a kódot erre:

y += global.jatekosMaxSebesseg;

Most mentsd el és futtasd a játékot.

Ha később úgy döntünk, hogy megváltoztatjuk a játékos sebességét, akkor ezt a kódban csak egy helyen, a "JatekKezdet" szkriptben kell megtennünk.

Ez egy jó programozási módszer annak érdekében, hogy a játékaidban minél kevesebbszer használj számokat. Így a számokat elég csak egy inicializáló, vagyis kezdőérték beállító szkriptben, mint pl. a "JatekKezdet", meghatározni globális változókba vagy valami hasonlókba, és helyettük ezeket a változókat használni. Ez nagymértékben leegyszerűsít minden változtatást, ami később szükséges lehet a játékban.

Adni fogunk még néhány más dolgot is a "JatekKezdet" szkripthez. Először is megadjuk a játékos maximális energiaszintjét, így:

global.jatekosMaxEnergia = 100;

Aztán megváltoztatjuk az "energia" sort a "JatekosBeallit" szkriptben:

energia = global.jatekosMaxEnergia;

Jó dolog, hogy ezek az értékmeghatározások mind egy helyen vannak.

Folytatjuk az értékek adását a "JatekKezdet" szkriptben, mígnem így néz ki:

global.jatekosMaxSebesseg = 8;
global.jatekosMaxEnergia = 100;
global.ellenseg1MaxEnergia = 100;
global.ellenseg1Karokozas = 30;
global.lovedekKarokozasaEllenseg1nek = 50;

Lehetne itt több érték is, amik így megváltoztathatók, de itt meg fogok állni. Ezen változók használatához kicsit meg kell változtatni néhány szkriptet. Nyisd meg az "Ellenseg1Indit" szkriptet, és változtasd meg erre az "energia" változó beállítását:

energia = global.ellenseg1MaxEnergia;

Aztán nyisd meg a "JatekosEllenseg1Utkozes" szkriptet, és változtasd meg az "energia" változó csökkentését a következőre:

energia -= global.ellenseg1Karokozas;

Végül nyisd meg a "LovedekEllensegUtkozes" szkriptet, és változtasd meg az energiacsökkentő utasítást erre:

energia -= global.lovedekKarokozasaEllenseg1nek;

Most már sokkal könnyebb megváltoztatni a játék ezen paramétereit. Mentsd el a játékot. Nem szükséges futtatni, kivéve ha látni akarod, hogy van-e valami hiba. A játékmenetet illetően semmi változásnak nem kellett történnie.

5.4 Hol az energiám?

Beszéltünk egy kicsit a játékos energiájáról, és hogyha az energia lemegy 0-ra, akkor a játékos megsemmisül. Azonban eddig nem láttuk annak az energiának semmiféle kijelzését. Itt az ideje egy energiamérő készítésének.

Az energiamérő létrehozásához semmiféle sprite-ot nem fogunk használni. Helyette először megnézzük a GM többi rajzolási lehetőségeit.

Hogy valami kirajzolódjon a képernyőn, szükség van egy objektumra. Ehhez készíteni fogunk egy másik vezérlőobjektumot. Ennél nem lesz kirajzolva a sprite, csak azért kell hozzá, hogy mutassa a szobában. Tehát hozz létre egy új sprite-ot, nevezd el "sprJatekosVezerlo"-nek és rendeld hozzá a "Sprites /Space" mappából a "Dish.bmp"-t. (Megjegyzés: ahogy a 4.1 fejezetben már említésre került, az újabb GM-ekben a sprite nélküli objektumok is kapnak egy kis képet, amely alapján látni lehet a szobában, tehát nem szükséges külön sprite-ot létrehozni.)

Aztán hozz létre egy új objektumot, az "objJatekosVezerlo"-t, és válaszd hozzá az "sprJatekosVezerlo" sprite-ot. NE állítsd be ezt az új objektumot láthatatlannak. Sok e-mailt kaptam olyanoktól, akik azt állították, hogy az energiacsík nem látszódott, pedig pontosan követték az itt leírtakat. Aztán, amikor átnéztem a fájljaikat, észrevettem, hogy az "objJatekosVezerlo" objektumot láthatatlannak állították be. Bár a legtöbb vezérlőnek láthatatlannak kellene lennie, de mivel ez egy energiacsík kirajzolására használt, ezért ennek láthatónak kell lennie, különben az objektum "Draw" (Rajz) eseménye nem hajtódik végre (Megjegyzés: az újabb GM változatokban már a láthatatlan objektumokon is végrehajtódik a Rajzolás esemény, de az itt említett probléma már egyébként is lényegtelen, mivel sprite nélkül is használhatók az objektumok, s ezeknél mindegy, hogy láthatók-e vagy nem). Később meg fogod érteni, hogy mire gondoltam. Azt hittem, biztosabb vagyok ebben, amikor először közzétettem ezt az írást, de nem voltam, és ez a szöveg, amit most olvasol, az első, 2002. 08. 15-i átdolgozáshoz lett hozzáadva.

Add az új objektum egy példányát a szobához. Ez az objektum lesz arra használva, hogy létrehozza az "objJatekos" példányt, ezért ezt el kell távolítani a szobából. Tehát töröld az "objJatekos" példányt (az SR71 repülőt) jobb egérgombbal való rákattintással.

Mivel a játékos nem fog létezni, amikor először elindul a játék, ezért a vezérlőből kell azt létrehoznunk. Hogy ezt csináljuk, annak oka az, hogy nem mindig világos, hogy a GM milyen sorrendben hozza létre a példányokat, legalábbis én nem tudom, hogy milyen sorrendben jönnek létre. Tehát meg kell győződnünk arról, hogy először a vezérlő példány jön létre, és aztán a játékos példány. A másik ok, hogy a vezérlőnek ismernie kell a játékos példány azonosítóját (ID-jét), és ez legkönnyebben a játékospéldány létrehozásából kapható meg a vezérlő objektumból.

A "JatekKezdet" szkriptnek az "objJatekosVezerlo" által kell futnia az "objJatekos" helyett. A "JatekKezdet" szkriptet végrehajtó "Execute a script" akciót helyezd át az "objJatekos" "Create" eseményéből az "objJatekosVezerlo" "Create" eseményébe.

Most szükségünk van egy szkriptre a játékos létrehozásához. Hozz létre egy új szkriptet, amit nevezz el "LetrehozJatekost"-nak, és írd bele ezt a sort:

jatekosom = instance_create(screen_width / 2, 400, objJatekos);

Ez létre fogja hozni az "objJatekos" objektum egy példányát. Az x koordináta a képernyő közepén lesz (screen_width/2), az y pedig valahol a képernyő alján (80 képpontra az aljától).

Vedd észre, hogy az "instance_create" függvény elé azt írtam, hogy "jatekosom =". Azért, mert amikor az "instance_create" létrehozta a játékos példányát, visszatér egy értékkel. Ez az, ahogyan egy függvény működik, emlékszel? Általában nem törődünk ezzel az értékkel, de most tárolni akarjuk a visszaadott értéket egy helyi változóban, a "jatekosom"-ban. A visszaadott érték a létrehozott játékospéldány azonosítója. Később használni fogjuk a játékospéldány néhány helyi változójának elérésére.

Az "objJatekosVezerlo" objektumablakában válaszd a "Create" eseményt, add hozzá az "Execute a script" akciót, és ehhez válaszd a "LetrehozJatekos"-t.

Most itt az ideje az energiacsík rajzolásának. Hozz létre egy új szkriptet (nem szereted már őket? :) :) ), és nevezd el "EnergiacsikRajzolas"-nak.

Az "objJatekosVezerlo" "Drawing" (Rajzolás) eseményéhez adj egy "Execute a script" akciót, és válaszd az "EnergiacsikRajzolas" szkriptet.

Aztán menj vissza a szkriptbe. Az energiacsíkot valahogy így szándékozom megrajzolni:

Először megrajzoljuk a szürke háttértéglalapot, aztán arra a kék energiatéglalapot. A téglalap körüli zöld keret csak a szoba hátteréből látszódik, ne törődj vele.

Alakok rajzolásához a GM-ben először be kell állítanunk egy színt a határvonalnak és egy kitöltési színt. Gépeld be ezeket a sorokat a szkriptbe:

pen_color = make_color(150, 150, 150);
brush_color = make_color(150, 150, 150);

A "make_color" (színkészítés) függvény három színből állít elő egyet. A zárójelek közti értékek a szín piros, zöld és kék összetevőinek erősségei. A számok értéke 0 és 255 közötti lehet, ami azt jelenti, hogy a "make_color(255,0,0)" egy mélyvörös színt ad, a "make_color(255,0,255)" pedig egy ibolyaszínt (piros és kék). A szkriptben használt (150,150,150) értékek egy középszürke színt adnak.

A pen_color használt a téglalap "körvonalazásához", a brush_color pedig egy színnel való feltöltéséhez. (Megjegyzés: a kikeverésen kívül a szín közvetlenül is megadható, pl. a szürke a "c_gray", tehát: brush_color=c_gray .)

Amikor végeztünk a színbeállítással, megrajzolhatjuk a téglalapot. Ehhez két koordinátapárra van szükségünk, az (x1,y1) és (x2,y2)-re. Nézd meg az alábbi képet:

Az energiacsíkot a képernyő bal alsó sarkába akarjuk elhelyezni, amit így tehetünk meg:

x1 = 5;
y1 = screen_height - 15;
x2 = 110;
y2 = screen_height - 5;
draw_rectangle(x1, y1, x2, y2);

Az imént létrehoztunk négy változót, amiket koordinátákként használunk a "draw_rectangle" függvényben. A koordinátaszámokat közvetlenül is begépelhetnénk a "draw_rectangle" függvénybe, de úgy a sor túl hosszúnak tűnt, ezért választottam ezt a módot.

Végül ki akarjuk rajzolni a kék csíkot, ami magát az energiát jelképezi:

pen_color = make_color(0, 0, 255);
brush_color = make_color(0, 0, 255);
x1 = 7;
y1 = screen_height - 13;
x2 = 7 + jatekosom.energia;
y2 = screen_height - 7;
draw_rectangle(x1, y1, x2, y2);

Itt látjuk, hogy a "jatekosom" változó mire használható. Arra, hogy megkapjuk az "energia" változót az "objJatekos" példányból, ami egy korábbi szkriptben jött létre. Egy másik példány helyi változójának eléréséhez a példány azonosítóját (ID-jét) kell használni, utána egy ponttal és a helyi változó nevével. Íme!

Van egy dolog ezzel kapcsolatban, ami veszélyes lehet. Ha a "jatekosom" azonosítójú játékos példány megsemmisül, akkor többé az "energia" változót sem lehet elérni a "jatekosom.energia"-n keresztül. Ez egy végzetes hibát okoz, és a Windows teljesen lefagyhat. A legjobb megoldás a "jatekosom" példány létezésének ellenőrzése, mielőtt használjuk a helyi változóját. Ezt az "instance_exists" (példány létezése) függvénnyel lehet elvégezni, így:

if (instance_exists(jatekosom))

Ez a teljes kódja az "EnergiacsikRajzolas" szkriptnek:

pen_color = make_color(150, 150, 150);
brush_color = make_color(150, 150, 150);
x1 = 5;
y1 = screen_height - 15;
x2 = 110;
y2 = screen_height - 5;
draw_rectangle(x1, y1, x2, y2);

if (instance_exists(jatekosom))
{
pen_color = make_color(0, 0, 255);
brush_color = make_color(0, 0, 255);
x1 = 7;
y1 = screen_height - 13;
x2 = 7 + myPlayer.myEnergy;
y2 = screen_height - 7;
draw_rectangle(x1, y1, x2, y2);
}

Győződj meg arról, hogy a szkript ezt a fönti kódot tartalmazza, aztán mentsd és próbáld ki a játékot.

Most a képernyő bal alsó sarkán látszódni fog egy kék energiacsík szürke háttérrel. Ha nem látszódik, akkor nézd meg, hogy az "objJatekosVezerlo" tényleg láthatónak van-e beállítva.
(Megjegyzés: a GM beépített "health" változójának kirajzolására használható az "action_draw_health" függvény is, illetve GM 5.3-tól bármilyen érték kirajzolására a "draw_healthbar" függvény.)

 

6. Élet, a Világegyetem és a többi

Nem tudom, te hogy vagy vele, de engem untat a zöld háttér. Ideje valamit csinálni vele.

Ez lesz az utolsó fejezet, mert ha most nem állok meg, akkor lehet, hogy soha nem lesz befejezve ez a mű. Remélhetőleg, ha jó sok választ kapok, akkor csinálni fogok egy folytatást több haladóbb programozással, de ezt majd meglátjuk.

6.1 Egy messzi, messzi galaxis

Az űrjátékok számára a legközönségesebb háttér néhány csillag lehet. Tudom, hogy az SR71 és a MIG41 nem repül az űrben, de én, mint ennek a dokumentumnak legfőbb uralkodója, elhatároztam, hogy lehetővé teszem számukra az űrben való repülést.

Nem találtam elég használható űrhátteret a GM mappájában, legalábbis görgethetőt nem, tehát el fogjuk készíteni a sajátunkat. Nem egy túl bonyolultat, hanem csak egy egyszerűt.

Először készíteni fogunk egy háttérképet, ami csillagosnak néz ki. Hozz létre egy új hátteret (Background), és nevezd el "htCsillagok1"-nek, majd kattints az "Edit Background" gombra. Ez megnyitja a GM képszerkesztőjét. Nem a világ legjobb 2D-s grafikus alkalmazása, de azért ismer néhány trükköt és kezdők számára elég jó. Első dolgunk beállítani a háttérkép méretét. A háttérkép végig ki lesz rakva az egész szobán, ha nem olyan nagy, mint a szoba. Ezt nevezik "csempézés"-nek. Hogy emiatt a háttér ne nézzen ki túl szabályszerűen ismétlődőnek, megnöveljük egy kicsit a méretét.

Válaszd a menüből a "Transform -> Resize Canvas"-t (Átalakítás -> Átméretezés). A felbukkanó ablakban keresd meg a két szövegdobozt, amik a "pixels" (képpontok) szó előtt vannak, és gépeld be mindkettőbe a "200" értéket. Ha a "Keep aspect ratio" (Oldalarány tartása) jelölődoboz be van jelölve, akkor csak az egyiket kell megváltoztatnod, és a másik követni fogja.

Kattints az OK-ra. Most a háttérképünknek 200 x 200 képpont nagyságúnak kell lennie.

Válaszd a fekete színt az ablak jobb oldalán található színválasztó dobozból. Most válaszd a vödör ikont, amit "Fill an area"-nek (Egy terület kitöltése) hívnak, aztán kattints a képre. Most az egész képnek feketének kell lennie.

Ezután válaszd a fehér színt, és kattints a ceruza ikonra, amit "Draw on the image"-nek (Rajzolás a képen) hívnak. Kattints a képen mindenfelé véletlenszerűen néhány csillag elhelyezéséhez, de ne rajzolj belőlük túl sokat. Én csak hármat rajzoltam, és így megfelelően nézett ki a játékban. Így néz ki az a kép, amit én készítettem:

Kattints az OK-ra a képszerkesztő bezárásához. Most nyisd meg a szobaablakot a "Room1"-re való duplakattintással, majd kattints a "Backgrounds" (Hátterek) fülre. Itt lehet kiválasztani, hogy mely háttérképek legyenek láthatók a szobában. Válaszd a "Background 0"-t. Aztán a kicsit lentebbi kiválasztódobozban válaszd ki a háttérképet, amit az előbb csináltunk, a "htCsillagok1"-et. Bizonyosodj meg arról, hogy a "Visible when room starts" (Látható a szoba kezdésekor) jelölődoboz be van jelölve. A "Tile Hor." (Vízszintesen csempézett) és "Tile vert." (Függőlegesen csempézett) jelölődobozoknak is bejelölve kell lenniük.

A másik dolog, amit itt meg kell tennünk, hogy kikapcsoljuk a "Draw background color" (Háttérszín rajzolása) feliratú jelölődobozt. Ez ki fogja kapcsolni a zöld színt, ami eddig háttérnek volt használva. Többé már nincs rá szükség, mivel az új háttérkép beborítja az egész szobát.

Rendben. Itt most befejeztük. Kattints az OK-ra, mentsd el a játékot és próbáld ki.

Ha minden úgy történt, ahogy kellett, akkor most a játékban néhány csillagnak kell lenni háttérként. Bár ez meglehetősen unalmasan néz ki. Adjunk némi mozgást a csillagoknak.

Nyisd meg a "Room1" ablakot, és kattints újra a "Background" fülre. Lentebb találod a "Vert. speed" (Vízszintes sebesség) feliratú jelölődobozt, ami jelenleg 0-ra van állítva. Állítsd 3-ra. Kattints az OK-ra, mentsd el a játékot és próbáld ki megint.

De még nem végeztünk a háttérrel.

6.2 Barátságtalan szó: Parallaxis

Ideje némi mélységet adni a háttérnek. A parallaxisnak nevezett illúziót fogjuk használni. (Megjegyzés: a parallaxis jelentése: eltolódás, a szemlélt tárgy látszólagos eltolódása a látószög megváltozása következtében.) Azon a tényen alapul, hogy amikor haladsz és oldalra nézel, például kinézel az autód oldalablakán, akkor a távoli tárgyak, mint a fák a látóhatáron, olyanok, mintha lassan mozognának, míg az autóhoz közelebbi tárgyak, mint a sínek és az útburkolati jelek, gyorsan elsuhannak. Ez egy illúzió készítésére is felhasználható. Az emberi szem be lesz csapva, és az agynak úgy tűnik, hogy amit a szem lát, az 3-dimenziós mélységben van.

Hozz létre egy új háttérképet, nevezd el "htCsillagok2"-nek, és méretezd 200 x 200 képpontnyira, ahogy az előbb. De ezúttal a csillagok színe legyen világosszürke, és ne teljesen fehér. Ehhez a háttérhez adhatsz egy kicsivel több csillagot. Talán ötöt.

Aztán nyisd meg ismét a szobaablakot, és kattints a Background fülre. Válaszd a "Background 1"-et, ehhez pedig a "htCsillagok2" háttérképet. Bizonyosodj meg arról, hogy a "Visible when room starts" be van jelölve. Az új háttér "Vert. speed" értékének adj 2-t.

Zárd be az ablakot, és próbáld ki a játékot. A háttér még mindig úgy néz ki, mint egy lapos csillagos mező. Ez azért van, mert elfelejtettük átlátszónak beállítani az új csillagmezőt. Nyisd meg a "htCsillagok2" hátteret, és jelöld be a "Transparent"-nek (Átlátszó) nevezett kis dobozt. Aztán mentsd és próbáld ki újra a játékot. Jól van! Most két, különböző sebességgel mozgó csillagos mező van, előidézve a mélység némi látszatát.

Hogy még jobb legyen, adunk egy harmadik csillagos felszínt. Csináld úgy, ahogy a második háttérnél, de ezt nevezd "htCsillagok3"-nak, és használj egy sötétebb szürke színt a csillagoknak. Ehhez egy kicsivel több csillagot adhatsz, körülbelül 10-et. Aztán jelöld be a "Transparent" dobozt, és nyisd meg a szobát. Add hozzá ezt az új hátteret "Background 2"-ként, és állítsd a "Vert. speed"-et 1-re.

Végül próbáld ki a játékot. Elég jól néz ki, ugye? Egy kis képzelőerővel láthatod a "mély" világűrt elmozogni a repülő alatt.

6.3 Ellenséges tűz

Ideje megcsinálni, hogy az ellenségek visszalőjenek rád. Nem lesz túl bonyolult, mivel már érted egy kicsit a GML-t. Az elképzelés az, hogy az ellenséges repülők véletlen időközönként egy lövedéket lőnek rád. Valahányszor egy lövedék eltalál, az energiád 10 egységgel csökken.

Először is szükségünk van egy új lövedék objektumra. Nem kellene ugyanazt használni, amit a játékos repülője lő ki, még akkor sem, ha annak a sprite-ját használjuk fel újra.

Hozz létre egy új objektumot, nevezd el "objEllenseg1Lovedek"-nek, és válaszd ki számára az "sprLovedek" sprite-ot. Most arra van szükségünk, hogy az ellenséges repülő kilője ezt a lövedéket a játékosra. Ehhez hozz létre egy új szkriptet, amit nevezz el "Ellenseg1Loves"-nek. Ennek a szkriptnek kell létrehoznia egy lövedékpéldányt, és beállítania az ellenség riasztó időzítőjét, hogy egy másik lövedék létrejöhessen.

Írd ezt a szkriptbe:

instance_create(x, y, objEllenseg1Lovedek);

Ez az "objEllenseg1Lovedek" objektum egy példányát fogja létrehozni az ellenséges repülő koordinátáira. De ez nem megy még semerre sem. Adnunk kell neki egy sebességértéket és az irányt a játékos felé. Szerencsére van egy GM függvény, amellyel ez nagyon könnyen elvégezhető. Ez a "move_towards_point" (Mozgás egy pont felé), amiben be lehet állítani, hogy mely koordináták felé kell a példánynak mozognia, és milyen gyorsan.

Most azt gondolod, hogy ez könnyű, mert csak be kell írni a függvényt. De ez nem így van, mert ha csak beírod ezt a függvényt, akkor az ellenséges repülő kezd el mozogni a játékos felé, és nem a lövedéke. A megoldáshoz szükségünk van a létrejött lövedékpéldány azonosítójára. Tehát változtasd meg erre az előbb írt sort:

lovedekem = instance_create(x, y, objEllenseg1Lovedek);

Most megkaptuk az új lövedék azonosítóját, és használhatjuk egy "with" utasításban, így:

with (lovedekem) move_towards_point(objJatekos.x, objJatekos.y, 5);

Ez el fogja indítani a lövedéket a játékos felé, 5 sebességértékkel. Jó. Ideje beállítani a riasztást, hogy némi idő elteltével hozzon létre új lövedéket. Add ezt a sort a kódhoz:

alarm[0] = 60 + random(60);

Ez egy kicsit különösnek tűnhet, de itt azt csináltuk, hogy hozzáadtuk a 60-as értéket, és egy 0 és 59.99999 közötti véletlenértéket. Ezért a következő lövedék valamikor 2 és 4 másodperc közti idő elteltével jön létre (60 és 119.99999 képkockák). Itt nem törődök a tizedesjegyekkel és hasonlókkal, mert a játékban nem fogjuk észrevenni az ebből eredő eltérést.

Most az "Ellenseg1Loves" szkriptnek így kell kinéznie:

lovedekem = instance_create(x, y, objEllenseg1Lovedek);
with (lovedekem) move_towards_point(objJatekos.x, objJatekos.y, 5);
alarm[0] = 60 + random(60);

Hátra van még az "objEllenseg1" objektum "alarm 0" eseményéhez a szkript hozzárendelése. Ezt mostanra már tudnod kell. Arról is meg kell győződnünk, hogy a szkript első alkalommal fut. Ez elvégezhető a risztás beállításával abban a szkriptben, ami az elelnségpéldány létrehozásakor fut. Ez az "Ellenseg1Indit" szkript. Nyisd meg ezt a szkriptet, és add hozzá a következő sort:

alarm[0] = random(60);

Itt nem törődtünk azzal, hogy a "60" is ott legyen a véletlenfüggvény előtt. Tedd ki ha akarod, de akkor az ellenséges repülők csak 2 másodperc eltelével fognak lőni, míg most az első lövésüket 0 és 2 másodperc között adják le a létrejöttük után.

A lövedékeknek, ahogyan a játékos lövedékeinek, el kellene tűnniük, amikor a képernyőn kívülre kerültek. Az "objEllenseg1Lovedek" objektumban nyisd meg az "Outside" eseményt, és az akciók közül add hozzá a "Destroy the instance" (Példány megsemmisítése) ikont. Fogadd el az alapértelmezett beállításokat, és kattints az OK-ra.

Végül, a lövedéknek károsítania kell a játékost. Hozz létre egy új szkriptet, aminek legyen a neve "JatekosLovedek1Utkozes". Itt meg kell semmisítenünk a lövedék példányt, valamint csökkentenünk kell a játékos energiáját. Azt is ellenőriznünk kell, hogy a játékos energiája 0 vagy kevesebb-e, és ezesetben meg kell semmisíteni a játékost. Mindez azt jelenti, hogy a kódnak nagyjából ugyanolyannak kell lennie, mint a "JatekosEllenseg1Utkozes" szkriptben. Írd az új szkriptbe ezt a kódot:

// A lövedék megsemmisítése
with (other) instance_destroy();
// A játékos energiájának csökentése
energia -= global.lovedek1Karokozas;
// Annak ellenőrzése, hogy az energia 0 vagy kisebb annál.
if (energia <= 0)
{
instance_destroy();
}

Vedd észre, hogy a "global.lovedek1Karokozas" globális változót használtam a játékos energiájának csökkentésére. Ennek a változónak már meghatározottnak kell lenni, ami a "JatekKezdet" szkripthez való hozzáadásával történik, tehát add ezt a sort ahhoz a szkripthez:

global.lovedek1Karokozas = 10;

Ez azt jelenti, hogy amikor az ellenség lövedéke eltalálja a játékost, akkor az energia 10 egységgel csökken.

Add hozzá a "JatekosLovedek1Utkozes" szkriptet az "objJatekos" objektumhoz, az ütközés eseményben az "objEllenseg1Lovedek" objektummal.

Hasonlítsuk össze a "JatekosLovedek1Utkozes" és a "JatekosEllenseg1Utkozes" szkripteket. Az utolsó "if" utasítás és a tartalma ugyanúgy néz ki mindkét szkriptben.

Mivel ugyanazt a kódblokkot egynél több helyen használjuk, jó ötlet elhelyezni a saját szkriptjében. Tehát hozz létre egy új szkriptet, és nevezd el "JatekosEnergiaEllenorzese"-nek. Másold az egész "if" utasítást a "JatekosLovedek1Utkozes" szkriptből az új szkriptbe, hogy az így nézzen ki:

// Annak ellenőrzése, hogy az energia 0 vagy kisebb annál.
if (energia <= 0)
{
instance_destroy();
}

Aztán töröljük az "if" utasításokat a másik két szkriptből, és helyette hívjuk ezt az új szkriptet. Csak azért, hogy megmutassam, hogyan lehet egy szkriptet hívni egy másikból. Abban a két szkriptben az "if" utasítások helyett ennek kell lennie:

JatekosEnergiaEllenorzese();

Ott van. Ilyen könnyű egy szkriptet használni egy másikból.

A biztonság kedvéért itt van a "JatekosLovedek1Utkozes" szkript teljes tartalma:

// A lövedék megsemmisítése
with (other) instance_destroy();
// A játékos energiájának csökentése
energia -= global.lovedek1Karokozas;
// Annak ellenőrzése, hogy az energia 0 vagy kisebb annál.
JatekosEnergiaEllenorzese();

És a "JatekosEllenseg1Utkozes" szkript tartalma:

with (other) instance_destroy();
energia -= global.ellenseg1Karokozas;
JatekosEnergiaEllenorzese();

Mentsd el a játékot és próbáld ki. Amikor az elelnséges repülők rádlőnek és eltalálnak, az energia csökkenni fog, míg végül a repülő megsemmisül. Jó. De várj! Amikor a játékos repülője megsemmisül, egy hibaüzenet bukkan fel az "Unknown variable or function" felirattal és a "move_towards_point" függvényre való utalással, amikor az ellenség lövedéke létrejön.

Ennek az az oka, hogy azt csináltuk, hogy a lövedékek menjenek a játékos repülője felé, és amikor a repülő megsemmisült, a lövedékek már nem tudják, hogy merre kell menni. Vagy, programozói nyelvhasználattal élve, egy már nem létező példányra hivatkozunk. Tehát ellenőriznünk kell a meglétét, mielőtt rálövünk.

Nyisd meg az "Ellenseg1Loves" szkriptet. Ez az, ahol az ellenség lövedéke létrejön. Ellenőriznünk kell, hogy elérhető-e az "objJatekos" egy példánya. Ezt az "instance_exists" függvénnyel végezhetjük el, melynek ez a meghatározása:

instance_exists(obj) - létezik-e az obj objektumtípus tagja. Obj lehet egy tárgy, példány id, vagy az all kulcsszó.

Tehát az "if" utasítást és az "instance_exists" függvényt kell használnunk a szkriptben. Szerkeszd át ilyenre a szkriptet:

if (instance_exists(objJatekos)) then
{
lovedekem = instance_create(x, y, objEllenseg1Lovedek);
with (lovedekem) move_towards_point(objJatekos.x, objJatekos.y, 5);
}
alarm[0] = 60 + random(60);

Vedd észre, hogy az "alarm[0]" beállítása most nem az "if" utasításon belül van. Azért, mert azt akarjuk, hogy a szkript hozzon működésbe egy új riasztás eseményt még akkor is, ha a játékos repülője abban az időpontban nem létezik.

Szép. Ez csak akkor fogja létrehozni és mozgatni a lövedéket, ha a játékos repülője létezik.

6.4 Az élet értelme

Hogy teljessé tegyük ezt a programozási oktató kézikönyvet, úgy gondolom, jó ötlet adni néhány életet a játékhoz, ahogyan pontozást is.

Nyisd meg a "LetrehozJatekost" szkriptet. Ez az, ahol a játékos példány létrejön az "objJatekosVezerlo" által. Add hozzá ezt a sort:

jatekosEletei = 3;

Ez az életek számát jelenti, ami az "objJatekosVezerlo" objektum példányában tárolódik. (Megjegyzés: ehelyett használható a GM beépített "lives" változója is.)

Most csökkentenünk kell az életek számát mindig, amikor a játékos felrobban. Ezt a "JatekosEnergiaEllenorzese" függvényben végezhetjük el. Nyisd meg.

Itt az "if" utasításon belül akarjuk csökkenteni az "objJatekosVezerlo" életeinek számát. Arról is meg akarunk bizonyosodni, hogy a játékos kis idő múlva újra létrejön, amihez az "objJatekosVezerlo" riasztóját fogjuk használni. Tehát az "if" utasításon belül, az "instance_destroy" függvény előtt, add a szkripthez ezeket a sorokat:

objJatekosVezerlo.jatekosEletei -= 1;
objJatekosVezerlo.alarm[0] = 60;

Ez csökkenteni fogja az életek számát 1-gyel, és beállítja az "objJatekosVezerlo" alarm 0 eseményét, hogy jelezzen 60 képkocka (2 másodperc) elteltével.

Tehát most az egész "JatekosEnergiaEllenorzese" szkriptnek így kell kinéznie:

if (energia <= 0)
{
objJatekosVezerlo.jatekosEletei -= 1;
objJatekosVezerlo.alarm[0] = 60;
instance_destroy();
}

Ideje létrehozni egy új szkriptet, ami felügyeli az új játékos létrehozását, bármennyi élete is van még. Legyen a szkript neve "EletekEllenorzese". Itt ellenőriznünk kell, hogy a játékosnak van-e még élete, és ha igen, akkor létrehozni a játékos objektum egy új példányát. Gépeld be ezt a kódot:

if (jatekosEletei > 0)
{
jatekosom = instance_create(screen_width / 2, 400, objJatekos);
}
else
{
game_end();
}

Itt mutattam be az "else" (Különben) utasítást. Ezt egy "if" utasítás után lehet használni. Úgy működik, hogy ha az "if" utasításban a kifejezés (esetünkben a jatekosEletei > 0) nem igaz, akkor az "else" ágon belüli utasítások hajtódnak végre. Ebben a példában ez azt jelenti, hogy ha jatekosEletei nem nagyobb, mint 0, akkor a játék véget ér. Erre való a "game_end()" függvény.

Most ezt az új szkriptet hozzá kell adnunk az "objJatekosVezerlo" objektum "Alarm 0" eseményéhez. Tedd meg.

Mentsd el és próbáld ki a játékot. Most három életednek kell lennie, amit használhatsz, mielőtt a játék véget ér.

Jó lenne az életeket valahogy megmutatni a felhasználónak.

Ehhez nyisd meg az "EnergiacsikRajzolas" szkriptet. Ez az, ahol az energiacsík kirajzolódik. Hogyan lehet kirajzolni a játékos repülőjének kicsinyített mását az egyes életekként az energiacsík után?

Válaszd ki az "sprJatekos" sprite-ot, és kattints jobb gombbal a nevére. A felbukkanó menüből válaszd a "Duplicate" (Kettőzés) menüpontot. Ez a játékos sprite egy másolatát fogja létrehozni. Nyisd meg az új sprite-ot, és nevezd el "sprElet"-nek, majd kattints az "Edit Sprite" gombra. Válaszd a menüből a "Transform -> Stretch"-et. Gépeld be az "50%"-ot a "width" és "height" dobozokba, majd a "Quality" (minőség) csoportból válaszd az "Excellent"-et (Kiváló). Kattints az OK-ra, majd újra az OK-ra. Most van egy kicsinyített változata a játékos sprite-nak, amit jól fel lehet használni élet sprite-ként. Még meg kell változtatnunk a sprite eredetét, hogy középre igazított legyen, tehát az "Origin" részben az X legyen 13 és az Y 19. Végül kattints az OK-ra az ablak bezárásához.

Menjünk vissza az "EnergiacsikRajzolas" szkriptbe. Egy sprite képernyőn való kirajzolásához a "draw_sprite" függvényt fogjuk használni. Itt a meghatározása:

draw_sprite(n,alkep,x,y) - az n sprite alkep alképének (-1=pillanatnyi) kirajzolása az (x,y) helyre.

Az "sprElet" sprite-ot fogjuk használni, és annak első alképét, amely a "0" sorszámú. Tehát a függvényt így fogjuk használni (a koordináták még nincsenek maghatározva):

draw_sprite(sprElet, 0, valamilyenXhely, valamilyenYhely);

Egy új utasítást is fogunk használni, mégpedig a "for" utasítást, az életsprite-ok pontos számának kirajzolásához. Nézd át a GM súgójában a "for" utasítás meghatározását.

A "for" ciklusok utasítások ismétlésére használhatók. Egy példa a "for" utasításra:

szam = 8;
for (i = 0; i < 5; i += 1)
{
szam += 1;
}

Ez a program először is beállítja a "szam" változó értékét 8-ra. Aztán belép a "for" ciklusba, amely hozzáadja az "1" számot néhányszor a "szam" változóhoz. De mennyiszer? Az eredmény az, hogy a változó szám 13 lesz, amikor a ciklus végez. Ami azt jelenti, hogy a ciklus 5-ször futott le.

Amikor a "for" ciklus először fut, akkor az "i" változó 0-ra állítódik. Aztán a "for" utasítás zárójelei közötti középső kifejezés kerül elelnőrzésre (i < 5). Ha ez igaz, akkor a "for" cikluson belüli utasítás hajtódik végre (szam += 1). Végül a "for" utasítás zárójelei közötti utolsó kifejezés (i += 1) hajtódik végre, amely az "i" változó értékét 1-re állítja. Ismét a középső kifejezés (i < 5) ellenőrződik le. Ha ez még igaz, akkor megint végrehajtódik a következő két utasítás. Ez újra megtörténik, amíg az "i" változó értéke "5" nem lesz. Akkor az "i < 5" kifejezés már nem igaz, és a "for" ciklus kilép. Jó.

Amit akarunk az az, hogy annyi sprite rajzolódjon ki, amennyi a "jatekosEletei" változó értéke. Ez elvégezhető a következő kód hozzáadásával az "EnergiacsikRajzolas" szkript végére:

for (i = 0; i < jatekosEletei; i +=1)
{
draw_sprite(sprElet, 0, 140 + 30 * i, screen_height - 25);
}

Hűha, ez egy kicsit sok. Először is, a "for" ciklus annyiszor hajtódik végre, amennyi a "jatekosEletei" változó értéke. Amikor a "for" ciklus először végrehajtódik, akkor az "i" változó értéke 0 lesz. Ez atr jelenti, hogy az első kirajzolandó sprite x koordinátája 140+30*0 lesz, ami 140. A "for" ciklus következő végrehajtásakor az "i" értéke 1. Ezért a második sprite x koordinátája 140+30*1 lesz, ami 170. Azután az "i" 3 lesz, és a "for" ciklus kilép. Az y koordináta mindig ugyanaz, 25 képpontnyira a képernyő aljától.

Ez azt jelenti, hogy három sprite lesz kirajzolva egymás után a képernyő bal alsó sarkába, az energiacsík után. Amikor a játékos elveszít egy életet, a "jatekosEletei" változó értéke 2 lesz, és a "for" ciklus csak kétszer fut le, ezért csak két sprite rajzolódik ki.

6.5 Pontozás

A pontozási részt először az utolsó bekezdésbe szándékoztam tenni, de végül úgy döntöttem, hogy ide rakom, a saját bekezdésébe. Természetesen adnunk kell némi pontozást a játékhoz, különben unalmas lehet az ellenségek lelövése.

Erre a beépített "score" változót fogjuk használni. Ez a pontot önműködően megjeleníti az ablak címsorában. (Megjegyzés: az újabb GM-ekben be kell állítani, hogy látszódjon-e a címsorban a pont (score), az energia (health), és az élet (lives).)

Először is 0-ra kell állítanunk a pontszámot a játék kezdésekor. Nyisd meg a "JatekKezdet" szkriptet, és add hozzá ezt a sort:

score = 0;

El kell döntenünk azt is, hogy a játékos mennyi pontot kapjon egy ellenség megsemmisítéséért. Adjunk ezért 100 pontot a játékosnak. Add hozzá a következő sort a "JatekKezdet" szkripthez:

global.ellenseg1Pont = 100;

Jó. Ezentúl hozzá kell adnunk ezt a pontot a "score" változóhoz, valahányszor a játékos megsemmisít egy ellenséget. Ez két szkriptben történik meg. Először nyisd meg a "JatekosEllenseg1Utkozes" szkriptet, és add a végéhez a következő sort:

score += global.ellenseg1Pont;

Aztán nyisd meg a "LovedekEllensegUtkozes" szkriptet. Itt van egy kis ravaszkodás, mivel a ponthozzáadást az "if" utasításon belülre helyeztük el. Hogy biztosan ne hibázz, itt az egész szkript:

with (other)
{
// Az ellenség energiájának csökkentése
energia -= global.lovedekKarokozasaEllenseg1nek;
// Annak ellenőrzése, hogy az ellenség energiája 0 vagy kisebb-e már
if (energia <= 0)
{
// Ha annyi, akkor az ellenség megsemmisítése.
instance_destroy();
//ÚJ!!! Pont adása a játékosnak
score += global.ellenseg1Pont;
}
}
// Ennek a lövedéknek a megsemmisítése
instance_destroy();

Ennyi volt. Mentsd el a játékot, és próbáld ki.

 

7. Záró szavak

7.1 Az oktató kézikönyv vége

Itt a vége a GM programozásról szóló oktató kézikönyvnek. Remélem, élvezted az olvasását és követted az utasításokat, de leginkább azt remélem, hogy tanultál belőle valamit. A GM-rel igazán jó játékokat lehet készíteni, és én nagyon várom, hogy láthassam az általad készített játékokat.

Ha lesz időm, és kapok ösztönzést, akkor készítek ennek egy folytatást, ahol beljebb hatolok a haladó programozásba.

7.2 Kapcsolat

Ha van valami kérdésed, vagy csak el akarod olvasni, hogy mit ír a többi GM felhasználó, akkor mindig szívesen látunk a hivatalos GM közösség fórumán, ahová a GM honlapján juthatsz el:
http://www.gamemaker.nl

Az én jelenlegi honlapom, ami nem néz ki túl gyönyörűen, de tartalmaz néhány hasznos dolgot, itt található: http://hem.passagen.se/birchdale/carl
Küldj nekem egy e-mailt, ha hibát vagy valami hasonlót találsz ebben az írásban, és én megpróbálom kijavítani. Az elektronikus levélcímemet a lap alján találhatod.

7.3 Viszontlátásra

Végül szeretnék ismét köszönetet mondani mindenkinek, aki segített nekem ennek a dokumentumnak az elkészítésében.

SEMMI SEM LEHETETLEN, KIVÉVE KERESZTÜLSÍELNI EGY FORGÓAJTÓN

Üdvözlettel:

Carl Gustafsson

Karlskrona, Svédország

carl.gustafsson@home.se