петак, 19 априла, 2024
Како да...?

LibGDX „Java game development framework” (4. дио)

Аутор: Гаврило Продановић

У прошлим бројевима, говорили смо о LibGDX-у у контексту графике и улаза. У овом броју ћемо рећи нешто више о осталим помоћним класама које се не односе директно на ово двоје, а ту су да убрзају и помогну развој игрице и дотјеривање gameplay-а. Запоћечемо звуком чиме смо заоукружили једну цјелину која је потребна за „комплетну” игрицу (графика, улаз и звук).

Од формата за аудио, LibGDX подржава OGG, MP3 и WAV, а за манипулацију овим класама постоје класе Sound и Music. Основна разлика ове двије класе у примјени је та да прву користимо за репродукцију звучних ефеката, а другу за streaming неке пјесме у позадини. Технички гледано, Sound класа декодује датотеку и декодирани stream учитава у RAM, што d омогућује пуштање ефекта у тачно потребном тренутку. Из једне Sounинстанце могуће је стимулативно репродуковати исти ефекат више пута, а свака нова инстанца добија свој ID који враћа play метода. Свакој новој инстанци можемо посебно да подесимо волумен, loop, pan и pitch. На Android платформи, Sound инстанца не може да буде већа од 1 MB, док на iOS-у није подржан OGG формат. Ако желимо да репродукујемо звук који је дужи од неколико секунди, као што је музичка пратња, коришћење Music класе је много бољи избор јер се датотека stream-ује са диска по потреби умјесто да се учита читав у RAM. Из једне инстанце Music објекта није могуће пустити више узастопних нумера као у Sound класе. У току репродукције можемо да промјенимо волумен и pan или позицију у секундама или да подесимо да playback иде у круг. Такође, могуће je имплементирати listener који ће да „окине” када се playback заврши. Једна од веома лијепих опција по питању аудија је могућност директног приступа хардверу помоћу класе AudioDevice преко чијих метода можемо да пошаљемо директно PCM „узорак” у бафер. Ако желимо да добијемо улаз са микрофона у томе ће нам помоћи AudioRecorder. AudioDevice и AudioRecorder нису доступни на JavaScript/WebGL backend-у. За све класе које смо овде навели потребно је позвати dispose() када постану непотребне да би се ослободили ресурси.

JSON и XML су често коришћени за организацију података, па се међу многобројним класама налазе и класе за читање и писање ових формата. За JSON можемо да се послужимо следећим класама: JsonWriter, JsonReader, JsonValue и Json. Функција класа JsonWriter и JsonReader је очигледна; JsonValue описује Json објекат, а Json класа ће нам помоћи да неки java објекат серијализујемо у Json објекат или да из Json објекта да десерализујемо у java објекат. Захваљујући аутоматској серијализацији у LibGDX, могуће је без много муке сачувати или учитати игру из датотеке ако је потребно. У случају да се јави потреба да контролишете серијализацију или десерализацију постоје интерфејси који ће вам у томе помоћи, да ли на нивоу своје java класе тако што ћете имплементирати Json.Serializable или на нивоу читавог процеса серијализације тако што ћете у Json објекту постави серијализер преко setSerializer() методе. За оне којима је потребан XML, постоји XmlReader и XmlWriter. Ако мислите да је то неки од познатих java XML паресера имплементиран у LibGDX преварили сте се. Према ријечима аутора, он је написао намјенски XML парсер за LibGDX јер је имао предосјећај да је смислио нешто мало и брзо и хтјео је да тестира своје вјештине у Ragel-у. У тестовима LibGDX-ов Xml парсер се показао много бољи од javax.xml.parsers.DocumentBuilder у датотекама од 1 MB на desktop-у и Android-у, док је на датотеци од 100 MB javax парсер добио трку за нешто мање од једне секунде, Android је био искључен у тесту на великим датотекама. Нећемо залазити у коришћење парсера, пошто је XML нешто компликованији од JSON-а.

