• Apgrejdovali smo forum na XenForo 2.1.1, ukoliko imate predloga vezanih za izgled ili funkcionalnost foruma, ili ukoliko naletite na neki problem, javite nam OVDE

    DEFINISALI SMO PRAVILA FORUMA. Pročitajte ih, pojaviće se automatski kada krenete da čitate nešto!

XNA Programiranje Igara | Tutorijal #10 | Prva Kompletna Igra

Uni

PCAXE Addicted
Učlanjen(a)
31.08.2018.
Poruka
2.469
Rezultat reagovanja
984
Moja konfiguracija
PC / Laptop Name:
Lenovo ThinkPad X250 - i5 5300U/8GB/256GB EVO 860/6 Cell
Mice & keyboard:
Bloody V7M & Stock Thinkpad X250 Keyboard
OS & Browser:
Windows 10 + Microsoft Edge | ArcoLinux + i3 + Mozilla Firefox Quantum
Negde početkom ove godine, započeo sam pisanje tutorijala o programiranju i sveukupnom razvoju softvera. To je trasalo nekih dva i po meseca, a onda je krajem februara objavljen poslednji tutorijal osnvova jezika C#. Zatim, kada je trebalo da počnem pisanje pravih tutorijala o igrama, izgubio sam volju da pišem, ali evo me sad ponovo krećemo aktivno :)

Ako nemate barem osnovno znanje o C#-u ili ikakvo znanje o programiranju, preporučio bih vam da pročitate moj mali "kurs" napisan o tome koji je dostupan baš na ovom forumu potpuno besplatno. Sastoji se iz samo 13 kratkih tutorijala, koji će vam omogućiti da rešavate razne logičke probleme, a velika većina ovog kursa se takođe bazira na samim osnovama. Sve što je potrebno jeste da u polje "pretraga" unesete pojam #xna-gwid što će vam pokazati sve objavljene tutorijale. :)

Šta ćemo danas obraditi?
Danas ćemo kreirati, već dugo očekivanu, sasvim funkcionalnu i igrivu video igru! :) Igra je bazirana na prikazivanju kvadrata na ekranu, koje mi moramo pojuriti srelicom kursora. Iako ne deluje toliko zanimljivo, u samo par linija koda, kreiraćete igru, razumeti kako ona tačno funkcioniše i samim tim izdvojiti se od ostatka ljudi koji u većini slučajeva samo igraju igre :) Ako ste spremni, hajde da počnemo!

Kreiranje projekta, kao i nalaženje izvornog koda, ćemo preskočiti. Ukoliko niste sigurni kako to pravilno uraditi, možete to pročitati u ovom tutorijalu LINK
Jedino što treba dodati jeste ubacivanje ove dve linije koda kao petu i šestu :
using System;
using System.Collections;
Struktura Igre
Kako bismo razumeli šta kreiramo i na koji način, moramo znati da C# u zavisnosti od mesta pisanja koda, može prevesti red izvršavanja, ali i same funkcije igre u suprotnom smeru. Nakon kreiranja projekta, sav potreban kod za nesmetan rad naše igre, smeštamo u fajlu Game1.cs. Na samom početku bloka klase Game1, primećujemo dve linije koda koje ne poseduju svoj blok. Ovaj prostor nazivamo prostor za inicijalizaciju. U ovom delu navodimo sve komponente koje ćemo koristiti tokom daljeg programiranja. Hajde da ispunimo taj blok nekim promenljivim vrednostima koje ćemo koristiti. Tačno ispod linije "SpriteBatch spriteBatch;" unesite sledeće :
Random rand = new Random();
Texture2D teksturaKvadrata;
Rectangle trenutniKvadrat;
int rezultat = 0;
float preostaloVreme = 0.0f;
const float ZivotniVekKvadrata = 0.75f;
Color[] boje = new Color[3] {Color.Yellow, Color.Red, Color.Orange};
Sada ćemo razjasniti značenje svih pojmova. Većina ovih reči ne bi trebalo da budu nepoznate osim par razlika koje XNA uvodi u odnosu na standardne biblioteke C#-a. Random, koji smo već ranije pominjali, predstavlja tip korišten za generisanje nasumičnih brojeva, kome dajemo naziv rand. Zatim Texture2D predstavlja tip kojim učitavamo teksture, tzv. sprajtove koji prestavljaju dvodimenzionalne slike. Rectangle je tip koji je u XNA-u tj. MonoGame-u korišten za čuvanje informacija o lokaciji objekata na ekranu, koja se izražava u pikselima i predstavlja horizontalnu i vertikalnu udaljenost objekta od gornjeg levog ugla prozora igre. Rezultat, kao što mu ime govori, jeste promenljiva za čuvanje informacija o broju "ulovljenih" kvadrata, dok preostaloVreme i ZivotniVekKvadrata skladište informacije o broju sekundi koliko će objekat biti prisutan na ekranu, odnosno broju sekundi koliko će ukupno biti prisutan. Na samom kraju, preostaje niz tipa Color, koji služi za skladištenje boja, kao što su Red, CornFlowerBlue, NavyBlue itd.

