Android in razvoj 3D igre
V prejšnjih člankih smo predstavili vrsto potrebnih konceptov in pomembnih stvari, na katere moramo biti pozorni pri razvoju 3D iger. Tako smo pokukali v osnove 3D grafike in predstavili nekaj naprednejših funkcionalnosti in učinkov. Prav tako smo spoznali tudi nekaj naprednejših konceptov, kot so scenski graf in fizikalni pogon, ki nam olajšajo izdelavo določenih delov igre, kot so razporejanje elementov na prizorišču in obnašanje elementov v navideznem svetu v skladu z določenimi fizikalnimi zakoni. Vsebina članka je skupaj s programsko kodo dosegljiva na android.monitor.si.
Oboroženi z novim znanjem, predstavljenim v nekaj preteklih člankih, se bomo v pričujočem članku lotili razvoja enostavne 3D igre in se tako spoznali s poenostavljenim pristopom k izdelavi 3D igre. Seveda se ne bomo takoj lotili razvoja naslednika ige Crysis za platformo Android, temveč bomo razvili kar se da enostavno igro. Pri tem bomo predstavili tudi sam postopek razvoja, od zamisli pa do končne igre. Tako boste lahko v prihodnje s podobnim pristopom poskusili razviti kompleksnejše igre. Razvoj 3D iger je v marsičem podoben razvoju 2D iger, seveda pa se močno razlikuje v implementaciji prikaza, saj moramo upoštevati dodatno prostorsko razsežnost. To s seboj prinese tudi nekaj novih težav.
Programska ogrodja za razvoj
Z raziskovanjem osnovnih in naprednejših tehnik smo si ogledali tiste najosnovnejše dele, ki nas nevede spremljajo v interakciji z večpredstavnimi vsebinami. Z nizkonivojskim pristopom smo v prejšnjih člankih predstavili načela, po katerih delujejo tudi najnaprednejše igre. Med razvojem pa zaradi časovne omejitve aplikacije ne sestavljamo iz tako osnovnih gradnikov, temveč uporabljamo ogrodja za razvoj. Nekatera ogrodja znanih iger smo omenili v prejšnjem članku. Namen ogrodij je olajšanje in skrajšanje razvoja skozi vnovično rabo višjenivojskih gradnikov, ki se pogosto pojavljajo pri razvoju. Z višjim nivojem abstrakcije in vnovične rabe gradnikov seveda lahko izgubimo pri optimizaciji, a se bo slednja obrestovala pri hitrosti razvoja igre. V našem primeru bomo za razvoj 3D igre uporabili ogrodje Rajawalli (https://github.com/MasDennis/Rajawali). Poleg uporabljenega je seveda še kopica podobnih ogrodij (podobna zgleda sta: min3d in dwarf-fw), a smo zaradi naše lastne preference izbrali omenjenega. Ogrodje Rajawali je prosto dostopno programsko ogrodje za razvoj z OpenGL ES 2.0 za mobilno platformo Android. Slednje vsebuje naprednejše funkcionalnosti, kot so podpora uvozu 3D predmetov raznovrstnih formatov, upravljanje senčilnikov in še kup drugih funkcionalnosti, ki nam bodo olajšale delo pri razvoju igre.
Ogrodje Rajawali
Ogrodje smo izbrali zaradi proste dostopnosti na spletnem mestu za gostovanje projektov Github, pa tudi zaradi številnih implementiranih funkcionalnosti in podpore OpenGL ES 2.0. Dodatna dokumentacija in vodniki za uporabo ogrodja so prav tako na omenjeni spletni strani. V nadaljevanju si bomo pogledali, kako ogrodje usposobimo za uporabo in na nekaj zgledih prikazali, kako ga uporabiti.
Slika 1: Zaslonski posnetek aplikacije z zgledi, zgrajenimi ob pomoči ogrodja Rajawali.
Ogrodje Rajawali se lahko pohvali s tem, da ga je za izdelavo svojih aplikacij uporabila vrsta razvijalcev, katerih seznam je objavljen na domači strani ogrodja. Ogrodje se nenehno prilagaja in razširja ter s tem dodaja podporo najnovejšim funkcionalnostim sistema OpenGL ES 2.0 odpravlja morebitne hrošče in dodaja nove funkcionalnosti. Za ogrodje je razvitih kar nekaj ilustrativnih zgledov rabe posameznih funkcionalnosti, s katerimi si lahko razvijalci pomagamo pri razvoju naših lastnih aplikacij. Nekoliko zastarel je le vodnik, ki opisuje, kako si ogrodje za uporabo pripravimo na svojem računalniku.
Priprava ogrodja za uporabo
Ogrodje je zasnovano kot programska knjižnica, ki jo uporabimo pri razvoju lastne aplikacije. Na domači strani je na voljo izvorna koda knjižnice v obliki projekta Android za razvojno okolje Eclipse. Izvorno kodo lahko z domače strani prenesemo v obliki ZIP arhiva, tega pa nato preprosto uvozimo v okolje Eclipse (File -> Import -> Existing Projects into Workspace -> Select archive file, nato izberemo preneseni ZIP arhiv, v katerem je omenjeni projekt, in izbiro potrdimo). S tem smo v razvojno okolje uvozili programsko knjižnico in smo nared za začetek razvoja lastne aplikacije.
Kot smo že omenili, so nam v veliko pomoč tudi zgledi rabe programske knjižnice, ki so na voljo tudi kot aplikacija za platformo Android v spletni trgovini Google Play (v iskalnik preprosto vnesite besedo Rajawali). Tako kot je na voljo izvorna koda odprtokodnega ogrodja, je na voljo tudi izvorna koda omenjene aplikacije z zgledi (https://github.com/MasDennis/RajawaliExamples). Iz omenjenega spletnega mesta lahko podobno prenesemo in v razvojno okolje uvozimo projekt za platformo Android. Da bomo lahko aplikacijo z zgledi prevedli, moramo prej v razvojno okolje uvoziti samo knjižnico, kot smo opisali zgoraj. Po uspešnem uvozu obeh projektov lahko aplikacijo z zgledi tudi prevedemo in zaženemo na naši napravi. S tem smo nared, da se tudi samo postopoma lotimo razvoja lastne aplikacije, ki pri delovanju uporablja funkcionalnosti ogrodja Rajawali.
Sposobnosti ogrodja
Ogrodje nam občutno olajša razvoj igre, saj nam poenostavi celo vrsto stvari. Ogrodje omogoča uvoz in prikaz različnih vrst 3D modelov, ki so lahko tako statični (shranjeni v formatih OBJ, 3DS ipd.) kot animirani (shranjeni v formatih FBX, MD2 ali MD5). Tako pridobimo možnost enostavnega uvažanja kompleksne geometrije, s tem pa se izognemo potrebi po ročni definiciji 3D modelov, kot smo za preprost model trikotnika in piramide prikazali v prejšnjih člankih.
Z materiali lahko definiramo, kako bodo 3D modeli obarvani in kako se bodo odzivali na svetlobo luči na prizorišču. Ogrodje nam omogoča uporabo pestre množice materialov, od preprostega difuznega materiala, s katerim definiramo odziv na difuzno komponento svetlobe, pa do prosojnih materialov, kožnih materialov, video materialov, materialov okolja (angl. skybox) in materialov z vdolbinami (angl. bump map) ter materialov, namenjenih prikazu sistemov delcev. Seveda pa nam ogrodje omogoča, da razvijalci razvijemo in definiramo tudi lastne materiale v programskem jeziku GLSL. Tako nam ogrodje omogoča, da geometrijo, uvoženo ali ročno narejeno, oblečemo v ustrezne materiale in jih s tem predstavimo čim bolj realistično. Poleg materialov nam ogrodje olajša tudi izdelavo senčilnikov in s tem še razširi možnosti prikaza bolj realistične podobe 3D sveta in dodatnih učinkov. Poleg »oblačenja« objektov v materiale nam ogrodje omogoča, da primerno »oblečemo« tudi okolje z uporabo »prostorske škatle« (angl. skybox), kar izvedemo z nekoliko večjo škatlo, ki jo z notranje strani oblečemo v ustrezne teksture in jo skupaj s kamero pomikamo po navideznem svetu. S tem dobimo občutek oddaljenih objektov ozadja.
Slika 2: Napenjanje tekstur na objekte in nalaganje modelov iz datotek
Zelo pomemben del skoraj vsake aplikacije, še posebej tistih, ki pričakujejo interakcijo z uporabnikom, je grafični uporabniški vmesnik. Tudi tu nam ogrodje Rajawali pomaga z zbirko prej pripravljenih elementov uporabniškega vmesnika, s katerimi lahko preprosto definiramo celoten grafični uporabniški vmesnik naše aplikacije. Ogrodje pa nam zelo olajša tudi izbiro elementov v 3D prostoru, tako da samim razvijalcem ni treba implementirati inverznih prostorskih transformacij nad matrikami objektov.
Knjižnica obsega tudi zbirko metod za enostavno definicijo sistemov delcev in animacijo objektov v 3D prostoru. Animacijski del omogoča zelo preprosto izvajanje akcij v želenem časovnem intervalu. Animacije se lahko izvedejo le enkrat ali pa se ponavljajo v neskončni zanki. Ogrodje prav tako omogoča, da zaporedne okvirje izrisujemo z želenim številom sličic na sekundo, kar omogoča enostavno izvajanje akcij v želenem časovnem zaporedju.
Kot smo v zadnjem članku omenjali, nam razvoj iger zelo olajša uporaba scenskega grafa, kjer lahko med elementi scene določimo hierarhijo in jih tako laže razporejamo po prostoru. Tudi to je ena izmed funkcionalnosti, ki nam jo ponuja ogrodje. Vsak objekt lahko hierarhično podredimo nekemu drugemu objektu, element, ki je najviše po hierarhiji, pa neposredno podredimo prizorišču in s tem sprožimo, da se izriše na zaslonu. Medsebojno interakcijo med elementi lahko preverjamo tudi z detekcijo trkov, ki je prav tako del ogrodja. Objekte lahko obdamo z obsegajočo škatlo (angl. bounding box) ali obsegajočo sfero (angl. bounding sphere), lahko pa definiramo tudi lastno vsebujoče telo, za katero moramo seveda sami definirati, kako bomo zanj zaznavali trke z drugimi objekti.
Ne nazadnje nam ogrodje omogoča tudi uporabo različnih končnih učinkov, kot so odsevi leč, in filtrov, kot so valovanje, sepia ipd. Ogrodje omogoča tudi implementacijo lastnih učinkov in filtrov z implementacijo lastnih senčilnikov oglišč v jeziku GLSL.
Zgoraj smo predstavili nekaj najuporabnejših lastnosti ogrodja Rajawali. Samo ogrodje ponuja še dosti funkcionalnosti, ki jih nismo omenili, njihovo spoznavanje pa bomo prepustili bralcu.
Slika 3: Prikaz sistemov delcev in detekcije trkov
Kako začeti?
Za lažje spoznavanje z ogrodjem bomo v nadaljevanju predstavili, kako ustvariti in ustrezno nastaviti nov projekt v razvojnem okolju Eclipse in kako izdelati uvodno aplikacijo. Postopek razvoja takšne aplikacije je veliko preprostejši kot z nizkonivojskim pristopom z ukazi OpenGL, ki smo ga predstavili v preteklosti. Odpade veliko podrobnosti, ki jih namesto nas opravijo inicializacijske funkcije ogrodja in s tem ustrezno pripravijo okolje.
Razvoj se začne podobno kot za vsako drugo androidno aplikacijo, pri predpostavki, da smo v naše razvojno okolje že uvozili projekt Rajawali, kot smo to že predstavili. V okolju Eclipse torej ustvarimo nov projekt (File -> New -> Android Application Project). Določimo mu ime, Build SDK nastavimo na različico naše naprave (naj bo višja od API11, sami smo razvijali za različico API16). Najnižjo zahtevano različico za SDK pa nastavite na API11. Na tem mestu lahko določite tudi lastno zagonsko ikono aplikacije (Create custom launcher icon), za kar se v našem primeru nismo odločili. Izberemo gumb Next in v naslednjem oknu izberemo kreiranje lastne prazne aktivnosti (Create Activity -> BlankActivity). Spet potrdimo izbiro s klikom gumba Next in v naslednjem oknu izberemo ime naše zagonske aktivnosti. V našem primeru smo aktivnosti določili ime HelloRajawali. Druge izbire pustimo na privzetih vrednostih. Kreiranje projekta potrdimo s klikom gumba Finish.
Po uspešnem kreiranju projekta se nam v razvojnem okolju odpre okno naše aplikacije (oz. natančneje videza naše aplikacije). Ker se s samim videzom aplikacije zaenkrat ne bomo ukvarjali, lahko okno zapremo. V nadaljevanju se bomo posvetili ustvarjeni dejavnosti, ki jo bomo tako predelali, da bo prikazovala vrtečo kocko izbrane barve.
Da bomo lahko uporabljali knjižnico Rajawali, moramo svoj projekt ustrezno nastaviti – povezati s projektom knjižnice. To storimo tako, da projekt kliknemo z desnim gumbom miške in izberemo Properties. Na levi strani izberemo sklop nastavitev Android, nato na pa na desni strani v spodnjem razdelku Library dodamo povezavo na projekt Rajawali. Kliknemo gumb Add..., odpre se nam okno za izbiro projekta s knjižnico, kjer bi moral biti projekt Rajawali. Izberemo ga in izbiro potrdimo. V prejšnjem oknu se nam na seznamu prikaže knjižnica Rajawali in poleg nje zelena kljukica. Ogrodje Eclipse nas bo morebiti opozorilo, da projekta vsebujeta nezdružljivi različici knjižnice android-support. Če se to zgodi, lahko brez težav iz našega projekta izbrišemo mapo libs oz. knjižnico android-support-v4.jar znotraj te mape. S tem bomo odpravili morebitno nezdružljivost med knjižnicama.
Odpremo našo aktivnost, ki jo najdemo v mapi src našega projekta (HelloRajawali.java), in na zaslonu se nam prikaže izvirna koda razreda, ki vsebuje tudi dve metodi: onCreate in onCreateOptionsMenu. Druge metode ne bomo potrebovali in jo lahko v celoti izbrišemo, prvo metodo pa bomo prikrojili našim potrebam. Prav tako bomo v metodi onCreate izbrisali vrstico, ki naši aktivnosti določi videz (angl. layout). Da bomo lahko začeli uporabljati tudi funkcionalnosti ogrodja, bo naš razred namesto razreda Activity razširjal razred RajawaliActivity.
Na tem mestu naj pojasnimo, da nas bo razvojno okolje opozarjalo na to, da je razred RajawaliActivity opuščen (angl. deprecated, metode in razredi bodo prečrtani in rumeno podčrtani). Ogrodje Rajawali namreč sledi najnovejšim trendom razvoja v Androidu, ki obsegajo razvoj s fragmenti. Kljub temu bomo razvijali na omenjeni način, saj bomo tako ohranili tudi funkcionalnost za nazaj. Osnovno ogrodje aktivnosti je prikazano v spodnji programski kodi.
import rajawali.RajawaliActivity;
import android.os.Bundle;
public class HelloRajawali extends Rajawa
liActivity {
@Override
public void onCreate(Bundle savedInstan
ceState) {
// okno aplikacije brez naslovne
// vrstice
requestWindowFeature(Window.FEATURE_NO_TITLE);
// okno aplikacije preko celotnega
// zaslona
getWindow().setFlags(
WindowManager.LayoutParams.FLAG_FULL
SCREEN,
WindowManager.LayoutParams.FLAG_FULL
SCREEN);
super.onCreate(savedInstanceState);
}
}
V takem stanju se nam bo aplikacija sicer zagnala, a se bo kmalu nepredvideno prekinila, saj nismo definirali, kaj naj aplikacija izrisuje in kako. Tako moram v naš projekt dodati nov razred – izrisovalnik (angl. renderer). V našem primeru bomo razred poimenovali HelloRenderer. Osnovno ogrodje izrisovalnika je prikazano spodaj.
import android.content.Context;
import rajawali.renderer.RajawaliRenderer;
public class HelloRenderer extends Rajawa
liRenderer{
public HelloRenderer(Context context) {
super(context);
// nastavimo hitrost izrisa
setFrameRate(60);
}
public void onSurfaceCreated(GL10 gl, EGL
Config config) {
super.onSurfaceCreated(gl, config);
}
}
V predhodno definirano aktivnost HelloRajawali dodamo uporabo izrisovalnika, kot je prikazano spodaj (tropičje predstavlja nespremenjene dele programske kode).
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
...
import android.view.Window;
import android.view.WindowManager;
public class HelloRajawali extends Rajawa
liActivity {
HelloRenderer mRenderer;
@Override
public void onCreate(Bundle savedInstan
ceState) {
...
mRenderer = new HelloRenderer(this);
mRenderer.setSurfaceView(mSurfaceView);
super.setRenderer(mRenderer);
}
}
Če aplikacijo zaženemo v takšni obliki, se bo brez težav zagnala in se ne bo več nepredvideno zaprla. Na zaslonu pa bomo videli le črnino, ki večinoma pomeni uspešno inicializiran sistem OpenGL. Da bomo dosegli cilj – vrtečo kocko – moramo zgolj še nekoliko dodelati razred HelloRenderer. V nadaljevanju bomo znova definirali dve metodi razreda RajawaliRenderer; prva metoda je initScene, namenjena nastavljanju začetnih parametrov pred začetkom izrisa, med katere spadajo definicije luči, nalaganje modelov, prirejanje materialov in podobno; druga metoda je onDrawFrame, ki se izvede ob izrisu novega okvirja in definira sam izris.
V spodnjem zgledu je prikazano, kako definiramo kocko (Cube), kako definiramo preprosto usmerjeno luč (DirectionalLight) in difuzni material (DiffuseMaterial). Luči po kreiranju nastavimo smer, barvo in moč svetlobe, ki jo oddaja na prizorišče. Materialu ne bomo prirejali dodatnih lastnosti, kar je sicer mogoče. Kocki pa bomo ob kreiranju določili velikost, material, luč in barvo. Po končanem kreiranju kocko dodamo na prizorišče in določimo še položaj kamere v prostoru (odmaknemo jo od objekta, da ga bomo dejansko videli). Za rotacijo kocke pa v metodi onDrawFrame preprosto popravljamo rotacijo kocke okoli osi Y ob vsakem izrisu novega okvirja.
Slika 6: Zaslonski posnetek uvodne aplikacije HelloRajawali
...
import rajawali.lights.DirectionalLight;
import rajawali.materials.DiffuseMaterial;
import rajawali.math.Number3D;
import rajawali.primitives.Cube;
public class HelloRenderer extends Rajawa
liRenderer{
// definicija objektov
private DirectionalLight mLight;
private Cube mCube;
private DiffuseMaterial mMaterial;
...
protected void initScene() {
// definicija usmerjene luči
mLight = new DirectionalLight(1.0f, 2.0f, 1.0f);
mLight.setColor(1.0f, 1.0f, 1.0f);
mLight.setPower(2);
// definicija materiala
mMaterial = new DiffuseMaterial();
// definicija kocke in prirejanje
// materiala, barve in luči
mCube = new Cube(1.0f);
mCube.addLight(mLight);
mCube.setMaterial(mMaterial);
mCube.setColor(new Number3D(1.0f, 0.0f, 0.0f));
// dodajanja objekta v sceno
this.addChild(mCube);
// premaknemo kamero iz izhodišča
mCamera.setZ(-4.0f);
}
public void onDrawFrame(GL10 glUnused) {
super.onDrawFrame(glUnused);
// ob vsakem izrisanem okvirju
// povečamo rotacijo okoli osi Y
mCube.setRotY(mCube.getRotY() + 1);
}
}
Predstavljeni zgled prikazuje zelo preprost način rabe ogrodja Rajawali in predstavi osnovne koncepte, ki jih potrebujemo pri izdelavi igre. Ogrodje poleg 3D primitiva kocka pozna še sfero, črto, ploskev in delce. Zelo preprosto pa lahko uvozimo tudi 3D model iz datoteke, to bomo predstavili prihodnjič.
Slika 4: Prikaz lastnih senčilnikov in zgled uporabniškega vmesnika
Idejna zasnova igre
Po kratkem pregledu funkcionalnosti ogrodja Rajawali in uspešno izvedeni uvodni aplikaciji je čas, da podrobneje predstavimo tudi idejno zasnovo igre, ki jo bomo skupaj razvili v prihajajočih člankih. Osnovna zamisel je izdelati igro, podobno starodavni igri Asteroids, ki bo ohranila pogled iz ptičje perspektive, a bodo vsi elementi v njej v treh razsežnostih. Tako se bodo tako asteroidi kot nasprotniki in bonusi vrteli okoli vseh treh osi, a bodo zaradi preprostosti ostajali v isti ravnini. S tem bo igra dovolj preprosta za izvedbo in bo hkrati prikazala osnovne koncepte razvoja v treh dimenzijah. Objekti v igri, kot so vesoljska ladja, asteroidi, napadalci, izstrelki …, bodo 3D modeli, ki jih bomo v igro uvozili iz datotek. Poskusili bomo dodati tudi različne učinke, kot so eksplozije, za boljšo igralsko izkušnjo.
Slika 5: Nastavitev povezave s projektom knjižnice Rawajali
• • •
V članku smo najprej predstavili izbrani del funkcionalnosti ogrodja Rajawali in navedli prednosti, ki jih takšno ogrodje prinaša k razvoju 3D aplikacij. V nadaljevanju smo predstavili, kako ogrodje pripravimo za uporabo v razvojnem okolju Eclipse, in si ogledali njegovo rabo na preprostem zgledu rotirajoče kocke v prostoru. Nazadnje smo predstavili samo zasnovo igre, ki jo bomo skupaj razvili v prihajajočih člankih. Tako bomo v naslednjem članku začeli s samim razvojem igre ob pomoči predstavljenega ogrodja.