Sergiy Krutykov

Die Aufgabe der C-Bibliothek, die Routinen zum Zeichnen, Parser, Mathematik und Logik beinhaltet und auf unterschiedlichen Geräten möglichst gleich aussieht, ist eine Schnittstelle zu schaffen, die von der inneren Implementierung möglichst abstrahiert und die Benutzung von der Bibliotheck möglichst einfach gestaltet.

Die Schnittstelle, die im Rahmen des Projekts erarbeitet wurde, bietet, grob skizziert, folgende Funktionen an:

  • Initialisierungsroutine
  • Verwalten von Tresoren und Geschenken
  • Zeichnen
  • Behandlung von Touch Events

Die Schnittstelle mit ihren meisten Funktionen wird im folgenden wegen der bessern Übersicht vollständig aufgelistet. Im Weiteren wird die Applikation, die diese Bibliothek benutzt, als Client bezeichnet.

Initialisierungsroutine

Zu der Initialisierungsroutine gehören die folgenden Funktionen der Schnittelle, die in der angegebenen Reihenfolge beim Starten des Programms (bis auf geTearDown) ausgeführt werden (das "ge" am Anfang der Funktionsnamen steht etwa für Game Engine):

void geSetup(int screenWidth, int screenHeight, int cameraWidth, int cameraHeight);
 
void geCreateProgramsFromStrings(char *solidObjectShaders, char *quadShaders, char *planeShaders);
 
void geLoadSafeObjectFromStrings(char *body, char *door, char *knob, char *chaines,
				 char *balloon1, char* ballon1_texture,
				 char *balloon2, char* ballon2_texture,
				 char *balloon3, char* ballon3_texture,
				 char *eight_puzzle_texture);
 
void geLoadGiftObjectFromStrings(char *box, char *ribbon, char *rope,
                                 char *balloon, char* ballon_texture);
 
void geTearDown();

geSetup setzt OpenGL auf, wobei alle Framebuffer-Objekte, einige simple Vertexbuffer-Objekte initialisiert und in den Speicher von OpenGL geladen werden, wobei der Client nur die Auflösungen des Bildschirms (Höhe und Breite in Pixel) und der Kamera angeben muss.

geCreateProgramsFromStrings parst und lädt alle Shader in Speicher, die in Form von einem char-Array von dem Client geliefert werden müssen.

geLoadSafeObjectFromStrings parst alle Teile des Tresors und Texturen, initialisiert und lädt anschließend die Texturen in den Speicher und erzeugt Vertexbuffer-Objekte, die zu dem Tresor gehören. Alle Bestandteile müssen als char-Arrays von dem Client geliefert werden.

geLoadGiftObjectFromStrings ist das Analogon zu geLoadSafeObjectFromStrings für die Geschenke.

geTearDown befreit allen Speicher, der durch OpenGL belegt wurde.

Verwalten von Tresoren und Geschenken

Zum Verwalten der Tresoren und Geschenken müssen die folgenden Funktionen benutzt werden:

void geAddSafe(int ID, GLfloat x, GLfloat y, int value);
 
void geRemoveSafe(int ID);
 
void geRemoveAllSafes();
 
void geAddGift(int ID, GLfloat x, GLfloat y, int type);
 
void geRemoveGift(int ID);
 
void geRemoveAllGifts();
 
void geGetSafeMapIconAsBitmap(char *result, GLint safeIconSize, long absoluteTime);
 
void geGetGiftMapIconAsBitmap(char *result, GLint safeIconSize, long absoluteTime);

Die Methoden geAddSafe und geAddGift fügen einen Tresor bzw. ein Geschenk mit der angegebenen ID und mit angegebenen Koordinaten zu einer internen Liste. Die Bezeichnungen der Methoden geRemoveSafe, geRemoveAllSafes, geAddGift, geRemoveGift und geRemoveAllGifts sprechen für sich.

geGetSafeMapIconAsBitmap und geGetGiftMapIconAsBitmap schreiben in das mittels eines Pointers übergebene Array result das Bild für Karten-Annotationen zu der gegebenen Größe. Außerdem erwartet die Funktion die absolute Zeit in Millisekunden, um den Schritt der Animation zu ermitteln.

Zeichnen

Zum unmittelbaren Zeichnen sollen die beiden Funktionen benutzt werden:

void geSetBackground(GLint width, GLint height, char* pixels);
 
void geRender(long timeDelta, GLfloat angleX, GLfloat angleY, GLfloat angleZ,
                              GLfloat centerX, GLfloat centerY, GLfloat centerZ);

