Útmutató kezdőknek a Game Maker programozáshoz

Ez egy oktató kézikönyv a Game Makerrel (GM) való programozás elindulásához azon kezdők számára, akiknek egyáltalán nincs, vagy csak kevés ismeretük van a számítógépprogramozási nyelvekről. Ennek írásakor a GM 4.0-t használtam, de bizonyára alkalmazható az újabb változatoknál is.

Írta: Carl Gustafsson (carl.gustafsson@home.se), 2002. 03. 05 (Második átdolgozás ideje: 2002. 12. 17)
Magyarra fordította (és a zárójelekbeni megjegyzéseket tette):
itamas@citromail.hu, 2005. augusztus.
A Game Makert készítette: Mark Overmars

1. Előszó

1.1 Köszönetnyilvánítások

Ehelyütt szeretnék teljes szívből köszönetet mondani Mark Overmarsnak ezért a csodálatos játékfejlesztő eszköznek a megalkotásáért. Köszönöm Mark!

Köszönet Martijnnak, hogy átalakította ezt az útmutatót Wordból HTML formátumba, és közzétette a http://www.gamecreators.nl honlapon.

Köszönet a GM közösség minden tagjának, akik nagy segítségemre voltak nekem és egymásnak a játékfejlesztéssel kapcsolatos tanácsokkal és javaslatokkal.

Végül hálás vagyok minden megjegyzésért és javaslatért, amiket erre a dokumentumra vonatkozóan kaptam számos GM felhasználótól.

1.2 A Game Makerről

A GM-t Mark Overmars írta. Ez egy teljes értékű játékfejlesztő eszköz, amellyel 2-dimenziós számítógépes játékokat lehet készíteni Microsoft Windows rendszerek alá. A program letölthető a GM honlapjáról: http://www.gamemaker.nl

Az alkalmazás tartalmaz egy beépített rajzolóprogramot a sprite-ok létrehozására, egy fogd-és-vidd felületet, egy beépített programozási nyelvet, amely hasonló az olyan jól ismert programozási nyelvekhez, mint a C/C++, Pascal és Basic.

A GM-rel kódsorok használata nélkül is lehetséges számítógépes játékokat készíteni, köszönhetően a fogd-és-vidd ikonoknak, amelyek a játék eseményeit és akcióit képviselik. Azonban a fejlettebb játékok készítéséhez és a GM összes lehetőségének kihasználásához feltétlenül szükséges ennek a beépített programozási nyelvnek, a Game Maker Language-nek (GML) a használata.

1.3 A dokumentum szándéka

Ez az útmutató azért íródott, hogy megpróbálja bemutatni a GML-használatot azoknak a felhasználóknak, akik eddig a fogd-és-vidd módszert használták a játékkészítéshez.

 

2. Egy játék készítése

Valamilyen játékot szükséges készítenünk ahhoz, hogy megérthessük a GML-elvet. A kezdetekben utalok a grafikus fogd-és-vidd ikonokra, és összehasonlítom ezeket a GML kóddal. Tehát indítsd el a GM-t, és csinálj egy üres játékot (File -> New, de ezt biztosan tudod már).

2.1 Néhány sprite

Ahhoz, hogy lássunk valamit a játékunkban, szükségünk van néhány sprite-ra. Ha nem tudod, hogy mi az a sprite, akkor nézd át a GM felhasználói kézikönyvének, vagyis súgójának ezzel kapcsolatos fejezetét. Néhány sprite-ot már a GM is tartalmaz, amik annak telepítése során kerülnek fel a Sprites mappába.

A játékos számára az "SR71.bmp" sprite-ot fogjuk használni, ami a GM "Sprites \ Transport" mappáján belül található (Megjegyzés: ezek már alapban nem részei a GM-nek, hanem külön erőforráscsomagokban (resource pack) tölthetők le a GM honlapjáról). A kép így néz ki:

Ó, igen! Az SR-71 Blackbird a kedvenc repülőgépem! Adjunk hozzá az erőforrásokhoz egy új sprite-ot. A név szövegdobozába írjuk be, hogy "sprJatekos". A sprite-jaim elnevezésekor mindig használni fogom az "spr" előtagot, mert hibákat okozhat, ha egy sprite neve megegyezik egy objektuméval. Jó szokásnak tartom az ilyen sprite névadási rendszert, mert amikor az objektum létrejön, nem kell aggódni amiatt, hogy a név megegyezik egy sprite névvel. Egy másik jó dolog ezzel kapcsolatban, hogy később, amikor átnézed a kódodat, például hibakereséskor, azonnal tudni fogod, hogy egy változónévvel egy sprite-ra utalsz-e, vagy nem. Az objektumoknál javaslom az "obj" előtag használatát.

Rendben, tehát elnevezted a sprite-ot? Jó. Most kattints a "Load Sprite" gombra egy sprite fájlból való betöltéséhez. A megjelenő fájlkiválasztó párbeszédablakban keresd ki az "SR71.bmp" képfájlt, és válaszd ki.

Győződj meg arról, hogy a "Transparent", vagyis "Átlátszó" kiválasztódoboza be van jelölve (vagyis kipipálva kell lennie). Máskülönben jelöld be. Ez a sprite bizonyos részeit átlátszóvá teszi. Mely részeit? Minden képpont átlátszó lesz később a képernyőn való kirajzolás során, amelyik ugyanolyan színű, mint a sprite bal alsó sarkának képpontja.

A legtöbb játékban van némi lövöldözés. A lövéshez szükségünk van lövedékekre. Hozz létre egy új sprite-ot, és nevezd el "sprLovedek"-nek. A lövedék képének használjunk egy piros labdát, ehhez a "ball2.gif" fájlt töltsük be a sprite-hoz (). Ennél is győződj meg, hogy a sprite átlátszó.

Ezekre a sprite-okra lesz most szükségünk.

2.2 Objektumok létrehozása

A sprite-ok, amiket létrehoztunk, csak buta képek. Rendben, elismerem, hogy van némi intelligenciájuk, például az átlátszóságuk és információ a foglalatukról, de igazából nem csinálnak semmit. A GM játékokban az Objektumok hajtanak végre cselekvéseket. Elolvastad a GM súgójában? Ha nem, akkor olvasd el, mert az magyarázza el az objektumok értelmét.

Hozz létre egy új objektumot, és nevezd el "objJatekos"-nak, és ne csak "Jatekos"-nak. A sprite-kiválasztó dobozában válaszd ki a "sprJatekos"-t. Most az "objJatekos" objektumunk olyan kinézetű lesz, mint az "sprJatekos" sprite. Nagyszerű!

Ideje létrehozni a lövedék objektumot. Hozz létre egy új objektumot, nevezd el "objLovedek"-nek, és válaszd ki hozzá az "sprLovedek" sprite-ot képnek.

2.3 A tökéletesítés helye

Most szükségünk van egy helyre, ahol az objektumok tevékenykedhetnek. Ez egy szobában (room) valósul meg.

Hozz létre egy új szobát, és nevezd el "Room1"-nek (HOPPÁ! Nincs a nevében szóköz). Megpróbálhatod elnevezni "Room 1"-nek (szóközzel az "1" előtt), de ez a GML-ben hibákat fog okozni, tehát soha ne használj szóközöket az objektumaid, sprite-jaid, szobáid, stb. elnevezésében. Ha két szót el kell választani a névben, akkor helyette használd az aláhúzás jelet "_".

Kattints a "Background" (háttér) fülre a "Room1"-ben a háttér beállításainak megtekintéséhez. Győződj meg arról, hogy a "Draw background color" (háttérszín rajzolása) engedélyezve van, és kattints a "background color"-ra. Válassz egy szép zöld (mint a fű) színt. Most az egész szobádnak zöldnek kell lennie. A szoba méretének 640 szélesnek 480 magasnak kell lennie. Ha nem, akkor állítsd be ezeket az értékeket. Az alapértelmezett sebesség (Speed) beállítás a 30, mely meglehetősen normális a játékokhoz. Innentől az "FPS" kifejezés a Frames Per Second-ot, a másodpercenkénti képkockákat jelenti; ne téveszd össze a First Person Shooter-rel.

Most elhelyezzük az "objJatekos" objektumunk egy példányát a szobában. Kattints a szoba tulajdonságok ablakban az "Objects" fülre, majd az "Objects to add with left mouse" kiválasztódobozban válaszd ki az "objJatekos"-t. Most kattints EGYSZER a szoba közepén. Ezzel el kellett hogy helyeződjön egy "objJatekos" példány ott, ahová kattintottál. Ha többször kattintottál, vagy túl sokat mozgattad az egeret a kattintáskor, akkor lehet, hogy egynél több "objJatekos" példány jött létre a szobában. Az eltávolításukhoz kattints rájuk jobb gombbal. Bizonyosodj meg arról, hogy csak egy "objJatekos" példány van a szobában.

