U prvom blog postu smo opisali igru – kroz jedan User Story, zatim definisali koji su joj osnovni elementi (Igrač, Asteroid, Municija – Fighter, Asteroid, Ammo) i pripremili smo grafiku. Danas programiramo. Neka vas ne plaši programiranje, odnosno kod koji ćemo napisati. Većina stvari će vam biti objašnjena, a ako vam nešto ne bude jasno, slobodno pitajte u komentarima ispod.
Na kraju posta možete preuzeti celu igru.
Napomena: Programiranje se velikom broju ljudi čini veoma kompleksnim i teškim, što je uglavnom krivica samih programera koji pokušavaju da zaplaše ostale i učine sebe vrednijim i inteligentnijim. Taj stav naročito ispoljavaju prema dizajnerima.
U ovom tekstu, ću se potruditi da programiranje učinim što logičnijim i interesantnijim. Svaka linija koda će imati svoju ulogu u vizuelnom delu igre. Uživajte!
Nakon što smo pripremili naše grafičke elemente i dozvolili Flash-u da automatski generiše odgovarajuće klase za njih (o ovome je bilo priče u prethodnom blog postu), sada se vraćamo na fajl GameController.as . Otvorite ga i umesto linije koda gde piše „//constructor code“ napišite „LoadGraphics();“. Tako da konstruktor klase GameController izgleda ovako:
1 2 3 4 5 | public function GameController(){ LoadGraphics(); } |
Zatim ispod konstruktora, napravite tu funkciju LoadGraphics, zatim ukucajte sledeće:
1 2 3 4 5 6 7 8 9 | public function LoadGraphics(){ player.y = stage.stageHeight - (player.height + PLAYER_BOTTOM_OFFSET); player.x = stage.stageWidth/2 - player.width/2; addChild(player); } |
Verovatno već pretpostavljate šta radi sledeći kod, ali svakako ću objasniti.
U prvoj i drugoj liniji ćemo postaviti X i Y koordinate igračeve letelice, dok treća linija dodaje igrača na sam Stage (vidljivu površinu). Ako ne razumete šta ovaj kod radi, nemojte se zamarati, sutra u dodatku ću vam detaljno objasniti svaku funkciju (jer ipak hoćemo da igru napravimo u 2 blog posta kako naslov kaže).
Kada budete pritisnuli CTRL+ENTER, za test prikaz, prijaviće vam grešku. Moramo inicijalizovati igrača (player) i konstantu PLAYER_BOTTOM_OFFSET koja pravi razmak između igrača i donje ivice ekrana, jer nećemo da ga baš pribijemo uz donju ivicu.
Iznad konstruktora upišite sledeći kod:
1 2 3 | var player:Fighter = new Fighter(); var PLAYER_BOTTOM_OFFSET:Number = 10; |
Sada, otvorite fajl “AsteroidDefender.fla“ i testirajte sa CTRL+ENTER i videćete da vam se na ekranu pojavila svemirska letelica. Već sada možete videti rezultate vašeg rada, ali ne brinite, ovde nije kraj. Sada će tek biti interesantno.
U vaš konstruktor, ispod koda kojim pozivate vašu funkciju LoadGraphics, upišite sledeću liniju koda.
stage.addEventListener(MouseEvent.MOUSE_MOVE, OnMouseMotion);
Potom ispod funkcije LoadGraphics napravite novu funkciju OnMouseMotion sa sledećim kodom:
1 2 3 4 5 | public function OnMouseMotion(e:MouseEvent):void{ player.x = stage.mouseX-player.width/2; } |
Ovom funkcijom smo podesili da se igrač mišem može pomerati levo, desno. Ako se pitate zašto nismo podesili da može da ide i gore dole, razlog je zato što želimo da ograničimo igrača jer mu je cilj da zaštiti planetu Zemlju, kao i da igru učinimo malo težom. (kasnije možemo dodati i opciju pomeranja levo, desno preko tastature)
Hajde sad da podesimo da igrač može da ispaljuje projektile, odnosno municiju.
Prvo ćemo iznad konstruktora, a ispod ostalih promenljivih dodati još jednu promenljivu bunchOfAmmo, koja predstavlja niz, odnosno skup municije.
var bunchOfAmmo:Array = new Array();
Onda u konstruktor, ispod ostalog koda ćemo ukucati sledeći kod:
stage.addEventListener(MouseEvent.CLICK, OnMouseClick);
Sada ispod funkcije OnMouseMove dodajemo još jednu funkciju OnMouseClick sa sledećim kodom:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | public function OnMouseClick(e:MouseEvent):void{ if(bunchOfAmmo.length < 2){ var ammo:Ammo = new Ammo(); ammo.y = player.y - ammo.height; ammo.x = player.x + player.width/2 - ammo.width/2; bunchOfAmmo.push(ammo); addChild(ammo); } } |
Sada kada budemo testirali prikaz aplikacije, možemo primetiti da na klik mišem se ispred letelice pojavi zeleni laserski zrak i da se na ekranu mogu nalaziti samo dva. Kod koji smo otkucali upravo to radi.
Objašnjenje.
Naime proverava da li se u nizu projektila nalazi manje od dva. U slučaju da se ne nalazi nijedan ili jedan on kreira novi projektil. Projektil se postavlja na koordinate koje se nalaze tačno ispred sredine letelice. Tačne koordinate projektila dobijamo kada na početku koordinata letelice dodamo polovinu dužine letelice, a onda oduzmemo polovinu dužine projektila. Tako se sredina projektila nalazi tačno na sredini letelice. Potom projektil dodajemo u niz i onda na ekran komandom addChild().
Međutim, možemo primetiti da kada se ispali projektil, on samo stoji. Razlog je u tome što mi ne ažuriramo njegovu poziciju u skladu sa vremenom. Ako se sećate, u mom postu o samom mehanizmu igara Kako da razvijete svoju igru ~ Početak, sam vam prikazao mehanizam (Engine) svake igre, tj Game Loop. Game Loop predstavlja jednu petlju koja ima dve funkcije (metode) Update i Draw. Update funkcija ažurira poziciju grafičkih elemenata igre u odnosu na vreme. Draw metoda vrši iscrtavanje elemenata u odnosu na njihovu poziciju. Možete to vizualizovati sledećom slikom.
U našem slučaju, mi smo za naše grafičke elemente automatski generisali klase u SWF fajlu i te klase su usko vezane sa njima. To znači da nam metoda Draw nije potrebna, jer kada u metodi Update budemo promenili poziciju projektila ili asteroida, automatski će se iscrtati grafički elementi na ekranu, na novoj poziciji.
To znači da je neophodno da implementiramo funkciju Update. U nju moramo da ubacimo funkcije koje će pomerati ispaljene projektile i asteroide (MoveAsteroids, MoveAmmo), funkciju koja će generisati asteroide na vrhu ekrana (SpawnAsteroids) i još jednu funkciju koja proverava da li je došlo do sudara između nekog projektila i nekog asteroida (CheckCollision).
Da bi funkcija Update uopšte bila konstantno pozivana neophodno je da napravimo okidač za njeno automatsko pozivanje. Moramo dodati sledeću liniju koda unutar konstruktora (ispod svih ostalih linija):
stage.addEventListener(Event.ENTER_FRAME, Update);
Ovaj osluškivač (EventListener) će na svaki frame (obično vam stoji podešeno 24 fps u Flash dokumentu), pozivati funkciju Update. Iz tog razloga moramo biti pažljivi i postaviti male vrednosti promenljivih koje će predstavjlati brzinu elementa. U našem slučaju ćemo imati gravitaciju – GRAVITY i brzinu kretanja laserskog zraka – AMMO SPEED.
Update bi trebalo da izgleda ovako (dodajte ga ispod svih funkcija):
1 2 3 4 5 6 7 8 9 10 11 | public function Update(e:Event):void{ SpawnAsteroids(); MoveAsteroids(); MoveAmmo(); CheckCollision(); } |
Odmah počinjemo sa implementacijom.
Spawn Asteroids
Krećemo od metode SpawnAsteroids:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public function SpawnAsteroids():void{ if(bunchOfAsteroids.length < 2){ var newAsteroid = new Asteroid(); newAsteroid.x = Math.random() * stage.stageWidth; newAsteroid.y = - newAsteroid.height; bunchOfAsteroids.push(newAsteroid); addChild(newAsteroid); } } |
Da bi ova metoda funkcionisala moramo imati promenljivu bunchOfAsteroids definisanu iznad konstruktora.
var bunchOfAsteroids:Array = new Array();
Metoda SpawnAsteroids prvo proverava koliko je elementa u nizu bunchOfAsteroids, ako ih ima manje od 2, ona kreira novi asteroid. Potom se podešavaju X,Y koordinate.
Pošto ne želimo da igrač vidi kako se „iznenada“ pojavljuje na ekranu niotkuda, postavljamo njegovu Y koordinatu da bude u negativnoj vrednosti njegove dužine, odnosno iznad vidljivog dela ekrana.
X koordinatu računamo pomoću funkcije Math.random() koja nam nasumično vraća broj između 0 i 1. Tu vrednost onda pomnožimo sa širinom igračevog ekrana i dobijamo nasumično generisanje asteroida duž gornje ivice igračevog ekrana. Time se igraču čini da se asteroidi stvaraju na slučajan način, kao što smo i želeli. (Verujem da vam možda ovo zvuči zbunjujuće, ali ću vam u jednom kasnijem blog postu biti detaljno objašnjeno)
Nakon toga, kao što već znate iz funkcije sa projektilom, dodajemo asteroide u njihov niz i na ekran.
MoveAsteroids
Pomeranje asteroida se vrši na osnovu gravitacije, zato ćemo uvesti konstantu GRAVITY. Unećemo je iznad konstruktora.
var GRAVITY:Number = 5;
Pošto želimo da nam se Asteroidi kreću sporije, da bi nam bilo lakše da ih pogodimo, podesili smo je na manju vrednost.
Suština kretanja elemenata na ekranu je da se pri svakom pozivu metode Update dodaje vrednost GRAVITY na Y koordinatu. Tako se čini da se asteroidi kreću. Ukoliko Y koordinata asteroida pređe donju ivicu ekrana, moramo ga ukloniti sa ekrana. To sve ćemo uraditi u okviru ove funkcije MoveAsteroids.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | public function MoveAsteroids():void{ for (var i:Number = bunchOfAsteroids.length-1; i >= 0; i--){ bunchOfAsteroids[i].y += GRAVITY; if (bunchOfAsteroids[i].y > stage.stageHeight){ removeChild(bunchOfAsteroids[i]); bunchOfAsteroids.splice(i,1); } } } |
U gore navedenom kodu, preko FOR petlje učitavamo svaki Asteroids iz niza bunchOfAsteroids i menjamo mu vrednost Y koordinate. Potom proveravamo da li je Y vrednost veća od dužine ekrana i ako jeste, uklanjamo ga sa ekrana i iz niza.
MoveAmmo
Slično metodi MoveAsteroids, moramo da dodamo konstantu koja predstavlja brzinu naših projektila – AMMO_SPEED:
var AMMO_SPEED:Number = 10;
Nakon toga, na sličan način je implementirana funkcija MoveAmmo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | public function MoveAmmo():void{ for (var i:Number = bunchOfAmmo.length-1; i >= 0; i--){ bunchOfAmmo[i].y -= AMMO_SPEED; if (bunchOfAmmo[i].y + bunchOfAmmo[i].height < 0){ removeChild(bunchOfAmmo[i]); bunchOfAmmo.splice(i,1); } } } |
Kada uporedimo kod metoda MoveAmmo i MoveAsteroids vidimo da je razlika samo u dodavanju/oduzimanju konstanti AMMO_SPEED i GRAVITY od Y koordinate projektila/asteroida.
Ako su vam i dalje nejasne ove funkcije, možete me pitati putem komentara, Twitter-a ili email-a.
CheckCollision
Najteža funkcija za razumevanje, ako ste početnik. Obratite pažnju.
Ako vam ne bude jasno, ne brinite. Ovu metodu ću detaljno objasniti u narednom blog postu.
Prvenstveno moramo da prođemo svaku kombinaciju asteroida i projektila na ekranu, pa će nam biti potrebne 2 FOR petlje koje će proći kroz oba niza tih elemenata. FOR petlja asteroida će biti unutar FOR petlje projektila, jer želimo da za svaki projektil proverimo da li je dovoljno blizu svakog asteroida na mapi da bi došlo do kolizije. Da bismo to pravilno proverili moramo poznavati osnove geometrije .
Da, znam da je većina vas bežala od toga još od osnovne škole, ali kao što vidite ima veoma dobru primenu.
Da bismo došli do tačne razdaljine između asteroida i projektila , moramo znati koordinatnu tačku, poluprečnik i tačnu razdaljinu između centara naših grafičkih elemenata.
Grafički elementi mogu biti malo čudnog oblika, stoga, najsigurnija opcija za proveru razdaljine elemenata jeste da fiktivno opišemo krug oko njih i da koristimo poluprečnik i centar tog kruga za proveru, osim kada je u pitanju element kvadratnog ili pravouganog oblika.
Funkciju upišite posle svih funkcija:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | public function CheckCollision():void{ for (var n:Number = bunchOfAmmo.length-1; n >= 0; n--){ var ammoRadius:Number = bunchOfAmmo[n].width/2; var ammoCentralPoint:Point = new Point(bunchOfAmmo[n].x + ammoRadius,bunchOfAmmo[n].y + ammoRadius); for (var i:Number = bunchOfAsteroids.length-1; i >= 0; i--){ var asteroidRadius:Number = bunchOfAsteroids[i].width/2; var asteroidCentralPoint:Point = new Point(bunchOfAsteroids[i].x + asteroidRadius,bunchOfAsteroids[i].y + asteroidRadius); var hitTolerance:Number = asteroidRadius + ammoRadius; if (Point.distance(asteroidCentralPoint, ammoCentralPoint) <= hitTolerance){ removeChild(bunchOfAsteroids[i]); removeChild(bunchOfAmmo[n]); bunchOfAsteroids.splice(i,1); bunchOfAmmo.splice(n,1); } } } } |
U metodi nakon izračunavanja centra i poluprečnika asteroida, odnosno projektila, proveravamo da li je razdaljina između koordinatnih tačaka centra asteroida i centra projektila, ponaosob, manja ili jednaka od promenljive hitTolerance. Promenljiva hitTolerance predstavlja zbir poluprečnika asteroida i projektila, jer ne želimo da poluprečnike uračunavamo u tu razdaljinu, jer gledamo od centra elemenata ne od ivica.
Zaključak
Nažalost, ovaj blog post je previše dug, čak i za moj ukus. Ali želeo sam da ispunim obećanje i uprkos mojoj želji da ovo bude kratko i brzo, morao sam da ubacim objašnjenja. Nisu previše detaljna, neka su možda i malo konfuzna (nadam se da ćete mi oprostiti), ali smatram da će ovo i za početnike biti donekle dovoljno za adekvatno razumevanje.
U ova dva blog posta bilo je bitno tehnički izraditi iole funkcionalnu igru. Za one koje zanimaju podrobnija objašnjenja određenih funkcija, mogu da isprate sledeći blog post koji će se tome posvetiti.
Srećno!
Download: ZIP fajl cele igre
RSS Feed
Twitter