geSetBackground setzt das Kamerabild der angegebenen Auflösung, wobei die Pixels des Bildes als ein char-Array von dem Client geliefert werden muss.

geRender zeichnet alles, was auf dem Bildschirm auftauchen muss. Das Argument timeDelta ist die Zeitdifferenz in Millisekunden, die seit dem letzten Aufruf von geRender vergangen ist. Die Argumente angleX angleY angleZ stehen für die Orientierung des Smartphones im Raum (Pitch-, Roll- und Kompass-Winkel) stehen und die Argumente centerX, centerY, centerZ die relative Position des Spielers (und damit der OpenGL-Kamera) im Raum.

Behandlung von Touch Events

Um die Logik der Bibliotheck möglichst zu kapseln, werden die Touch Events des Benutzers von dieser Bibliothek selbst behandelt, wobei die Bibliothek selbst entscheidet, wie sie darauf reagiert:

void geTapScreen(GLfloat x, GLfloat y);
 
void gePanScreen(GLfloat x, GLfloat y, GLfloat tx, GLfloat ty);
 
void gePinchScreen(GLfloat scaleFactor);

geTapScreen reagiert auf das Tippen auf dem Touchscreen durch den Benutzer und erwartet die Bildschirmkoordinaten als Argumente und reagiert darauf je nach dem Zustand des Spiels. Diese Reaktion kann das Nehmen von einem Geschenk oder einem Safe, das Öffnen der Tür eines Tresors oder das Nehmen der Goldbarren sein.

gePanScreen reagiert auf das Ziehen mit einem Finger über den Touchscreen durch den Benutzer und bekommt wie auch bei geTapScreen die Bildschirmkoordinaten als Argumente und außerdem die Richtung und Länge der Verschiebung des Fingers. Wird wirksam nur beim Lösen von dem 8-Puzzle, indem die Plättchen dadurch bewegt werden.

gePinchScreen reagiert auf die "Pinch-to-Zoom" Geste des Benutzers und bekommt als Argument den entsprechenden Skalierungsfaktor. Wird auschließlich für das Zoomen benutzt.

Callbacks

Da die Bibliothek völlig gekapselt ist und der Client nichts über ihren Zustand weiß und kann deshalb nicht entsprechend reagieren, wenn seine Reaktion für den Verlauf der Applikation notwendig ist, zum Beispiel bei den Sachen, die OpenGL selbst nicht kann, wie etwa einen Sound abspielen. Deshalb muss die Bibliotheck in der Lage sein, dem Client eine Zustandsänderung mitzuteilen. Dafür wird die Idee der Callbacks benutzt: Der Client bietet auch eine (sehr kleine) Schnittstelle an, die der Bibliothek zur Verfügung steht. Hier sind ein paar Funktionen aus dieser Schnittelle:

void gcAGiftBoxHasBeenPicked(int screenX, int screenY);
 
void gcChosenSafeCausedSound(int soundID);
 
void gcChosenSafeHasBeenSolved(int safeID);
 
void gcChosenSafeCouldNotBeSolvedInTime(int safeID);

gcAGiftBoxHasBeenPicked wird aufgerufen, sobald beim Tippen auf dem Touchscreen ein Geschenk erwischt wurde. Die Funktion liefert die Bildschirmkoordinaten des Fingers als Argument, damit der Client weiß, an welcher Stelle die Animation zu starten ist.

gcChosenSafeCausedSound teilt dem Client mit, dass ein Tresor ein Geräusch verursacht hat, wobei die ID des Geräusches als Argument auch mitgeteilt wird, damit der Client weiß, welcher Sound abgespielt werden kann.

gcChosenSafeHasBeenSolved signalisiert, dass der Tresor mit der ID safeID gerade durch den Benutzer gelöst wurde.

gcChosenSafeCouldNotBeSolvedInTime teilt dem Client mit, dass der Tresor mit der ID safeID nicht gelöst werden konnte, weil die dafür gegebene Zeit abgelaufen ist.

Portierung

Diese Bibliotheck wurde in erster Linie für zwei Plattformen entwickelt: iPhone und Android. Auf iPhone lässt sie sich einfach als Bestandteil des Projekts kompilieren und direkt benutzen. Auf Android benötigt man das Java Native Interface (JNI), um diese zu benutzen. Die Callbacks müssen in beiden Fällen implementiert werden.


Page last modified on March 22, 2011, at 01:01 AM