Itt tartunk egy kis szünetet, hogy elmélkedjünk az objektum és a példány megnevezéseken. Egy hasonlatot fogok használni a megmagyarázásához. Remélem beválik. Az objektumod hasonló egy sütemény-nyomóformához, amit mézeskalács sütik készítésére használsz. Amikor elhelyezed az objektumaidat a szobában, valójában objektumok "példányait" helyezed el, ami olyan, mint amikor a sütemény-nyomóformával a tésztából kinyomod a sütiket. Minden példány úgy fog viselkedni, ahogy az az objektumban meg van határozva, de minden példánynak lesznek saját helyi változói, például x és y helyzete és sebessége (később még esik több szó a változókról). Csakúgy, mint ahogy a formázóval kinyomott minden mézeskalács sütemény ugyanolyan alakú, de különböző kinézetet adhatsz mindegyiknek némi cukormázzal. Hé! Megéheztem! Vissza a játékhoz.

Kattints az OK-ra a szoba ablakának bezárásához.

2.4 Menteni, menteni, menteni!

Majdnem készen vagyunk, hogy elindítsuk a játékot, csak ellenőrizd le, hogy az "objJatekos" példány ott van-e a játék szobájában. De mielőtt futtatjuk, elmentjük! Ne felejtsd el gyakran menteni a játékodat, és futtatás előtt mindig menteni, mert megtörténhet, hogy a számítógép teljesen lefagy, és újra kell indítani. Ez nem túl mókás, ha a játékod nincs elmentve.

Rendben. Mentsd el valamilyen néven (az enyémet "GMLoktato"-nak neveztem).

Itt az ideje az elindításának. Ehhez nyomd le az F5-öt, vagy kattints a zöld nyílra.

Jó. Most elkészítettük egy Windows-os játék alapját. Ha nem látod a zöld hátteret, középen az "objJatekos" egy példányával, akkor kihagytál valamit, ami az előbbi leírásban volt, vagy valami rossz. Olvasd el újra, ha valamiről lemaradtál.

Zárd be a játékablakot az [Esc] lenyomásával, vagy az ablak "Bezárás" ikonjára való kattintással.

2.5 Akció

Hogy az alkotásunkat játéknak hívhassuk, szükséges valahogy kölcsönhatásba lépnünk vele, és valamilyen mozgatást is lehetővé tenni. Azzal kezdjük, hogy lehetővé tesszük az "objJatekos" mozgatását a billentyűzet kurzorgombjaival.

Térjünk vissza a GM-be, és kattintsunk duplán az "objJatekos"-on a megnyitásához. Most néhány akciót fogunk létrehozni. Amikor valami történik egy objektumon, azt nevezik eseménynek. Akciónak pedig az objektum erre az eseményre adott válaszát nevezik. Amit akarunk, az az, hogy amikor megnyomjuk valamelyik kurzorgombot, akkor az "objJatekos" induljon el abba az irányba.

Kattints az "Add event" gombra az "objJatekos" ablakában. Ez előhozza az "eseménykiválasztó"-nak nevezett ablakot. Itt lehet kiválasztani azokat az eseményeket, amelyekre az objektumnak reagálnia kell. Kattints a "Keyboard" (Billentyűzet) gombra. A felbukkanó listából válaszd a "<Left>" (Bal) billentyűt. Most az objektum eseménylistájában lennie kell egy "<Left>" (Bal kurzornyílgomb) eseménynek.

Most meghatározhatunk akciókat, amelyeknek meg kell történniük a bal kurzorgomb lenyomásakor. Az akciók listája az objektum ablakának jobb oldalán van. Keresd ki a "Start moving in a direction"-nek (Mozgás indítása egy irányba) nevezett akciót, amit 8 piros nyíl jelképez. Húzd ezt az ikont az esemény- és akciólista közötti fehér területre. Ezt a területet nevezzük akciósornak.

Egy ablak fog felbukkanni, amikor elengeded az akciót. Ebben az ablakban megadhatod a paramétereket, amelyek szükségesek az akció meghatározásához. Kattints a balra mutató nyílra az irány kiválasztásához, és állítsd a sebességet (speed) 5-re. Végül kattints az "OK"-ra.

Ami most rendben van, az annak meghatározása, hogy a bal gomb lenyomásakor az "objJatekos" induljon el balra.

Most ennek alapján add az eseménylistához a "<Right>" (Jobb) gomb eseményt is, amihez szintén a "Start moving in a direction" akciót rendeld, amiben állítsd be a jobbra irányt, és a sebességet 5-re.

Ismételd ezt meg az "<Up>" (Föl) és "<Down>" (Le) gomboknál is, beállítva a megfelelő irányt.

Mentsd el a játékot, és indítsd el megint.

Most a kurzorgombok használatával tudod mozgatni a repülőt. Viszont nem lehet megállítani, és átlósan sem lehet mozgatni.

2.6 Az akciók finomítása

Most finomítani fogunk egy kicsit az akciókon, valamint hozzáadjuk a "lövés" akciót is.

Nyisd meg az "objJatekos" objektumablakát. Add hozzá a "<No key>" (Nincs gomb) eseményt, mely a többi "Keyboard" esemény között található. Ez az esemény akkor történik, amikor a billentyűzeten minden gomb fel van engedve. Add a "Start moving in a direction" akciót, és abban válaszd az irányok közepén levő négyzetet, majd kattints az OK-ra. Ez okozza azt, hogy az "objJatekos" megáll, ha nincs lenyomva gomb.

Egy lövés akció hozzáadásához el kell döntenünk, hogy melyik gomb legyen használva a lövedék kilövéséhez. Én rendszerint a [Szóköz] gombot hsználom erre.

Az "objJatekos" objektumablakában add hozzá a "<Space>" (Szóköz) gomb eseményt. Az akciólistából keresd ki a "Create an instance of an object" (Egy objektum példányának létrehozása) akciót (úgy néz ki, mint egy fénylő villanykörte), majd húzd az akciósor listába. A felbukkanó ablakban válaszd ki az "objLovedek" objektumot, és jelöld be a "Relative" (Viszonylagos) feliratú kiválasztódobozt. Ez azt jelenti, hogy az "objLovedek" objektum egy példánya ugyanazon a koordintájú helyen fog létrejönni, ahol az "objJatekos" objektum példánya van. Kattints az OK-ra.

Most egy lövedék létre fog jönni. De azt mozgatni is kell. Ehhez nyisd meg az "objLovedek" objektumablakát, és add hozzá a "Create" (Létrehozás) eseményt. Add meg az irányt: "Set direction and speed of motion" (Irány és mozgássebesség beállítása). Ezt az akciót 8 kék nyíl jelképezi. A felbukkanó ablakában gépeld be iránynak (direction) a 90-et, sebességnek (speed) pedig a 15-öt. Ennek eredményeképpen a lövedék elkezd mozogni a "90" irányba "15" sebességgel. Az irányok fokokban vannak mérve, és így működnek:

Tehát a 90 az egyenesen fölfelé irány.

Még egy dolgot rendezni kell a játék indítása előtt. Mi történik, ha a lövedék eléri a képernyő tetejét? Semmi. Csak megmarad "örökké". Így amikor már sok lövedék van kilőve, a számítógép memóriája megtelik a lövedékek adataival, ami mozgatja föl, föl, föl, és mi soha nem látjuk azokat. A dolog amit meg kell tennünk, hogy megbizonyosodjunk arról, hogy a lövedék megsemmisül, amint eléri a képernyő tetejét.

Keresd ki és add hozzá az "Outside room" (Szobán kívül) eseményt. Ez az esemény akkor történik, amikor egy példány a szobán kívülre kerül. Add hozzá a "Destroy the instance" (A példány megsemmisítése) akciót. Az alapértelmezett érték rendben van. Most a lövedék meg fog semmisülni, ha a képernyőn kívülre kerül.

Mentsd el, indítsd el a játékot, és próbálj mozogni, megállni és lőni.

 

3. A kódolás megkezdődik

Jól van. Ha követted a fenti útmutatásokat, akkor lennie kell egy kis játékodnak egy repülővel, ami mozog és lő. Amiért ezt csináltam, annak oka az, hogy most van neked valami a fogd-és-vidd módszerrel elkészítve, amit megcsinálhatunk kódolással is a javítás és megértés érdekében.

Kódolás. Sokak számára ijesztően hangozhat, míg mások szerint nagyszerű dolog. Valójában a kódolás hasonlóan csinálja a dolgokat, mint mi az előbb az ikonokkal és hasonlókkal, de helyette szöveggel. Persze lehet csinálni olyan játékkészítő programot, ahol mindent el lehet végezni ikonokkal, de ez különböző ikonok százait jelentheti, amelyek listája az akciósorban hosszabb lehet a képernyőnél, és lehetetlenség jól áttekinteni. (Megjegyzés: a jobb áttekinthetőség mellett az ikonok helyetti kódolás használatával a program forráskódjának (a .gmd illetve .gm6 kiterjesztésű fájl) méretét is csökkenthetjük.)

Tehát ez egy olyan helyzet, ahol egy kép NEM mond többet ezer szónál.

