petak, 19 aprila, 2024
Kako da...?

LibGDX „Java game development framework” (4. dio)

Autor: Gavrilo Prodanović

U prošlim brojevima, govorili smo o LibGDX-u u kontekstu grafike i ulaza. U ovom broju ćemo reći nešto više o ostalim pomoćnim klasama koje se ne odnose direktno na ovo dvoje, a tu su da ubrzaju i pomognu razvoj igrice i dotjerivanje gameplay-a. Zapoćečemo zvukom čime smo zaoukružili jednu cjelinu koja je potrebna za „kompletnu” igricu (grafika, ulaz i zvuk).

Od formata za audio, LibGDX podržava OGG, MP3 i WAV, a za manipulaciju ovim klasama postoje klase Sound i Music. Osnovna razlika ove dvije klase u primjeni je ta da prvu koristimo za reprodukciju zvučnih efekata, a drugu za streaming neke pjesme u pozadini. Tehnički gledano, Sound klasa dekoduje datoteku i dekodirani stream učitava u RAM, što d omogućuje puštanje efekta u tačno potrebnom trenutku. Iz jedne Souninstance moguće je stimulativno reprodukovati isti efekat više puta, a svaka nova instanca dobija svoj ID koji vraća play metoda. Svakoj novoj instanci možemo posebno da podesimo volumen, loop, pan i pitch. Na Android platformi, Sound instanca ne može da bude veća od 1 MB, dok na iOS-u nije podržan OGG format. Ako želimo da reprodukujemo zvuk koji je duži od nekoliko sekundi, kao što je muzička pratnja, korišćenje Music klase je mnogo bolji izbor jer se datoteka stream-uje sa diska po potrebi umjesto da se učita čitav u RAM. Iz jedne instance Music objekta nije moguće pustiti više uzastopnih numera kao u Sound klase. U toku reprodukcije možemo da promjenimo volumen i pan ili poziciju u sekundama ili da podesimo da playback ide u krug. Takođe, moguće je implementirati listener koji će da „okine” kada se playback završi. Jedna od veoma lijepih opcija po pitanju audija je mogućnost direktnog pristupa hardveru pomoću klase AudioDevice preko čijih metoda možemo da pošaljemo direktno PCM „uzorak” u bafer. Ako želimo da dobijemo ulaz sa mikrofona u tome će nam pomoći AudioRecorder. AudioDevice i AudioRecorder nisu dostupni na JavaScript/WebGLbackend-u. Za sve klase koje smo ovde naveli potrebno je pozvati dispose() kada postanu nepotrebne da bi se oslobodili resursi.

JSON i XML su često korišćeni za organizaciju podataka, pa se među mnogobrojnim klasama nalaze i klase za čitanje i pisanje ovih formata. Za JSON možemo da se poslužimo sledećim klasama: JsonWriter, JsonReader, JsonValue i Json. Funkcija klasa JsonWriter i JsonReader je očigledna; JsonValue opisuje Json objekat, a Json klasa će nam pomoći da neki java objekat serijalizujemo u Json objekat ili da iz Json objekta da deseralizujemo u java objekat. Zahvaljujući automatskoj serijalizaciji u LibGDX, moguće je bez mnogo muke sačuvati ili učitati igru iz datoteke ako je potrebno. U slučaju da se javi potreba da kontrolišete serijalizaciju ili deseralizaciju postoje interfejsi koji će vam u tome pomoći, da li na nivou svoje java klase tako što ćete implementirati Json.Serializable ili na nivou čitavog procesa serijalizacije tako što ćete u Json objektu postavi serijalizer preko setSerializer() metode. Za one kojima je potreban XML, postoji XmlReader i XmlWriter. Ako mislite da je to neki od poznatih javaXML paresera implementiran u LibGDX prevarili ste se. Prema riječima autora, on je napisao namjenski XML parser za LibGDX jer je imao predosjećaj da je smislio nešto malo i brzo i htjeo je da testira svoje vještine u Ragel-u. U testovima LibGDX-ov Xml parser se pokazao mnogo bolji od javax.xml.parsers.DocumentBuilder u datotekama od 1 MB na desktop-u i Android-u, dok je na datoteci od 100 MBjavax parser dobio trku za nešto manje od jedne sekunde, Android je bio isključen u testu na velikim datotekama. Nećemo zalaziti u korišćenje parsera, pošto je XML nešto komplikovaniji od JSON-a.

