Download
Igra 3D tetris Vam pruža nezaboravno iskustvo stavljajući dimenzije i iskustvo igranja tetrisa na višu razinu! Igra je apsolutno besplatna i dostupna putem sljedeće poveznice.
Ovaj projekt je u potpunosti open source, te ga možete preuzeti na GitHub-u putem ove poveznice.
Multimedija
univ.bacc.inf. Domić Krunoslav
Student Fakulteta organizacije i informatike Sveučilišta u Zagrebu, Diplomski studij informatike, smjer Informacijsko i programsko inženjerstvo
(kontakt: kdomic@foi.hr)
univ.bacc.inf. Knežević Matija
Student Fakulteta organizacije i informatike Sveučilišta u Zagrebu, Diplomski studij informatike, smjer Informacijsko i programsko inženjerstvo
(kontakt: mknezevi1@foi.hr)
Kontakt podaci
FOI, Pavlinska 2, Varaždin, Croatia
Krunoslav Domić - kdomic@foi.hr
Matija Knežević - mknezevi1@foi.hr
Dokumentacija
Tehničke specifikacije
Zahtjev za pokretanje: Android OS 2.2 i više
Aplikacija razvijena pod: Windows OS i Linux OS
Razvojno okruženje: Eclipse
API: OpenGL ES 1.1 i Android ADK
Dodaci razvojnom okruženju: android-support-v7-appcompat
Testna okolina: Genymotion
Diagram igre
Objašnjanje realizacije
Za potrebe izrade ove igre kreirali smo nekoliko specijaliziranih algoritama kako bismo uspjeli realizirati cjelokupnu logiku igre. Značajniji algoritmi koji su kreirani su sljedeći:
- Algoritam za kreiranje i iscrtavanje objekata
- Algoritam za rotaciju objekata
- Algoritam za detekciju kolizija
- Algoritam za poništavanje redova
Algoritam za kreiranje i iscrtavanje objekata
Za pohranjivanje i iscrtavanje objekata u našem programu koristi se trodimenzionalna bool matrica.
Kako bi se definirano novi objekt u programu sve što je potrebno napraviti je kreirati trodimenzionalnu kvadratnu matricu i popuniti je s bool vrijednostima (true ili false). Sukladno toj matrici algoritam će iscrtavati objekte na ekran.
Algoritam za rotaciju objekata
Za rotaciju objekata nije korištena nativna funkcija od OpenGl-a. Budući da su svi objekti napravljeni u matričnoj reprezentaciji rotacija objekata se vrši na tako da se trodimenzionalna matrica zarotira po određenoj osi.
Algoritam za detekciju kolizija
Budući da svi objekti u igri imaju matričnu reprezentaciju detekcija kolizija se vrši upravo pomoću njih. U igri osim matrice za svaki objekt postoje i dvije globalne matrice koje reprezentiraju stanje igre. Jedna globalna matrica je tipa bool i predstavlja zauzetost ploče, dok druga globalna matrica je tipa string te sadrži informaciju o boji zauzetog polja. Jednostavnom primjenom logičkih usporedbi globalnih matrica i matrica objekata dolazimo do algoritma za detekciju kolizije objekata.
Algoritam za poništavanje redova
I na kraju kreiran je algoritam za poništavanje redova koji u svakom ciklusu provjerava da li u nekoj matici postoji red da je u potpunosti popunjen, te ako postoji uklanja ga.
Dijelovi programskih kodova za algoritme biti će navedeni u nastavku.
Detekcija prstiju
Za interakciju korisnika s igrom koristili smo mogućnost detekcije pritiska.
Naime, ako korisnik s jednim prstom prođe po ekranu u jednom od smjerova
(gore, dolje, lijevo, desno) objekt se pomiče sukladno odabranom.
Ako korisnik prođe s dva ili tri prista po ekranu objekt se rotira sukladno gesti.
Dok ako korisnik napravi dupli klik po ekranu objekt se spušta na najnižu moguću razinu.
Kod ove implementacije zanimljivo je pogledati matematičko određivanje smjera pomaka prstiju koje se određuje pomoću algoritma u nastavku.
public int detectDirection(float xFirst, float yFirst, float xSecond, float ySecond) { int rez = 0; int limit = 50; try { float diffY = ySecond - yFirst; float diffX = xSecond - xFirst; if (Math.abs(diffX) > Math.abs(diffY)) { if (Math.abs(diffX) < limit) return 0; //noSwipe if (diffX > 0) { return 1; // onSwipeRight(); } else { return 2; // onSwipeLeft(); } } else { if (Math.abs(diffY) < limit) return 0; if (diffY > 0) { return 3; // onSwipeBottom(); } else { return 4; // onSwipeTop(); } } } catch (Exception exception) { exception.printStackTrace(); } return rez; }
Dijelovi programskog koda
Primjer oblikovanja objekta: Prikazan je primjer oblikovanja objekta L. Pritom su realizirane dvije različite implementacije. Prva implementacija je zakomentirana i ona se sastoji od kreiranja složenog objekta (objekta L) od niza kockica (objekta klase Cube) koje se translatiraju i iscrtavaju kako bi dale traženi složeni objekt. Druga implementacija je optimizirana i sastoji se od matrične reprezentacije objekta. Tu matričnu reprezentaciju prima metoda drawObject() koja potom iscrtava objekt, na gore opisan način, a ovisno o datoj matričnoj reprezentaciji
String color = "#1D1DE7"; boolean objectMatrix[][][]; public ObjectL() { objectMatrix = createFalsMatrix(3); objectMatrix[0][0][0] = true; objectMatrix[0][0][1] = true; objectMatrix[0][0][2] = true; objectMatrix[1][0][0] = true; } @Override public void draw(GL10 gl) { drawObject(gl, objectMatrix, color); /*gl.glPushMatrix(); Cube c = new Cube(color); gl.glTranslatef(1, 0, 0); c.draw(gl); gl.glTranslatef(-1, 0, 0); c.draw(gl); gl.glTranslatef(0, 0, 1); c.draw(gl); gl.glTranslatef(0, 0, 1); c.draw(gl); gl.glPopMatrix();*/ }
Metoda za crtanje objekata: Kako je već gore opisano metoda drawObject iscrtava složeni objekt zadan njegovom matričnom reprezentacijom
public void drawObject(GL10 gl, boolean objectMatrix[][][], String color){ gl.glPushMatrix(); Cube c = new Cube(color); for (int i = 0; i < objectMatrix.length; i++) for (int j = 0; j < objectMatrix.length; j++) for (int k = 0; k < objectMatrix.length; k++) if(objectMatrix[i][j][k] == true){ gl.glPushMatrix(); gl.glTranslatef(i, j, k); c.draw(gl); gl.glPopMatrix(); } gl.glPopMatrix(); }
Metode za rotaciju objekata
public boolean[][] rot(boolean[][] a) { boolean rotatedMatrix[][] = new boolean[a.length][a.length]; for (int i = 0; i < a.length; i++) for (int j = 0; j < a.length; j++) rotatedMatrix[a.length - (j + 1)][i] = a[i][j]; return rotatedMatrix; } public boolean[][][] rotateX(boolean objectMatrix[][][]) { if(GameStatus.getCurrentObjectX()+GameStatus.getCurrentObject().getZsize()>=GameStatus.getGridSize()){ return objectMatrix; } boolean rotatedMatrix[][][] = createFalsMatrix(objectMatrix.length); for (int j = 0; j < objectMatrix.length; j++) { boolean temp[][] = new boolean[objectMatrix.length][objectMatrix.length]; for (int i = 0; i < objectMatrix.length; i++) for (int k = 0; k < objectMatrix.length; k++) temp[i][k] = objectMatrix[i][j][k]; temp = rot(temp); for (int i = 0; i < objectMatrix.length; i++) for (int k = 0; k < objectMatrix.length; k++) rotatedMatrix[i][j][k] = temp[i][k]; } return align(rotatedMatrix); } public boolean[][][] rotateY(boolean objectMatrix[][][]) { if(GameStatus.getCurrentObjectY()+GameStatus.getCurrentObject().getZsize()>=GameStatus.getGridSize()){ return objectMatrix; } boolean rotatedMatrix[][][] = createFalsMatrix(objectMatrix.length); for (int i = 0; i < objectMatrix.length; i++) { boolean temp[][] = new boolean[objectMatrix.length][objectMatrix.length]; for (int j = 0; j < objectMatrix.length; j++) for (int k = 0; k < objectMatrix.length; k++) temp[j][k] = objectMatrix[i][j][k]; temp = rot(temp); for (int j = 0; j < objectMatrix.length; j++) for (int k = 0; k < objectMatrix.length; k++) rotatedMatrix[i][j][k] = temp[j][k]; } return align(rotatedMatrix); } public boolean[][][] rotateZ(boolean objectMatrix[][][]) { if(GameStatus.getCurrentObjectY()+GameStatus.getCurrentObject().getXsize()>GameStatus.getGridSize()){ return objectMatrix; } if(GameStatus.getCurrentObjectX()+GameStatus.getCurrentObject().getYsize()>GameStatus.getGridSize()){ return objectMatrix; } boolean rotatedMatrix[][][] = createFalsMatrix(objectMatrix.length); for (int k = 0; k < objectMatrix.length; k++) { boolean temp[][] = new boolean[objectMatrix.length][objectMatrix.length]; for (int i = 0; i < objectMatrix.length; i++) for (int j = 0; j < objectMatrix.length; j++) temp[i][j] = objectMatrix[i][j][k]; temp = rot(temp); for (int i = 0; i < objectMatrix.length; i++) for (int j = 0; j < objectMatrix.length; j++) rotatedMatrix[i][j][k] = temp[i][j]; } return align(rotatedMatrix); }
Algoritam za brisanje redova
public static boolean removeFullRows() { ArrayListrowsToRemove = new ArrayList (); for (int k = gameHeight; k >= 0; k--) { boolean remove = true; for (int i = 0; i < gridSize; i++) for (int j = 0; j < gridSize; j++) if (gameBoolMatrix[i][j][k] == false) remove = false; if (remove) rowsToRemove.add(k); } if (!rowsToRemove.isEmpty()) { removeRows(rowsToRemove); return true; } return false; } private static void removeRows(ArrayList rowsToRemove) { for (Integer x : rowsToRemove) { for (int k = x; k < gameHeight; k++) for (int i = 0; i < gridSize; i++) for (int j = 0; j < gridSize; j++) if (x == gameHeight - 1){ gameBoolMatrix[i][j][k] = false; gameColorMatrix[i][j][k] = ""; }else{ gameBoolMatrix[i][j][k] = gameBoolMatrix[i][j][k + 1]; gameColorMatrix[i][j][k] = gameColorMatrix[i][j][k + 1]; } } }
Projekt
3D tetris i popratna web stranica su izrađeni u sklopu projekta iz kolegija Računalna grafika koji se izvodi na diplomskom studiju Fakulteta organizacije i informatike Sveučilišta u Zagrebu (Doc. dr. sc. Hip Ivan, Damir Horvat, prof.). Naziv teme je bio: "OpenGL ES 1.1 na Androidu - 3D Tetris" a cilj je bio primijeniti naučene vještine kolegija i pritom steći neke nove. Vjerujemo da smo taj zadatak ispunili u potpunosti.
OpenGL ES je API za naprednu cross-platform 2D i 3D grafiku koji se temelji na podskupu OpenGL-a. On dakle predstavlja otvoreni standard koji definira programsko sučelje za korištenje široke palete grafičkih mogućnosti na cijelom nizu uređaja. Otvorenost i višeplatformnost su svakako jedne od glavnih karakteristika koje su doprinijele da OpenGL ES postane opće prihvaćen u struci. Zanimljivost kod OpenGL ES-a jest nastojanje za kompatibilnošću prema prethodnim verzijama što ga je svojevremeno prilično kočilo u razvoju.
izvor: www.khronos.org/opengles/