3.1 Az első változó

Az első dolog, amit megcsináltunk a fogd-és-vidd akciókkal, hogy a repülő mozogjon a kurzorgombok lenyomásakor. Most lecseréljük az akcióikonokat kódra.

Nyisd meg az "objJatekos" objektumot, és válaszd ki a "<Left>" gomb eseményt. Töröld a "Start moving in a direction" akciót az akciósorból, úgy, hogy kiválasztod, és lenyomod a [Del] gombot a billentyűzeten. Most keresd ki az "Execute a piece of code" (Egy kóddarab végrehajtása) akciót, és húzd át az akciósorba.

Ami most felbukkan, az egy üres ablaknak néz ki - egy KÓD ablak. Ez az, ahol begépelhetjük azt a kódot, amit a "<Left>" gomb eseményre válaszul kell végrehajtani.

Gépeld be a következő kódot:

direction = 180;
speed = 5;

Ami azt jelenti, hogy az "objJatekos" objektum pillanatnyi példányának "direction" változóját 180-ra, a "speed" változóját pedig 5-re állítottuk. Ezt nevezik értékadásnak a változóhoz. A változókról és az értékadásokról bővebben a GM súgójában olvashatsz.

Alapvetően egy változó egy helyet jelent a számítógép memóriájában, amely egy értéket tartalmazhat. Különböző változófajták vannak, amelyek tartalmazhatnak egy értéket. A GM különbséget tesz két eltérő változóinformációtípus: a számok és szövegek között. Egy szám csak egy egyszerű szám, mint:

1
5
2.21
63.132
24

A szövegek írásjelek listái, amik egyszeres vagy kétszeres idézőjelekkel vannak közrefogva, mint:

"Hello"
"Ez egy szöveg"
'Ez egy másik szöveg'

Az egyik "szép" jellegzetessége a GM-nek, hogy az egyszeres és kétszeres idézőjelek is használhatók egy szöveg meghatározására. Így könnyen berakható egy szövegbe egy egyszeres vagy kettős idézőjel. Gondolj csak bele: ha csak kettős idézőjelekkel adhadsz meg egy szöveget, hogyan mondhatod el a számítógépnek, hogy egy olyan szöveget akarsz, ami TARTALMAZ egy kettős idézőjelet? Ez a kód:

egyKisSzoveg = "És akkor "zihált" és elájult";

nagyon megtévesztő lehet a számítógép számára. Lehet, hogy két szövegnek tekinti, a "zihált" szóval közöttük. Ugyanígy egyszeres idézőjelekkel is. Helyette ezt a kódot kellene használni (figyeld meg a különbséget):

egyKisSzoveg = 'És akkor "zihált" és elájult';

Vissza a játékhoz.

A "direction" és "speed" változók beépített változók, és minden példánynak megvannak. Amikor változókat írunk így, akkor az úgynevezett helyi változókra hivatkozunk, amelyek egy objektum egy példányához tartoznak. Ez azt jelenti, hogy ha le akarjuk ellenőrizni a "direction" változó értékét, például az "objLovedek" objektum egy példányánál, akkor nem az 5-öt akarjuk látni, hanem egy másik értéket, ami az "objLovedek" példány sajátja.

A "direction" változó 180-ra állításával elmondtuk a GM-nek, hogy az irány, amelyben ennek a példánynak mozognia kell, a 180 (bal). A "speed" változó 5-re állítása arra utasítja a GM-t, hogy a példányt képkockánként 5 képponttal mozgassa a "direction" változó irányába. Rendben van?

Miért van egy pontosvessző minden sor végén? Ez közli a programértelmezővel (Megjegyzés: a GM és minden programfejlesztő eszköz azon része, amely a programot a számítógép számára értelmezhető gépi kódra fordítja), hogy ez az utasítás vége. Minden utasítás végén pontosvesszőnek kellene lennie. Olyasmi, mint a pont az emberek számára, ami a mondat végét jelenti. Egy utasítás egy számítógépprogramban olyasmi, mint az embereknél a mondat. Valójában a GML nem követeli meg a pontosvesszők használatát, megérti a kódot egyébként is, ha minden utasítás külön sorban van, de a "jó programozás" szempontjából azért használhatjuk a pontosvesszőket.

Most rendben vagyunk a <Left> eseménnyel. Kattints a zöld pipára a kód mentéséhez és az ablak bezárásához, máskülönben ez az ablak gátolja az összes többi ablakot. Be kell zárni, amikor kész vagy a szerkesztéssel.

3.2 Az első függvény

Menjünk a <Right> eseménybe.

Távolítsuk el a "Start moving in a direction" akciót a <Right> gomb eseményből, és adjuk helyette az "Execute a piece of code" akciót.

Meg tudnánk ezt hasonló módon csinálni, mint a <Left> eseménynél, a "direction" és "speed" változók beállításával, de hogy megtanuljunk más módokat is az ugyanolyan dolgok elvégzésére, csinálunk valami különbözőt. Egy függvényt fogunk használni.

Egy függvény egybefoglalt programutasítások gyűjteményére hasonlít, amit a függvénynév hívásával lehet végrehajtani.

Amikor a matematikatanárod függvényekről beszél, akkor valami ilyesmire gondol: y(x) = 4 + 3x.

Ez egy függvény meghatározása. A függvény neve az "y". A zárójelekben levő "x"-et nevezik az "y" függvény egy argumentumának.

A megoldás például y(5) esetén 19 (4+3*5). A "*" (csillag) írásjel általánosan használt a szorzás műveleti jeleként a programozási nyelvekben.

Ha ez egy számítógépprogram volt, hívni kell az "y" függvényt az "5" argumentummal.

Megjegyzés: más nyelvekben a "függvény" fogalmat máshogy nevezik, például eljárás, metódus, alprogram stb., de gyakorlatilag ugyanazt fejezik ki.

Szóval, ha megnézted a GM súgójában, ott van a "motion_set" függvény meghatározása, ami így néz ki:

motion_set(ir, seb)

A magyarázatban az áll, hogy ez a függvény fogja beállítani egy objektum sebességét "seb" argumentumra, és az irányát "ir" argumentumra. Rendben, tehát most használjuk ezt a függvényt a <Right> gomb eseményhez.

Megvan még a kódablak (üresen) a <Right> gomb akcióhoz? Jó. Különben kattints kettőt, hogy kinyisd újra.

Az üres kódablakba írd be:

motion_set(0, 5);

Most hívtuk a "motion_set" függvényt, a "0" és "5" argumentumértékekkel az "ir" és "seb" megadási helyén. És íme! Amikor a jobb kurzorgomb lenyomódik a játékban, a példány elindul a 0 (jobb) irányba 5 sebességértékkel.

Most ugyanazt a dolgot csináltuk, de két különböző módon. A helyzettől függ, hogy melyik módszer a legjobb. Ebben az esetben úgy gondolom, hogy a második, vagyis a függvényhívás a legjobb, de tegyük fel, hogy meg akarod változtatni a példány sebességét, de az irányát nem. Ezesetben könnyebb lehet csak egy új sebességértéket hozzárendelni a "speed" változóhoz.

Később ebben az útmutatóban majd meghatározzuk és használjuk a saját szkriptjeinket, vagyis függvényeinket, amelyek hasonló módon működnek, mint a beépített függvények.

3.3 További változók

Van egy harmadik módja annak, hogy megvalósítsuk azt, amit a <Right> és <Left> gomb eseményeknél csináltunk.

Válaszd az "<Up>" gomb eseményt az "objJatekos" objektum számára, és add az "Execute a piece of code" akciót az akciósorba, a kódablakba pedig írd be:

hspeed = 0;
vspeed = -5;

Mi volt ez? A "hspeed" és "vspeed" két további példa azokra a változókra, amelyek "beépítettek" egy objektumban. Ezek a változók határozzák meg a vízszintes (hspeed) és függőleges (vspeed) sebességét az objektumnak. Ha te egy matematikus vagy, akkor kijelentheted, hogy a "hspeed" és "vspeed" változók határozzák meg a sebességvektort derékszögű koordinátákban, míg a "direction" és "speed" változók ugyanezt sarokkoordinátákban.

Mindenesetre a "vspeed" függőleges sebesség -5-re állítása jelenti azt, hogy a példánynak fölfelé kell elindulnia. Mivel az y tengely egy számítógépképernyőn lefelé irányul, ezért egy példány fölfelé mozgatásához negatív függőleges sebességet kell megadni.

A vízszintes sebesség 0-ra van állítva, ami azt jelenti, hogy a példány egyáltalán nem mozoghat se balra, se jobbra.

Az egyetlen mozgatás most a lefelé-kurzornyíl gombnál van. Meg fogjuk ezt csinálni egy negyedik módon. Zárd be a kódablakot.

3.4 Az első szkript

Most megtanulod, hogy hogyan működik egy különálló szkript. Hozz létre egy új szkriptet (Menü: Add -> Script).