Poslovna Fora - Primetili ste da je jedino promenljiva ZivotniVekKvadrata imenovana velikim početnim slovom, što je neobavezno, ali ne samo ja već i mnogi drugi programeri koriste ovo za raspoznavanje konstantnih vrednosti (const) čija se vrednost ne menja tokom rada programa.

Metoda Initialize()
Nakon što je naš program pokrenut, na snagu stupa metoda Initialize() koja se izvršava samo jednom tokom životnog veka naše igre odnosno programa. U njemu je korisno izvršiti promene prozora, kao što su rezolucija, vidljivost kursora, otvaranje u punom ekranu itd. Da bismo videli kako ovo zapravo funkcioniše, pokrenimo neizmenjeni program pristikom na dugme Start ili pritiskom tipke F5 na tastaturi. Prevlačenjem strelice na igru, primetićemo da ona nestaje, a budući da želimo da ona bude vidljiva, dodajmo sledeću liniju odmah iza polja "base.Initialize();" u kojoj ćemo uključiti vidljivost strelice :
this.IsMouseVisible = true;
Sada ponovo pokrenimo isti program i videćemo promenu u vidljivosti kursora, koji je sada prisutan svuda na ekranu. :)

Metoda LoadContent()
Jedna od dužnosti metode Initialize, između ostalog je i da poziva metodu LoadContent(). U njenom bloku se odvija učitavanje, kao i "crtanje" slika na ekranu pomoću promenljive spriteBatch, nakon njene inicijalizacije.

Da bismo videli kako ona radi, kreiraćemo sprajt našeg kvadrata. To ćemo jednostavno učiniti, pokretanjem Microsoft Paint-a ili programa za obradu slika po vašem izboru. Kreirajmo beli kvadrat dužine i visine 16px. To možemo učiniti odabirom olovke i zumiranjem do 800%. Zatim kada opcrtamo 16 piksela po horizontali i po vertikali, opsečemo integrisanim alatom traženu dužinu i širinu i na kraju sve obojimo u belo. Ovaj fajl sačuvajmo u lokaciji po želji, pod nazivom kvadrat sa ekstenzijom .bmp (16-bit color bitmap).

Kada smo završili, dodajmo fajl u content folder. Ovo obavljamo koristeći alat zvani MonoGame Pipeline. Sve što je potrebno jeste da se u Solution Explorer-u unutar Visual Studio-a orijentičemo do foldera Content, a zatim pritisnemo desni klik na fajl Content.mgcb pa "Open with". U novom prozoru idemo na "Add", a zatim "...", lociramo Pipeline.exe u "C:\Program Files (x86)\MSBuild\MonoGame\v3.0\Tools" pa odaberemo "Open", pa "OK", pa opet "OK". Sada u novootvorenom prozoru biramo "Add Existing Item", klikom na ikonicu pravougaonog oblika sa plusom u gornjem levom uglu, a onda lociramo kreirani bitmap kvadrat. U prozoru biramo prvu opciju (Copy file), a zatim Add. Kako bismo sačuvali trenutne postavke, idemo na File pa Save.