Математика у игрицама не може бити запостављена, а за чисту математику LibGDX обезбједио пакет com.badlogic.gdx.math у којем постоје разноврсне класе да нам помогну. Прво ћемо споменути MathUtils у којој постоје статичке методе за уобичајене математичке операције као што је заоукруживање, израчунавања синуса и косинуса, конвертовање степена у радијане. Споменућемо да постоји неколико згодних метода за генерисање насумичних бројева. Постоје класе које дефинишу основне геометријске облике у равни као што су круг, квадрат, елипса и класа која ће нам помоћи да одредимо да ли постоји пресјек између њих. Постоје класе и за интерполацију (познато и као tweening) које нам могу помоћи у састављању анимације. Споменућемо да су подржани вектори, матрице и кватерниони. Имплементиран су Ear-Clipping алгоритам и још неки, али не планирамо залазити у дубину математичких способности LibGDX-а и овде ћемо завршити.

У платформском или RPG жанру потребне су мапе које могу бити алгоритамски генерисане или ручно израђене за вријеме развоја игрице. У нашем framework-у постоји читав један скуп класа за рад са мапама. Мапа је скуп слојева (енг. layer), а сваки слој посједује своје објекте. Један од типова су Tile maps које су састављене од плочица или ћелија и карактеристичне су за RPG, платформске и сличне игре. Tile мапе су једини комплетно имплементирани тип мапа, али урађене су основне апстрактне класе које дефинишу уопштено мапу, слој и објекат тако да би имплементирање било којег другог типа 2Д мапе било једноставно. За tile мапе главна класа је TiledMap у којој се налазе инстанце TiledMapTileLayer класе. У layer-има се налазе ћелије које посједују референцу на TiledMapTile, а tile-ови су обично дјељени међу ћелијама. Tile може бити статична текстура или анимација. За рендеровање ових мапа постоји више рендера. За реднеровање ортогоналних мапа користићемо OrthogonalTiledMapRenderer. Рендеру се у конструктору прослеђује мапа како би се извршила оптимизација и кеширање мапе. За изометричне мапе постоји IsometricTiledMapRenderer чије је коришћење аналогно ортогоналном рендеру. Постоји још четири рендера, али су они експериментални и нећемо их спомињати. На крају, оно што је најважније међу tile мапама јесу формати који су подржани за учитавање. Постоје два едитора, односно формата која су подржани. Први је open source едитор Tiled са својим TMX (Tile Map XML) форматом, а други је формат едитора затвореног кода Tide.

LibGDX нам је обезбједио scene2d, да логику своје игре организујемо унутар графа који нам помаже да створимо хијерархију између наших „глумаца”(енг. actor). Оно што нам scene2d омогућује су олакшице као што је управљање групама, на примјер: ротација или транслација коју примјенимо на родитеља одразиће се и на потомке. Сваки потомак посједује свој сопствени кординатни систем који је релативан у односу на родитеља. Кроз организацију нам долази лакша могућност за исцртавање објеката кроз SpriteBatch, а могућност управљање улаза такође постоји, што нам даје могућност да родитељ ухвати догађај прије својих потомака или послије њих. Могућност да се actor-има задају акције, као што је кретање до одређене позиције је или да пређу одређену дужину у неком смјеру релативно на своју позицију. Трансформација и многе друге је можда главна предност scene2d-а да се користи у gameplay-у. Такође постоји и пакет scene2d.ui који посједује класе потребне за грађење менија или HUD-а што може много да нам уштеди вријеме.

За крај, споменућемо два engine-а за физику које наш framework подржава. Први engine је Box2D намјењен као што му име говори за 2D физику. Од верзије 1.0 је издвојен као екстензија док је прије био укључен у изворни код. Box2D је C++ engine док је у LibGDX имплементиран преко лаког java wrapper-а. Нећемо залазити у дубине, зато што је Box2D читава прича за себе, али скоро свако искуство које посједујете у C++ језику по питању овог engine, може се превести на java-у. По питању перформанси, Box2D се показао и више него солидан колико смо ми имали личног искуства у њему. Поред Box2D, постоји и подршка за Bullet Engine који је намјењен за 3D физику. Он је писан у C++-у, а за LibGDX постоји Java wrapper.