Ez elő fog hozni egy kódablakot, ami nagyon hasonlít az előzőekben már használthoz. A különbség az, hogy ennek az ablaknak a tetején van egy névdoboz. Gépeld be ide névnek, hogy "MozgatJatekostLe". A név akármi lehet, de célszerű, hogy jellemezze a szkript feladatát. Emlékezz; ne használj szóközöket a névben.

Gépeld be a következő kódot a szkriptbe:

hspeed = 0;
vspeed = 5;

Aztán, ha akarod, bezárhatod a szkriptet, de nyitva is hagyhatod.

Menj vissza az "objJatekos" objektumablakába, és válaszd a <Down> gomb eseményt. Távolítsd el az akciósorból az ott lévő akciót, és helyette add az "Execute a script" akciót.

Egy új ablak fog felbukkanni, ahol kiválaszthatod, hogy melyik szkriptnek kell futnia, és begépelhetsz számára néhány argumentumot is. A "script" feliratú szövegdoboz jobb oldalán van egy kiválasztóikon. Kattints rá, és felbukkan a szkriptek listája. Most csak egy, a "MozgatJatekostLe" szkript van benne, amit válassz ki.

Szkriptünk nem használ semmilyen argumentumot, tehát itt semmi mást nem kell megadnunk, ezért zárd be ezt az ablakot az OK-ra való kattintással.

Most már, amikor a játékos lenyomja a lefelé nyíl gombot, akkor az "objJatekos" példány hívni fogja a "MozgatJatekostLe" szkriptet, ami az "objJatekos"-t lefelé kezdi mozgatni.

Mi az értelme egy különálló kód, vagyis szkript készítésének? Ebben a kis példában nem igazán látni a szükségét, de ha a szkript nagyon hosszú, akkor könnyebb karbantartani, ha külön van, így nem kell megnyitni az objektumot, és kikeresni a megfelelő eseményt, hogy megtaláld és szerkeszd a szkriptet. Egyidejűleg több szkript is nyitva lehet.

A legfontosabb oka a szkriptek készítésének, hogy bármelyik objektum használhatja azokat. Ha az "objLovedek" objektum példányát lefelé kell mozgatni 5-ös sebességgel, ez elvégezhető ezzel az új szkripttel, és a szkript működhet az "objLovedek" példányon az "objJatekos" helyett. (Megjegyzés: magyarán, a programban többször használt kódot érdemesebb szkriptként megírni, és csak ezt a szkriptet hívni a felhasználás helyén ahelyett, hogy a kódot egyenként beszúrnánk minden ilyen helyre, fölöslegesen megnövelve ezzel a program méretét, és megnehezítve a dolgunkat, ha netán javítani kell benne valamit. Ha a szkriptben néhány érték eltérő az egyes hívási helyeken, akkor azokat argumentumként kell alkalmazni.)

Egy szkript hívható egy másik szkriptből is.

Majdnem elfeledkeztünk a <No key> (Nincs gomb) eseményről. Ez az, ahol a repülő megáll.

Zárd be a kódablakot, és válaszd a <No key> eseményt. Töröld a "Start moving in a direction" akciót, és add helyette az "Execute a piece of code" akciót. Írd be a következőt a kódablakba:

speed = 0;

Az irány nem számít, amikor a sebesség 0.

Ennyi.

Kicseréltük a grafikus fogd-és-vidd ikonokat kódszövegekre. Nem kellett sokat kódolni, ugye?

De még sok dolog tökéletesíthető kódolással, ezért folytatjuk még egy kicsit a játékunkon.

Mentsd el a játékodat, és próbáld ki. Ha mindent jól csináltál, akkor semmilyen különbséget nem fogsz észrevenni. A kódok ugyanúgy működnek, mint ahogy az ikonok.

3.5 Megszabadulás a <No key> eseménytől

Valójában a <No key> esemény nem jó egy játékos által irányított példány megállítására. Észreveheted, hogy ha lősz miközben mozogsz, és felengeded a mozgató gombot, de lenyomva tartod a lövés gombját, akkor még továbbra is mozogsz. Ennek oka az, hogy mivel lenyomva tartod a lövés gombját (a szóközt), ezért nincs <No key> esemény sem. És gondolhatod, mennyi <No key> eseményt kapnánk egy kétjátékos módú játékban. Nem sokat. Hogyan oldhatjuk ezt meg?
(Megjegyzés: a <No key> helyett a <Key release> (gomb felengedése) használata is célszerű, vagyis a mozgató gombok felengedésénél megállítani az objektumot.)

Ezen a módon oldhatjuk meg:

Nyisd meg az "objJatekos" objektumot, és válaszd ki a <No key> eseményt. Aztán kattints az eseménylista alatt levő "Delete" (Törlés) gombra. Ez eltávolítja a <No key> eseményt és az összes akcióit (előzőleg a "Yes"-re (Igen) kattintva hagyd ezt jóvá).

Válaszd a <Left> gomb eseményt, és kattints kettőt az "Execute a piece of code" akción az akciósorban. Ez felhozza a kódablakot.

Itt az "objJatekos" sebességének és irányának beállítása helyett csak a koordinátáit változtatjuk meg. Az objektum csak akkor mozog, amíg a gomb lenyomva van. A másik dolog, amit ezzel elérünk, hogy átlósan is mozoghatunk. Nagyszerű!

Tehát törölj mindent, ami a kódablakban van, és írd be helyette:

x = x - 5;

Ez azt jelenti, hogy az x változó értékét az értéke mínusz 5-re állítjuk, vagyis csökkentjük az értékét 5-tel.

Egy példány x változója tartalmazza az x-koordinátáját a szobában. Tehát az x változó 5-tel való csökkentésével a példány 5 képpontnyit fog balra mozogni. Ez megismétlődik a játék minden képkockájában, ameddig a játékos lenyomva tartja a bal nyílgombot. Pontosan ez az, amit akarunk.

Ugyanilyen dolgot akarunk a többi iránygomboknál is, ezért változtassuk meg a kódot a többi gomb számára is.

Az "objJatekos" objektum <Right> gomb eseményében kattints kettőt az akción a kódablak kinyitásához. Töröld a "motion_set" függvényhívást, és írd be helyette a következőt:

x += 5;

Ez ugyanaz, mintha azt írnánk, hogy

x = x + 5;

csak rövidebb.

Úgy gondolom, most már tudod, hogy mit kell csinálni a <Fel> és <Le> gomboknál. Az <Up> gomb eseménynél a kódnak a következőt kell tartalmaznia:

y -= 5;

a <Down> gombnál pedig ezt:

y += 5;

A <Down> gomb számára már készítettünk egy szkriptet. Megtarthatjuk és átszerkeszthetjük ezt ahelyett, hogy lecseréljük egy "Execute a piece of code" akcióra. Tehát csak nyisd meg a "MozgatJatekostLe" szkriptet, és változtasd meg benne a kódot az említettek szerint.

Most itt az ideje a játék elmentésének, és újbóli futtatásának. Két dolog van most a játékkal kapcsolatban, amire fel akarom hívni a figyelmed.

1. A repülő nem folytatja a mozgást, amikor a tűz gomb le van nyomva, de a mozgató gombok fel vannak engedve. Jó.
2. A repülő mozoghat átlósan is. Ez is jó.

3.6 Lövedéktöltési idő

Amikor lövedékeket lövünk a repülőből, észreveheted, hogy ezek megállás nélkül, egybefüggően jönnek. Előfordulhat, hogy ezt akarjuk, de nem hiszem, hogy jól néz ki. Tehát adjunk némi töltési időt a fegyvernek. Ehhez használni fogjuk az "objJatekos" objektum riasztás (alarm) tulajdnoságát, és egy helyi változót.

A töltési idő a következő elképzelésen alapul:
Amikor a [Szóköz] gomb le van nyomva, akkor egy lövedék kilövése előtt ellenőrzésre kerül egy helyi változó, hogy a fegyver tüzelésre kész-e, vagyis a repülő lőhet-e. Ezt a változót "lohet"-nek fogjuk nevezni. A "lohet" változó két érték közül vehet fel egyet. Az egyik a "true" (igaz), ami azt jelenti, hogy a fegyver lőhet, a másik pedig a "false" (hamis), ami azt jelenti, hogy a fegyver most nem tud tüzelni. Ha a "lohet" igaz a [Szóköz] lenyomásakor, akkor egy lövedék kilövésre kerül, és a "lohet" értéke hamisra állítódik. Ez azt jelenti, hogy amikor a [Szóköz] újra lenyomódik, a fegyver nem fog tüzelni. A "lohet" hamisra állításakor beállítunk egy riasztás időzítőt is. Amikor az időzítő lejár, visszaállítja a "lohet" változót igazra, és újra tudunk lőni. Ez ismételni fogja magát a játék egész ideje alatt.

Rendben. Első dolgunk megbizonyosodni arról, hogy a "lohet" változónak már van értéke a tűzgomb lenyomása első ellenőrzésekor is. Ezt egy változó inicializálásának, vagyis működése előkészítésének nevezik a kezdőérték beállításával. Ha egy változóhoz az ellenőrzése előtt nem lett hozzárendelve egy érték, akkor a játék egy hibaüzenettel le fog állni.