**Savet** Nakon što jednom dodamo Pipeline u Visual Studio kao open with opciju, on će je automatski koristiti svaki sledeći put
**Savet** Radi efikasnosti, ne zatvarajte Pipeline.


Sada ćemo se ponovo vratiti u Game1.cs i dodati sledeću liniju koda nakon inicijalizacije spriteBatch-a u metodi LoadContent(), koja će učitati naš sprajt u program :
teksturaKvadrata = Content.Load<Texture2D>("kvadrat");
 

Uni

PCAXE Addicted
Učlanjen(a)
31.08.2018.
Poruka
2.469
Rezultat reagovanja
984
Moja konfiguracija
PC / Laptop Name:
Lenovo ThinkPad X250 - i5 5300U/8GB/256GB EVO 860/6 Cell
Mice & keyboard:
Bloody V7M & Stock Thinkpad X250 Keyboard
OS & Browser:
Windows 10 + Microsoft Edge | ArcoLinux + i3 + Mozilla Firefox Quantum
Logika Igre
Sve instrukcije koje računaru naređujemo da izvrši, zvaćemo logikom igre. U prethodnom tutorijalu, objasnili smo da se sva logika smešta u metodi Update() što ćemo sada i primeniti. Da bi naša igra funkcionisala ispravno, unesite navedeni kod, koji ćemo kasnije razmotriti, u metodi Update() :
if (preostaloVreme == 0.0f)
{
trenutniKvadrat = new Rectangle( rand.Next(0, this.Window.ClientBounds.Width - 25), rand.Next(0, this.Window.ClientBounds.Height - 25), 25, 25);
preostaloVreme = ZivotniVekKvadrata;
}
MouseState mouse = Mouse.GetState();
if ((mouse.LeftButton == ButtonState.Pressed) && (trenutniKvadrat.Contains(mouse.X, mouse.Y)))
{
rezultat++;
preostaloVreme = 0.0f;
}
preostaloVreme = MathHelper.Max(0, preostaloVreme - (float)gameTime.ElapsedGameTime.TotalSeconds);
this.Window.Title = "Rezultat : " + rezultat.ToString();
Sve ove linije sada nemaju smisla, ali evo i objašnjenja svake zasebne linije.

Prva stvar koju naš program obavlja jeste provera da li je preostalo vreme jednako 0, što radimo prvim upitom if. Ovo znači da ukoliko jeste, treba nacrtati novi kvadrat na ekranu. Pri crtanju, određuju se koordinate primenjivanjem nasumične pozicije po horizontali (width, tj. širina) i vertikali (height, tj. visina). Preskočićemo prva dva argumenta, tj. rand.Next(bla bla bla). Treći i četvrti argument označavaju širinu i dužinu našeg kvadrata, koje su 25x25px. Sada se vratimo na prva dva argumenta. Pri navođenju nasumičnih vrednosti, funkciji Next tipa Random zadajemo minimalnu i maksimalnu vrednost. Minimalna vrednost u oba slučaja je 0, što bi značilo da je kvadrat u samom uglu. Maksimalna vrednost se određuje oduzimanjem širine kvadrata od širine prozora, kako bismo bili sigurni da je kvadrat uvek nacrtan unutar prozora celom širinom, a slično se dešava i sa dužinom (Window.ClientBounds.Width i Windows.ClientBounds.Height morate naučiti napamet).

MouseState predstavlja tip koji daje povratnu vrednost o poziciji strelice kursora ili je šalje. Pa u toj liniji metodom GetState() dobijamo informaciju o trenutnoj poziciji kursora u pikselima (ponovo mereno od gornjeg levog ugla).