Matematika u igricama ne može biti zapostavljena, a za čistu matematiku LibGDX obezbjedio paket com.badlogic.gdx.math u kojem postoje raznovrsne klase da nam pomognu. Prvo ćemo spomenuti MathUtils u kojoj postoje statičke metode za uobičajene matematičke operacije kao što je zaoukruživanje, izračunavanja sinusa i kosinusa, konvertovanje stepena u radijane. Spomenućemo da postoji nekoliko zgodnih metoda za generisanje nasumičnih brojeva. Postoje klase koje definišu osnovne geometrijske oblike u ravni kao što su krug, kvadrat, elipsa i klasa koja će nam pomoći da odredimo da li postoji presjek između njih. Postoje klase i za interpolaciju (poznato i kao tweening) koje nam mogu pomoći u sastavljanju animacije. Spomenućemo da su podržani vektori, matrice i kvaternioni. Implementiran su Ear-Clipping algoritam i još neki, ali ne planiramo zalaziti u dubinu matematičkih sposobnosti LibGDX-a i ovde ćemo završiti.

U platformskom ili RPG žanru potrebne su mape koje mogu biti algoritamski generisane ili ručno izrađene za vrijeme razvoja igrice. U našem framework-u postoji čitav jedan skup klasa za rad sa mapama. Mapa je skup slojeva (eng. layer), a svaki sloj posjeduje svoje objekte. Jedan od tipova su Tile maps koje su sastavljene od pločica ili ćelija i karakteristične su za RPG, platformske i slične igre. Tile mape su jedini kompletno implementirani tip mapa, ali urađene su osnovne apstraktne klase koje definišu uopšteno mapu, sloj i objekat tako da bi implementiranje bilo kojeg drugog tipa 2D mape bilo jednostavno. Za tile mape glavna klasa je TiledMap u kojoj se nalaze instance TiledMapTileLayer klase. U layer-ima se nalaze ćelije koje posjeduju referencu na TiledMapTile, a tile-ovi su obično djeljeni među ćelijama. Tile može biti statična tekstura ili animacija. Za renderovanje ovih mapa postoji više rendera. Za rednerovanje ortogonalnih mapa koristićemo OrthogonalTiledMapRenderer. Renderu se u konstruktoru prosleđuje mapa kako bi se izvršila optimizacija i keširanje mape. Za izometrične mape postoji IsometricTiledMapRenderer čije je korišćenje analogno ortogonalnom renderu. Postoji još četiri rendera, ali su oni eksperimentalni i nećemo ih spominjati. Na kraju, ono što je najvažnije među tile mapama jesu formati koji su podržani za učitavanje. Postoje dva editora, odnosno formata koja su podržani. Prvi je open source editor Tiled sa svojim TMX (Tile Map XML) formatom, a drugi je format editora zatvorenog koda Tide.

LibGDX nam je obezbjedio scene2d, da logiku svoje igre organizujemo unutar grafa koji nam pomaže da stvorimo hijerarhiju između naših „glumaca”(eng. actor). Ono što nam scene2d omogućuje su olakšice kao što je upravljanje grupama, na primjer: rotacija ili translacija koju primjenimo na roditelja odraziće se i na potomke. Svaki potomak posjeduje svoj sopstveni kordinatni sistem koji je relativan u odnosu na roditelja. Kroz organizaciju nam dolazi lakša mogućnost za iscrtavanje objekata kroz SpriteBatch, a mogućnost upravljanje ulaza takođe postoji, što nam daje mogućnost da roditelj uhvati događaj prije svojih potomaka ili poslije njih. Mogućnost da se actor-ima zadaju akcije, kao što je kretanje do određene pozicije je ili da pređu određenu dužinu u nekom smjeru relativno na svoju poziciju. Transformacija i mnoge druge je možda glavna prednost scene2d-a da se koristi u gameplay-u. Takođe postoji i paket scene2d.ui koji posjeduje klase potrebne za građenje menija ili HUD-a što može mnogo da nam uštedi vrijeme.

Za kraj, spomenućemo dva engine-a za fiziku koje naš framework podržava. Prvi engine je Box2D namjenjen kao što mu ime govori za 2D fiziku. Od verzije 1.0 je izdvojen kao ekstenzija dok je prije bio uključen u izvorni kod. Box2D je C++engine dok je u LibGDX implementiran preko lakog javawrapper-a. Nećemo zalaziti u dubine, zato što je Box2D čitava priča za sebe, ali skoro svako iskustvo koje posjedujete u C++ jeziku po pitanju ovog engine, može se prevesti na java-u. Po pitanju performansi, Box2D se pokazao i više nego solidan koliko smo mi imali ličnog iskustva u njemu. Pored Box2D, postoji i podrška za Bullet Engine koji je namjenjen za 3D fiziku. On je pisan u C++-u, a za LibGDX postoji Javawrapper.