Egy objektum "Create" eseménye egy jó hely a változók beállításához. Nyisd meg az "objJatekos" objektumablakát, és add hozzá a "Create" eseményt, amihez rendelj egy "Execute a piece of code" akciót. A felbukkanó kódablakba írd ezt be:

lohet = true;

Ez a "lohet" változót igazra állítja. A "true" szó egyike a GM beépített állandóinak. A "true" szó használata ugyanaz, mint az "1" számé. A "false" szó a GM másik beépített változója, és a "0" számot képviseli. Ez azt jelenti, hogy a következőt is írhatjuk:

lohet = 1;

és ez ugyanazt valósítja meg. De a "true" és "false" szavak használata jobban érthető.

Most egy új fogalmat mutatok be a kódolással kapcsolatban: a megjegyzések fogalmát. A megjegyzések nagyon fontosak a kódban. A kódot olvasó embernek szólnak, nem pedig a számítógépnek, amely egyszerűen figyelmen kívül hagyja azokat. A megjegyzések használatával leírhatod a kódban, hogy mi a szándékod vele. Ez később könnyebbséget jelent, amikor valamit meg akarsz változtatni. Vagy hibát keresel benne. Egy megjegyzés megadásához írj elé két per-jelet "//", például:

// A fegyver tűzkésszé tétele.
lohet = true;

A számítógép teljesen figyelmen kívül fogja hagyni a megjegyzést, és végrehajtja a többi kódot. De ha valaki belenéz ebbe a kódba, akkor a megjegyzés jóvoltából többet fog tudni arról, hogy mi történik benne, mintha csak a számítógépkódot látná.

A megjegyzés a programutasítás után is megadható, mint például:

lohet = true; // A fegyver tűzkésszé tétele.

de én jobban szeretem, ha a megjegyzés a saját sorában van.

Emlékszel, hogyan lőjük a lövedéket? Az "objJatekos" <Space> gomb eseményében létrehozzuk a lövedék objektum egy példányát. Nyisd meg az "objJatekos" <Space> gomb eseményét, és töröld az akciósorból a "Create an instance of an object" akciót. Adj az eseményhez egy "Execute a piece of code" akciót. Ez, szokás szerint, felhoz egy kódablakot. Az első dolog, amit itt elvégzünk, hogy leellenőrizzük, vajon készen áll-e a fegyver. Vagyis ellenőrizzük, hogy a "lohet" változó értéke "true". Hogyan ellenőrizhetjük le egy változó értékét? Erre való az "if" (ha) utasítás, amelynek ez a meghatározása (kivonat a GM súgójából):

Alakja: if (<feltétel>) <utasítás>
vagy (elágazás kétfelé): if (<feltétel>) <utasítás> else <utasítás>

Jobban áttekinthető, ha a következőképpen használjuk:

if (<feltétel>)
{
<utasítás>
}
else
{
<utasítás>
}


Elágazás alkalmazásával a kiértékelendő feltétel igaz vagy hamis voltától függően hajthatunk végre utasítás(oka)t.

Hmmm. Lehet, hogy kissé nehéz megértened, ha nem vagy programozó. A dolgok tisztázásához egyszerűen lemásolom az "if" utasítást, ahogy a kódablakunkban ki kell néznie. Írd le ezt:

if (lohet = true) then
{
}

Ez nem volt olyan nehéz, ugye? Ez azt jelenti, hogy ha a "lohet" változó értéke igaz, akkor a kód, amit (később) kapcsos zárójelek közé rakunk, végre fog hajtódni. Különben figyelmen kívül lesz hagyva. A zárójelek után írtam a "then" (akkor) szót, de ez nem kötelező. Ez csak olvashatóbbá teszi a kódot. Az úgynevezett kapcsos zárójelek sokszor lesznek használva a kódban. Ezek a C/C++ nyelvből származnak. Ha csak egy utasítást akarunk végrehajtani az "if" utasításon belül, akkor nem szükséges kapcsos zárójelek közé rakni, de a jó programozási stílus szempontjából érdemes mindig kirakni azokat. A kapcsos zárójelek egy kódblokkot alkotnak, ami a programértelmező számára olyan, mint egy egyszerű utasítás. Hogy megértsd, hogy ez miért hasznos, nézd át a következő példát.

Ha a "speed" változó nagyobb, mint 5, akkor az objektumot arra a helyre akarjuk mozgatni, ahol x=40 és y=80. Elég egyszerű. Tehát írjuk:

if (speed > 5) then
x = 40;
y = 80;

De ez NEM úgy fog működni, ahogy reméljük. Az "if" utasítás csak az alatta levő első utasításra van hatással. Ez azt jelenti, hogy ha a "speed" NEM nagyobb, mint 5, akkor az "x=40" ki lesz hagyva, de az "y=80" mindenképpen végrehajtódik. Hogy ezt megoldjuk, szükségünk van még egy "if" utasításra az "y=80" számára, vagy, ami sokkal könnyebb, használhatjuk a kapcsos zárójeleket. Így:

if (speed > 5) then
{
x = 40;
y = 80;
}

Ez azt jelenti, hogy az "x" és "y" dolgot is magában foglalja az "if" utasítás. Most, ha a "speed" NEM nagyobb, mint 5, akkor sem az "x=40", sem az "y=80" nem fog végrehajtódni. Ha pedig a "speed" NAGYOBB, mint 5, akkor mindkét utasítás végrehajtásra kerül. Most már érted a kapcsos zárójelek használatának lényegét? Ezek még több utasításban is használhatók, ahogy a későbbiekben majd látni fogjuk.

Most vissza a játék kódjához. A kódablakodban most egy tökéletes "if" utasításnak kell lennie kapcsos zárójelekkel, de azok között még nincs semmi. Itt fogjuk létrehozni az "objLovedek" objektum egy példányát. Korábban ezt fogd-és-vidd akció használatával végeztük el, most meg fogjuk csinálni kódban. Ez elég egyszerű. Az "instance_create" függvényt fogjuk használni, aminek leírását szintén megtalálod a GM súgójában.

A kapcsos zárójelek közé gépeld ezt be:

instance_create(x, y, objLovedek);

Most létrehoztál egy lövedéket. De mit szólsz az "x" és "y"-hoz? Nem kellene valami számértéket begépelni oda? Valójában éppen ezt tettük. Az "x" és "y" változók tartalmazzák a helyét a pillanatnyi "objJatekos" példánynak (emlékezz, az "objJatekos" objektumban vagyunk, miközben kódolunk), és ezek a koordináták lesznek az "objLovedek" példány kezdőkoordinátái. Ez ugyanaz, mint egy példány létrehozása a fogd-és-vidd ikonokkal, és a "Relative" bejelölése.

Be kell még állítanunk a "lohet" változót hamisra, hogy a fegyver ne lőhessen rögtön újra. Gépeld be ezt, csak az "instance_create" sora után:

lohet = false;

Ez könnyű volt!

Most be fogunk állítani egy risztást, hogy biztosak lehessünk abban, hogy a "lohet" változó újra igazzá válik, némi idő elteltével. Egy riasztás beállítása így történik:

alarm[0] = 10;

8 riasztó időzítő (Megjegyzés: GM 6.1-től 12) létezik (alarm[0]-alarm[7]). Mi itt az alarm[0]-t használtuk. A szögletes zárójelek ("[]") egy tömb meghatározására használhatók. Egy tömb olyasmi, mint egy listája a változóknak, ahol minden azonos nevű, de számozással vannak megkülönböztetve egymástól. A tömbökkel bővebben egy másik oktatói kézikönyvben fogunk foglalkozni. Tehát az alarm[0] riasztó 10 képkockányi idő elteltével fog eseményt kiváltani.

Most készen vagyunk a kóddal a <Space> gomb eseményben. Ha akarod, lerövidítheted az "if" utasítás sorát a következőképpen:

if (lohet) then

Mivel ez ugyanolyan, mint annak vizsgálata, hogy a "lohet" tartalmaz bármilyen pozitív számot.

Tehát a kódnak most így kell kinéznie:

if (lohet) then
{
instance_create(x, y, objLovedek);
lohet = false;
alarm[0] = 10;
}

A kapcsos zárójelek közötti három sort célszerű "behúzottá" tenni, vagyis egy "TAB"-ot ütni eléjük. Ez csak könnyebben olvashatóvá teszi a kódot, az értelmező nem veszi figyelembe a behúzásokat. Ezzel jobban látható, hogy a kód mely részei tartoznak egy kapcsos zárójeles blokkba.

Rendben!

Egy dolog maradt hátra; az alarm[0] esemény. Zárd be ezt a kódablakot, és add hozzá az objektumhoz az alarm[0] eseményt. Ehhez add hozzá az "Execute a piece of code" akciót, és írd be a következő kódot az ablakba:

lohet = true;