Narednim if upitom, proveravamo da li je korisnik pritisnuo levo dugme miša (mouse.LeftButton == ButtonState.Pressed) ali i da li kursor prekriva kvadrat (trenutniKvadrat.Contains(mouse.X, mouse.Y)). Ukoliko su oba uslova ispunjena, povećavamo rezultat igrača za 1, ali i prekidamo životni vek kvadrata. Isključivanjem tajmera (prekidanjem životnog veka), indukujemo da je potrebno kreirati novi na sledećem pozivu metode Update(). Zapravo ne kreiramo novi kvadrat, već premeštamo trenutni, što radimo radi efikasnosti, ali u praksi to deluje prilično prolazno.

U poslednjim linijama, pomoću metode Max, klase MathHelper, smanjujemo preostalo vreme za onoliko milisekundi koliko je proteklo, čije odbrojavanje počinjemo od 0, nakon kreiranja svakog novog kvadrata. Na samom kraju, naziv prozora menjamo u Rezultat: i ispisujemo ukupan rezultat korisnika.

Metoda Draw()
Ovu metodu, takođe smo objasnili u prethodnom tutorijalu, ali ukratko ona omogućava iscrtavanje svih sprajtova na ekranu. Ako probamo da pokrenemo prethodno napisanu igru, ponovo će nas sačekati prazan prozor. Ovo se dešava jer ništa nismo iscrtali na ekranu. Sada ćemo dovršiti našu igru, dodavanjem finalnih nekoliko linija. U metodi Draw() ispred linije "base.Draw(gameTime);" dodajmo sledeći kod :
spriteBatch.Begin();
spriteBatch.Draw(
teksturaKvadrata,
trenutniKvadrat,
boje[rezultat % 3]);
spriteBatch.End();
Kao što smo već pomenuli, spriteBatch se koristi za iscrtavanje sprajtova. Da bi znao kada treba da crta, a kada ne, započinjemo crtanje komandom Begin, a završavamo komandom End. Između ovih linija, navešćemo sve što ima veze sa crtanjem tokom celog programa. Draw je metoda koja u sebe unosi nekoliko argumenata, od kojih su najbitnija 3 : šta nacrtati, gde nacrtati, kojom bojom nacrtati. Radi malo kreativnosti, boje izvlačimo iz niza preko ostataka rezultata deljivosti brojem 3, jer imamo samo 3 boje u ponudi, za n boja, to možete odrediti ostatkom pri deljenju sa n.

Šta dalje?
Sada samo preostaje da se igrate! :)
Danas smo uspeli da kreiramo jednu jednostavnu igru, koja sadrži elemente mnogih većih igara. U sledećem tutorijalu otpočećemo kreiranje neke druge igre, koja će naravno biti malo kompleksnija, ali ipak imati dosta zajedničkih elemenata sa ovom. Ako smatrate da sam nešto propustio, napišite u komentaru, a naravno ako imate neke probleme, tu smo komuna i ja da pomognemo. :)
 

Uni

PCAXE Addicted
Učlanjen(a)
31.08.2018.
Poruka
2.469
Rezultat reagovanja
984
Moja konfiguracija
PC / Laptop Name:
Lenovo ThinkPad X250 - i5 5300U/8GB/256GB EVO 860/6 Cell
Mice & keyboard:
Bloody V7M & Stock Thinkpad X250 Keyboard
OS & Browser:
Windows 10 + Microsoft Edge | ArcoLinux + i3 + Mozilla Firefox Quantum
Mala korekcija koju sam ispustio tokom pisanje, konkretno objasnjenje pete i šeste linije koda. MonoGame po fabričkim podešavanjima učitava samo i isključivo XNA specifične biblioteke. Ovo znači da mnoge tipove, kao i klase, metode itd, nećemo imati na raspolaganju, recimo Random, Array, List i mnoge "osnovne" koje smo koristili u prethodnom kursu. Ovo rešavamo učitavanjem imenskih prostora System u kome se nalazi za ovu igru potreban Random, kao i System.Collections koji poseduje Stack, Heap, Array i mnoge druge kolekcije. :)
 
Vrh