Ez biztosítja, hogy mikor a riasztó "csöng", 10 képkockával a lövedék kilövése után, a "lohet" változó ismét igazra álljon.

Most mentsd el és próbáld ki a játékodat.

Ha megfelelően követted az utasításokat, akkor az "objJatekos" most hosszabb időközökkel lő (kb. 3 lövedék másodpercenként). Sokkal jobb.

 

4. Ellenség a láthatáron!

Nagyon kevés olyan játék van, amiben nincsenek számítógép-vezérelte ellenségek. Elvégre harcolnunk kell valaki ellen. Ebben a fejezetben hozzáadunk a játékhoz néhány ellenséges repülőt, és tanulunk a "véletlen" függvény használatáról és az ütközésellenőrzésről.

4.1 Ellenséges repülőgép

Úgy döntöttem, hogy az ellenség egy másik repülő lesz, amely a képernyő tetején bukkan fel, és keresztülrepül a képernyő aljáig.

A következőkre van szükségünk:

- A repülő véletlenszerű időközönként jelenjen meg.
- A repülő véletlenszerű helyeken tűnjön fel a képernyő teteje mentén.
- Amikor a repülő eltűnik a képernyő alján, akkor semmisüljön meg.
- Később hozzáadunk egy ütközési eseményt az ellenséges repülő és az "objJatekos" között.

Először is létre kell hoznunk egy ellenséges repülő sprite-ot és objektumot.

A "mig41.gif" képet szándékozom használni erre a "Sprites/Transport" mappából, ami így néz ki:

Hozz létre egy új sprite-ot, nevezd el "sprEllenseg1"-nek, és töltsd be számára ezt a képet. Elfelejtetted, hogyan kell? Nézd meg a 2.1-es fejezetben. A repülő azonban fölfelé néz, nekünk pedig arra van szükségünk, hogy lefelé irányuljon, kivéve persze ha azt akarnánk, hogy hátulról támadjon, de nem hiszem, hogy az jól nézne ki.

A repülő elforgatásához nyisd meg a sprite-ot, és kattints az "Edit sprite" (kép szerkesztése) gombra. Kattints az "image 0"-ra a kiválasztásához, és a menüből válaszd a "Transform -> Rotate 180" pontot. Ez lefelé fordítja a repülőt. Kattints az OK-ra a képszerkesztő ablak bezárásához, majd ismét az OK-ra a spriteablak bezárásához.

Most létre kell hoznunk egy ellenség objektumot. Hozz létre egy új objektumot, és nevezd el "objEllenseg1"-nek, amihez állítsd be képnek az "sprEllenseg1"-et.

Azt akarjuk, hogy az ellenségek bizonyos időközönként jöjjenek, és repüljenek keresztül a képernyőn. Hogy ellenőrizzük az ellenségek létrehozását, szükségünk van egy vezérlőobjektumra. Használhatnánk erre az "objJatekos"-t, de én jobban szeretnék inkább egy külön ellenségvezérlő objektumot használni erre.

Ideje megmagyarázni a "vezérlő objektum" kifejezést. Ez azt jelenti, hogy van egy objektumpéldány, ami mindig jelen van a játékban. Ezt nem lehet lelőni, vagy más módon megsemmisíteni. Ez a vezérlőobjektum arra való, hogy más objektumok példányait hozza létre, például ellenségeket. A vezérlőobjektumok arra is jók, hogy bizonyos adatokat tároljanak a helyi változóikban, például a játékos objektum példányát, mivel az időnként megsemmisül. Egy vezérlőobjektum rendszerint láthatatlan, tehát a felhasználó nem tudja, hogy ott van. "Titkosan" végzi a saját munkáját.

Tehát ennek a vezérlőobjektumnak a példánya nem lesz látható a játék során, de szükségünk van arra, hogy képviseljük valami sprite-tal azért, hogy el tudjuk helyezni és lássuk a szobában. Tehát csináljunk így.

Hozzunk létre egy új sprite-ot, nevezzük el "sprEllensegVezerlo"-nek, és töltsük be hozzá a "trigger.gif" képet a "Sprites/Maze" mappából. Ez jól néz ki egy vezérlő számára, ugye? Hozz létre egy új objektumot, amit nevezz el "objEllensegVezerlo"-nek, és rendeld hozzá az "sprEllensegVezerlo" sprite-ot. Győződj meg arról, hogy az "objEllensegVezerlo" objektumablakában a "Visible" (Látható) jelölődoboz NINCS bejelölve. Ez láthatatlanná teszi a játékos számára, de mi látni fogjuk a játék tervezése közben. (Megjegyzés: az újabb GM-ek már egy kék körben levő kérdőjellel mutatják a sprite nélküli objektumokat a szobában, tehát nem szükséges külön sprite-ot hozzárendelni.)

Nyisd meg a Room1-et, és add hozzá valahol az "objEllensegVezerlo"-t. Nem lényeges, hogy hol helyezed el, csak az a fontos, hogy lásd valahol. A játékos példányt (az "objJatekos"-t) is helyezd el, valahol a képernyő alján.

Most létre kell hoznunk egy szkriptet, ami előkészít egy új ellenséget. Tehát add meg ezt az új szkriptet, és neved el "Ellenseg1Indit"-nak. Az indítónál csak azt akarjuk, hogy az ellenséges repülőt mozgassa keresztül a képernyőn. Tehát írd ezt be ebbe az új szkriptbe:

vspeed = 10;

Amelyik objektum ezt a szkriptet hívja, azt lefelé mozgatja keresztül a képernyőn 10-es sebességgel.

Arra is szükségünk van, hogy az ellenségek tűnjenek el, ha már elérték a képernyő alját. Hozz létre egy új szkriptet, és nevezd el "EllensegMegsemmisitese"-nek. Ebbe gépeld be ezt a kódot:

instance_destroy();

Ez a kód az "instance_destroy" függvényt hívja, amely megsemmisíti az őt hívó példányt.

Az ellenség számára eddig ennyi kódra van szükség. Később egy kicsit fejleszteni fogunk rajta.

Most hozzá fogunk adni két szkriptet az ellenségvezérlő objektumhoz. Hozz létre két új szkriptet, és nevezd el "EllensegVezerloBeallit"-nak és "LetrehozEllenseg1et"-nek. Az "EllensegVezerloBeallit" szkriptbe írd a következőt:

alarm[0] = 30;

Ez beállítja az "objEllensegVezerlo" alarm[0] függvényét, hogy jelezzen 30 képkocka lefutása után. Ami "valós időben" kb. 1 másodperc, feltéve, hogy a szobasebesség 30 FPS. Amikor a riasztó jelez, hívja a másik szkriptet, a "LetrehozEllenseg1et" nevűt, amibe írd ezt:

instance_create(50, 0, objEllenseg1);
alarm[0] = 30;

Az első sor létre fogja hozni az "objEllenseg1" egy új példányát az 50-es x-, és 0-s y-koordinátára. Erről a függvényről a GM súgójában tudhatsz meg további dolgokat.

A második sor újra 30 képkockára (1 másodperc) állítja az alarm[0]-t, hogy egy másik ellenség bukkanjon fel 1 másodperc elteltével.

Most be kell állítanunk az objektumoknál, hogy hívják ezeket a szkripteket. Nyisd meg az "objEllenseg1" objektumot, és add hozzá a "Create" eseményt. Ez az objektum egy példányának létrejöttekor történik meg. Add ehhez az "Execute a script" akciót. A felbukkanó ablakban válaszd az "Ellenseg1Indit" szkriptet, majd kattints az OK-ra az ablak bezárásához.

Add hozzá az "Outside" eseményt. Ehhez is válaszd az "Execute a script" akciót, és válaszd az "EllensegMegsemmisitese" szkriptet, majd az OK-ra kattintással zárd be az ablakot.

Nyisd meg az "objEllensegVezerlo" objektumablakát, és add meg a "Create" eseményt. Ehhez add az "Execute a script" akciót, amiben válaszd ki az "EllensegVezerloBeallit" szkriptet, majd zárd be az akcióablakot. Végül az "Alarm 0" eseményhez is add hozzá az "Execute a script" akciót, amiben válaszd a "LetrehozEllenseg1et" szkriptet.

Mentsd el a játékot és futtasd.

Most látnod kell, hogy ellenséges repülők bukkannak fel és átrepülnek a képernyőn, majd eltűnnek az alján. Az "objEllensegVezerlo" objektum nem látszik a játék futtatásakor. Ha mégis, akkor elfelejtetted az objektumablakában a láthatóságát letiltani a "Visible" jelölőnégyzetben.

4.2 Véletlenszerűsítés

Az ellenségek felbukkanása meglehetősen unalmas. Azt szeretnénk, hogy véletlen helyeken jelenjenek meg a képernyő teteje mentén, véletlen távolságban és véletlen sebességgel.

Nézd meg a "random" függvény meghatározását a GM súgójában. Ez a függvény használható arra, hogy a dolgok megtörténtét véletlenszerűvé tegyük, például utánozzunk egy dobókockát. Visszaad egy véletlenszerű értéket, ami mindig kevesebb a paraméterében megadott értéknél. Például:

random(3);

A fönti sor visszaad egy 0 és 2.9999999999999999999. Ha jól tudom, a GM 20 tizedesjeggyel dolgozik.

Hogy megszabaduljunk a tizedesjegyektől, használnunk kell egy másik GM függvényt, a "floor"-t. Ez a függvény egyszerűen "levágja" az összes tizedesjegyet, tehát például:

floor(1.234239) = 1
floor(2.999999999) = 2

Ha egy hatoldalú dobókockát akarunk utánozni, akkor alkalmazhatjuk:

dobokocka = random(6);

A fenti sor 0 és 5.9999... közötti számot adhat, ezért utána használjuk a floor függvényt:

dobokocka = floor(dobokocka);

A floor() függvény argumentumaként magát a "dobokocka" változót használjuk. Ez teljesen rendben van. Az eredmény most egy 0 és 5 közötti egész szám lesz. Majdnem megvagyunk. Most hozzáadhatunk 1-et az eredményhez.

dobokocka = dobokocka + 1;

És végül most már az eredmény 1 és 6 közötti lesz, mindegyik ugyanolyan valószínűséggel.

Hogy megtakarítsunk némi helyet a kódablakban, elvégezhetjük az összes iménti számítást egy sorban is:

dobokocka = floor(random(6)) + 1;

A fenti sor talán egy kicsit nehezebben olvasható, de egy utasításban végrehajtja az összes előbbi számítást.

Most elhelyezzük a random függvényt három helyen a kódunkban. Nyisd meg a "LetrehozEllenseg1et" szkriptet.

Először is azt akarjuk, hogy az ellenségek véletlen helyeken bukkanjanak fel a képernyő teteje mentén. Ez azt jelenti, hogy a 0-tól 639-ig terjedő értékhatárból szeretnénk véletlenszámokat használni, amely a játékképernyőnk szélessége (640 képpont széles). A

random(640);

utasítás 0-tól 639.999999-ig ad értékeket. Egészítsük ki a "floor" függvénnyel,

floor(random(640));

amely 0-tól 639-ig fog értékeket adni, tizedesjegyek nélkül. Ez jól néz ki. De mi van akkor, ha később meg akarjuk változtatni a képernyő szélességét? Semmi gond. A GM-ben van egy változó, amely a képernyő szélességének meghatározására használható. Ez a "screen_width" (képernyő szélesség), ezt fogjuk használni a "640" helyett. (Nézd át a GM súgóját, hogy mit ír ezzel a függvénnyel kapcsolatban.) Tehát most megváltoztatjuk a szkript első sorát:

instance_create(floor(random(screen_width)), 0, objEllenseg1);

Legyél NAGYON figyelmes a zárójelekkel, mert hibát okoz, ha nincs mindegyik a helyén.

Ha akarod, elmentheted és kipróbálhatod a játékot.

Azt is akarjuk, hogy az ellenségek különböző időközönként jelenjenek meg. Mondjuk 0.3 és 2 másodperc között. 0.3 mp 10 képkockát jelent, és 2 mp 60 képkockát. Tehát egy 10 és 60 közötti véletlenszámot akarunk. Változtassuk meg a szkript második sorát a következőre:

alarm[0] = floor(random(51)) + 10;

Ez riasztást fog eredményezni 10-60 képkockányi időközönként.

Mentsd el és futtasd a játékot.

Végül azt is szeretnénk, hogy az ellenségek véletlen sebességgel repüljenek. Nyisd meg az "Ellenseg1Indit" szkriptet, és változtasd meg a kódot erre:

vspeed = random(8) + 2;

Ez egy sebességértéket fog adni 2 és 9.999999 között. Itt nem muszáj használnunk a "floor"-t, mivel a sebességértékek tizedesjegyekkel is megfelelőek.

Próbáld ki a játékot. Most az ellenségeket különböző helyeken, különböző időközönként és különöbző sebességgel fogod látni. Pontosan ezt akartuk!

4.3 Aú! Ez fájt!

Eddig az ellenségek nem voltak veszélyesek, nem fontos kikerülni őket. Ezt most orvosolni fogjuk.

Ha már készítettél valamilyen játékot ezt megelőzően, akkor valószínűleg használtad a GM Ütközésellenőrzés tulajdonságát. Ez valóban nagyszerű dolog. Most használni fogjuk az ütközés észlelésére a játékos és az ellenségek között.

Hozz létre egy új szkriptet, amit nevezz el "JatekosEllenseg1Utkozes"-nek. Nyisd meg az "objJatekos" objektumablakát, és add hozzá az ütközés eseményt az "objEllenseg1" objektummal. Ez az az esemény, amelynek a gombján két egymás felé irányuló piros nyíl van. A listából válaszd az "objEllenseg1"-et. Jó.

Ehhez add hozzá az "Execute a script" akciót, és válaszd ki benne a "JatekosEllenseg1Utkozes" szkriptet, majd zárd be az akcióablakot és az objektumablakot is.

Most összpontosíthatunk a szkriptre. Azt akarjuk, hogy a játékos ütközhessen néhány ellenséggel, mielőtt meghal, különben a játék túl nehéz lesz. Tehát használni fogunk valamit, amit energiának hívunk, hogy lemérhessük, mennyi ütközést bír ki az "objJatekos", mielőtt felrobban. Induljon az "objJatekos" mondjuk 100 energiaegységgel. Aztán minden ellenséges repülővel való ütközés során veszíteni fog 30 energiaegységet. Rendben? És amikor az energiaegysége 0, akkor megsemmisül. Az ellenséges repülő pedig azonnal megsemmisül az "objJatekos"-sal való ütközéskor.

Ezért az ütközés eseménynél levő szkriptben csökkentenünk kell az "objJatekos" energiáját. Ezt a következőképpen végezhetjük el:

energia -= 30;

Ellenőriznünk kell azt is, hogy az energia 0-e. Ha annyi, akkor az "objJatekos" példányt meg kell semmisíteni. Emlékszel az "if" utasításra?

if (energia = 0) then
{
instance_destroy();
}

Itt szándékosan csináltam egy rossz dolgot az "if" utasításnál. Látod, hogy mi a hiba? Az ellenőrzés, "=". Ez az utasítás csak azt fogja ellenőrizni, hogy az energia pontosan 0-e. De mi van akkor, ha az energia először 10, aztán nekimész egy ellenségnek, veszítesz 30 energiaegységet, és az energia -20 lesz? Akkor az "if" utasításnál nem fog jelezni, és az nem semmisíti meg az "objJatekos" példányát. Ezért jobb, ha a "<=" ellenőrző műveleti jelet használjuk. Ez azt jelenti, hogy "Kisebb vagy egyenlő mint". Tehát írd helyette ezt:

if (energia <= 0) then
{
instance_destroy();
}

Ez meg fogja semmisíteni az "objJatekos"-t, ha az energia 0, vagy annál kevesebb.

Az ellenséget is meg akarjuk semmisíteni, ezért add ezt a sort

with (other) instance_destroy();

a szkript KEZDETÉHEZ.

A "with" (-val/-vel) utasítás nagyon hasznos. Lehetővé teszi számunkra, hogy csináljunk valamit a másik példánnyal. Ezesetben meg akarjuk semmisíteni azt a példányt, amellyel ütköztünk. Ez az "other" (másik) példány az ütközés eseményben. Tehát a MÁSIK példánnyal akarjuk azt megcsinálni, hogy MEGSEMMISÍTSE magát. A fönti sor ugyanaz, mintha az "instance_destroy()" a másik objektum kódjába íródott volna.

Tehát most a teljes "JatekosEllenseg1Utkozes" szkriptnek így kell kinéznie:

with (other) instance_destroy();
energia -= 30;
if (energia <= 0) then
{
instance_destroy();
}

Most már csak a kezdeti energiaszintet kell beállítanunk. Úgy döntöttünk, hogy 100 energiaegységgel indulunk. Ezt az "objJatekos" "Create" eseményében kell megadnunk. Csinálhatunk erre is egy szkriptet, elvégre erről szól ez az oktató kézikönyv. Tehát hozz létre egy új szkriptet, és nevezd el "JatekosBeallit"-nak. Gépeld be a következő kódot a szkriptbe:

energia = 100;

Ha nem adjuk meg az "energia" változó kezdőértékét, akkor egy hibaüzenet fog felbukkanni, amikor az "objJatekos" ütközik az "objEllenseg1" objektum egy példányával. Megpróbálhatod futtatni a játékot (előtte ments!), mielőtt beállítod az "objJatekos" "Create" eseményét, hogy lásd, mi történik akkor, ha elfelejted egy változó kezdőértékét megadni. Jó lehet tudni, mert könnyen elfeledkezhetsz róla. Ha ütközöl egy repülővel, a következő üzenet bukkan fel:

"Unknown variable or function: energia" (Ismeretlen változó vagy függvény: energia)

Tehát kattints az "Abort" (megszakít) gombra, és állítsuk be az "objJatekos" objektum "Create" eseményét. Nyisd meg az "objJatekos" objektumot, és válaszd a "Create" eseményt. Itt találod az "Execute a piece of code" akciót, amit korábban hozzáadtunk. Kattints rajta kettőt. Csak egy kódsort tartalmaz; a "lohet" változó beállítását. Ez egy jó dolog, amit átviszünk az új "JatekosBeallit" szkriptünkbe. Tehát válaszd ki a kódablakban ezt a kódot, és másold ki, majd zárd be a kódablakot, és töröld az "Execute a piece of code" akciót az akciósorból. Adj egy "Execute a script" akciót, és válaszd a "JatekosBeallit" szkriptet. Most nyisd meg a "JatekosBeallit" szkriptet, és illeszd be az előbb törölt akcióból kimásolt kódot. Ha mindent jól csináltál, akkor a "JatekosBeallit" szkriptnek most a következőket kell tartalmaznia:

lohet = true;
energia = 100;

Mentsd el a játékot, és futtasd újra.

Észre kell venned, hogy az ellenséges repülő eltűnik, ha nekimész. Amikor nekiütközöl a negyedik repülőnek, az "objJatekos" példány is eltűnik. Helyes! Most a lényeg az ellenségek kikerülése.

4.4 Kilövés

Eddig a lövedékeidnek semmi közük nem volt a közeledő ellenséges repülőgépek számának csökkentéséhez. Egy fegyvernek egy játékban az a lényege, hogy képes legyen némi kárt okozni. Tehát arra van szükségünk, hogy az "objJatekos" példányból kilőtt lövedékek rongálják meg az ellenséges repülőt.

Egy szkriptet fogunk készíteni, ami egy lövedék és egy ellenséges repülő közötti ütközéssel törődik. Hozz létre egy új szkriptet, amit nevezz el "LovedekEllensegUtkozes"-nek.

Ez majdnem ugyanúgy működik, mint az "objJatekos" és "objEllenseg1" objektumoknál levő ütközési szkript. Csökkenteni kell az ellenség energiaszintjét, és ellenőrizni, hogy az energiaszint már 0 vagy kevesebb-e. Mondjuk az ellenséges repülő is 100 energiaegységgel induljon. Aztán amikor egy lövedék eltalálja, az energiaszintje csökkenjen 50 egységgel, ami azt jelenti, hogy két lövés kell egy ellenség megsemmisítéséhez.

Itt a szkript, amit használni fogunk:

with (other)
{
// Az ellenség energiájának csökkentése
energia -= 50;
// 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();
}
}
// Ennek a lövedéknek a megsemmisítése
instance_destroy();

A különbség ezen szkript és az előző között az, hogy ez legnagyobbrészt az ellenséges példányon végzi a munkáját, és nem azon az objektumpéldányon, ami a kódot tartalmazza (az "objLovedek" objektum).

Ha a "with"-en belül több mint egy utasítás van, akkor így használhatod a kapcsos zárójeleket. Amint látod, egymásba lehet ágyazni a kapcsos zárójeleket. Ez azt jelenti ebben a példában, hogy van egy "if" utasítás a "with" utasításon belül. Hasonló esetben rendszerint TAB karaktereket használsz a legmélyebben beágyazott sorok elején, hogy látszódjon, hogy melyik utasításhoz tartoznak. Még egyszer: a TAB jelek, vagy másképpen behúzások csak az ember számára fontosak, a számítógép nem törődik velük.

Tehát ez a szkript csökenti az ellenség energiáját 50 egységgel, és ellenőrzi, hogy az energiaszint 0 vagy kevesebb-e már. Ha igen, akkor az ellenség megsemmisül.

Végül a szkript megsemmisíti a lövedéket is, különben az folytatná a mozgását a képernyőn keresztül. Néhány megjegyzést is elhelyeztem a szkriptben, hogy megmutassam, hogy én rendszerint hogyan használom a megjegyzéseket. Ezek növelik a kód olvashatóságát és könnyebben érthetővé teszik.

Nyisd meg az "objLovedek" objektumablakát, és válaszd ehhez az ütközés eseményt az "objEllenseg1" objektummal. Adj hozzá egy "Execute a script" akciót, és válaszd az imént készített szkriptet ("LovedekEllensegUtkozes").

Most már csak meg kell adnunk az ellenségeknek a kezdeti energiaszintet. Már van egy inicializáló szkriptünk az ellenségek számára, tehát hasznájuk azt. Nyisd meg az "Ellenseg1Indit" szkriptet, és add hozzá ezt a sort:

energia = 100;

Ami 100-ra állítja az ellenségek kezdő energiaszintjét.

Mentsd el a játékot, és próbáld ki. Remek! Most le lehet lőni az ellenséges repülőket, de csak a lassúakat. A gyorsan mozgókat nehéz eltalálni.

4.5 Tűzijáték

Robbanások! Ezek azok, amik hiányoznak a játékunkból. Szerencsére a GM tartalmaz néhány szép robbanás animációt. Egy robbanás objektumból fogjuk létrehozni.

Először létre kell hoznunk a sprite-ot. Ezúttal ez egy animált sprite lesz, ami azt jelenti, hogy több, mint egy képből áll. Hozz létre egy új sprite-ot, és nevezd el "sprRobbanas"-nak. Töltsd be hozzá a "Sprites / Various" mappából az "explode2.gif" képfájlt. Amikor betöltötted, észre kell venned, hogy a sprite ablak azt mutatja, hogy az alképek száma 17. Kattints az "Edit Sprite" gombra.

Most látni fogod a sprite mind a 17 alképét, amik "image0"-tól "image16"-ig vannak elnevezve. Hogy megnézd az animációját, kattints a "Show Preview" (Előzetes mutatása) feliratú jelölődobozba.

Ha megnézed a képeket, akkor láthatod, hogy a 0. kép azt mutatja, amint a robbanás egy kicsit már szétpukkant. Úgy tűnik, hogy az animáció valahol a robbanás közepén kezdődik. Ez furcsa, és fogalmam sincs, hogy ez miért ilyen. Meg fogjuk változtatni a sorrendet, hogy a robbanás megfelelően nézzen ki, amikor a játékban használjuk.

Válaszd ki az image0-t, és kattints a jobbra mutató piros nyílra az eszköztáron. Ez a képet arrébb helyezi eggyel jobbra.

Kattintgass addig a piros nyílra, amíg a kép az animáció végére nem kerül. A kiválasztott kép neve most image16. Most válaszd ki az új image0-t, és mozgasd végig a képsorozaton. A [Ctrl]+[R] billentyűkombinációt is használhatod erre a kattintások helyett. Mozgasd a képeket addig, mígnem az image0 ábrázolja a legkisebb fénylő pontot, az image16 pedig egy zöld hátterű üres kép lesz. Így kell kinéznie, amikor elkészültél vele:

Ha megint bejelölöd a "Show Preview" dobozt, akkor azt látod, hogy nincs különbség. De ez csak azért van, mert az animáció folyamatosan ismétlődik egymás után. Amikor a játékban használjuk, csak egyszer fogjuk látni, és ott lesz fontosabb a kezdete.

Zárd be a képszerkesztő és a sprite ablakot. Hozz létre egy új objektumot, nevezd el "objRobbanas"-nak, és rendeld hozzá az "sprRobbanas" sprite-ot. Az "objRobbanas" objektum "Animation End" (Animáció vége) eseményéhez adj egy "Execute a piece of code" akciót. Az "Animation End" esemény az "Other" (Egyebek) esemény gomb menüjében található:

A felbukkanó kódablakban semmisítsük meg a példányt:

instance_destroy();

Ez meg fogja semmisíteni a robbanás példányt, amikor lejátszotta az animációs képkockáit.

Most nyisd meg az "objEllenseg1"-et, aminek "Destroy" (Megsemmisítés) eseményéhez adj egy "Execute a piece of code" akciót. Ezt azért használjuk a szkript akció helyett, mert a kód, amit itt végre fogunk hajtani, olyan kicsi, hogy teljesen szükségtelen egy különálló szkriptben alkalmazni. Az ellenség megsemmisülésekor egy robbanást akarunk mutatni, tehát egy robbanás példányt kell létrehozni, amikor az ellenség megsemmisül. Írjuk a felbukkanó kódablakba:

instance_create(x, y, objRobbanas);

Ez létre fog hozni egy robbanást az "objEllenseg1" koordinátáin. Emlékezz, hogy egy példánynál az x és y változók tartalmazzák a koordinátáit.

Mentsd a játékot és indítsd el. Most egy gyönyörű robbanás jelenik meg egy ellenség lelövésekor.

Adj robbanást az "objJatekos" "Destroy" eseményéhez is. Ezt most már magadtól is meg kell tudnod csinálni. Én nem fogom elmondani, hogyan kell ;). Használd ugyanazt az "objRobbanas" objektumot, mint amit az ellenségeknél.

GMprog_oldal2.htm