Über Uns
Wir Josua Münch, Christian Jürgensen und Benedetto Kotzaneck sind Studenten der Angewandten Informatik (BA) in Heidelberg. Hier möchten wir unser 2D Spritesgame Spacebanana vorstellen.
Wir Josua Münch, Christian Jürgensen und Benedetto Kotzaneck sind Studenten der Angewandten Informatik (BA) in Heidelberg. Hier möchten wir unser 2D Spritesgame Spacebanana vorstellen.
Wir drei waren schon immer Videospiel begeisterte, weshalb wir in unserem Studium lernen wollen, wie man In-game Modelle aber auch Animationen erstellen kann. Die Möglichkeit in einem Praktikum dies zu tun motivierte uns Spacebanana zu entwickeln.
Da wir Vorkenntnisse in C++ hatten entschieden wir uns die SDL Bibliothek für unsere Game-Enginge zu verwenden. Die SDL Bibliothek gewährleistet eine hohe Portabilität und ist lizenzrechtlich problemlos zu verwenden.
Blender eignet sich für unsere Ziele optimal. Es lassen sich Modelle wie auch Animationen, wenn auch am Anfang etwas schwer, entwickeln. Wie auch die SDL Bibliothek ist Blender lizenzrechtlich frei.
Am Anfang setzten wir die Priorität vor allem darin, die wichtigsten Grundlagen zum Arbeiten mit Blender zu lernen. Zunächst war es schwer bei der vollgepackten Benutzeroberfläche Funktionen zu finden, in denen man einen Nutzen beim modellieren sah. Zu Beginn wendeten wir in erster Linie den Border Select, um mehrere Punkte(Vertices) auszuwählen und anschließend die extrude Funktion um verschiedene Flächen gezielt zu verformen, an. Die Scale Funktion machte sich nützlich wenn man Flächen vergrößern oder verkleinern wollte. Es gab trotz allem viele Funktionen von denen man den Nutzen nicht verstand. Überwiegend versuchten wir Raumschiffe zu modellieren, da wir das aktuelle Konzept mit den Weltraum Autos erst später entwickelten. Unser Ziel war es dann die Schiffe in 3D zu modellieren und die Kamera so zu positionieren, dass sie orthogonal zur Seite unserer Modelle Bilder aufnimmt, da wir das Spiel in 2D entwickeln.
Eines unserer ersten Schiffe. Man erkennt, dass die Flächen(Faces) noch ziemlich “kantig” erscheinen. Außerdem sieht man noch viele dreieckige Faces. Das liegt daran, dass wir nicht wussten, dass dreieckige Faces nicht so schön gerendert werden können wie viereckige. Außerdem gab es Probleme beim Rendern wenn verschiedene Faces “übereinander” lagen, oder Edges eines Face durch ein anderes Face gezogen ist.
Hier ist eines unserer Fortgeschrittenen Schiffe. Hierbei achteten wir beim modellieren darauf, nur Vierecke Faces zu verwenden und dass sich keine Faces innerhalb des Objektes befinden oder sie sich kreuzen. Nutzen von Edge Loops Im späteren Verlauf der Modellierung lernten wir die Edge Loops kennen. Wenn 2 Punkte(Vertices) eine Kante(Edge) bilden und mehrere Edges einen Kreis nennt man dies in der Computergrafik einen Edge Loop.
Edge Loops bringen einige Vorteile mit sich. Achtet man beim modellieren darauf, nur Viereckige Flächen zu verwenden stellt man sicher, dass Edge Loops im Modell vorhanden sind. In Blender kann man dann leicht Details hinzufügen aber auch löschen. So kann man zum Beispiel zwischen 2 Edge Loops mehrere neue Edge Loops dazwischen einfügen.
So kann man mithilfe der Edge-Cut und der Extrude Funktion schnell und detailliert neue Flächen oder Details zum Objekt hinzufügen.
Es lassen sich zudem Edge Loops selektieren was den Verlauf der Modellierung beschleunigt.
Blender bietet Mehrere sogenannte Modifikatoren(Modifier) Funktionen die auf das Objekt(Mesh) angewendet werden können. Hier stellen wir die vor die in unserem Projekt zur Anwendung kamen.
Der Oberflächenunterteilungsmodifikator oder auch Subdivision surface modifier ist ein nützliches Tool um Objekte glatter zu machen. Hier ein Mesh ohne den modifier:
Die Catmull Clark Oberflächen sind folgendermaßen rekursiv definiert.
- Alle Punkte des Meshs sind ursprüngliche Punkte.
- Für jedes Face setze einen face point.
(Dieser ist der Durchschnitt aller ursprüngliche Punkte dieses Face)
- Für jede Edge setze einen edge point
(Dieser ist der Durchschnitt der 2 Endpunkte und den 2 benachbarten face points.)
- Für jeden face point setze eine edge für jeden edge eines face, welche den face point und die edge points verbindet.
- Für jeden ursprünglichen Punkt P berechne den Durchschnitt F aller n (gerade erstellten) face points der faces die P berühren. Dann berechne den Durchschnitt aller n edge-midpoints der ursprünglichen edges die P berühren, wobei jeder edge-midpoint der Durchschnitt der 2 Endpunkte dieser edge ist.
Bewege jeden ursprünglichen Punkt zum Punkt
- Verbinde jeden neuen Punkt mit den neuen edge points.
- Definiere die neuen faces die durch die edges geschlossen werden.
Der Simple Subdivision surface modifier unterteilt das Mesh in mehrere Faces während der Catmull-Clark Subdivision surface modifier zusätzlich noch Smooth Shading verwendet.
Der Spiegelmodifikator spielte für uns bei jedem Mesh eine Rolle, da all unsere Meshes Spiegelsymmetrisch sind. Bei diesem modifier lässt Blender wählen an welcher Achse das Objekt gespiegelt werden soll.
Spiegelt man das Mesh entlang der x Achse so bedeutet dies eine Spiegelung der Y-Z Ebene. Dabei ist zu beachten, dass der mirror modifier das Objekt an seinem Ursprung spiegelt.
Merging: Wenn 1 vertice an der selben stelle wie der gespiegelte vertice liegt werden diese zu einem verschmolzen. Wenn man hier merging nicht benutzt würden durch den Subdivision Surface modifier die Kanten an der Symmetrieachse nicht mehr organisch aussehen.
Clipping: Verhindert dass sich vertices durch die Ebenen des Spiegels bewegen
Hier sieht man die verwendeten Objekte die wir benutzten um eine Galaxie zu animieren. Der “Vortex” ist ein rotierendes Force Field was das drehen der Galaxie umsetzt. Die Partikelsysteme “innerStars” “Mainstar” und “outerStars” lassen die Sternen Partikel im Laufe der Zeit erscheinen. Weil wir auch Sterne an zufälligen Orten haben wollten, benutzten wir 3 unterschiedliche Größen von Partikelsystemen. Das Force field “Force” unterstützt den “Vortex” beim halten der Sterne in der Mitte der Galaxie, so haben wir in der Mitte mehrere Sterne als außen. Die 4 Cylinder lassen die Arme der Galaxie entstehen. Es sind Körper die die Sternen Partikel bei Kollision löschen.
Blender ermöglicht es mithilfe von Keyframes Animationen erstellen zu lassen. Mann kann die Position, Rotation und Größe der Objekte an diesen Keyframes setzen. Blender verändert dann im Laufe zwischen der Keyframes entsprechend diese Werte und lässt dann so die Animation entstehen. So haben wir in diesem Beispiel die y-Rotation des Raumschiffes am Anfang der Animation auf 0 gesetzt und bei Frame 30 auf 90°. Dann setzten wir auf Frame 120 die Rotation ebenso auf 90°, sodass es einige Zeit in dieser gedrehten Position bleibt. Schließlich setzten wir die y-Rotation auf Frame 150 wieder auf 0 um es zurück in die ursprüngliche Position zu bringen.
Zur Programmierung des Spiels verwendeten wir SDL, Simple DirectMedia Layer, eine plattformunabhängige Library für C (und C++). Die Library stellt einen einfachen Weg zur Verfügung, via OpenGL und Direct3D auf die Grafik-Hardware zuzugreifen und eignet sich so für die Programmierung von simplen Computerspielen. Bei der Erstellung des Codes bedienten wir uns einiger gängiger Konzepte zur Programmierung von Computerspielen. Einige davon nahmen wir durch Vorkenntnisse mit, in andere haben wir uns eingelesen. Diese Konzepte stellen wir im Folgenden kurz vor und erläutern ihren Nutzen für unseren Code. Zunächst die Game-Loop, ein Konzept, ohne das die Programmierung von Computerspielen kaum möglich wäre. Die Game-Loop bildet den Kern des Programms. Im Gegensatz zur klassischen Programmierung nach dem EVA-Prinzip (Eingabe, Verarbeitung, Ausgabe) wird die Schleife dauerhaft durchlaufen und ändert auch Zustände, wenn keine User-Eingabe stattgefunden hat. Unsere Game-Loop teilt sich dabei in drei Hauptpunkte auf: Zunächst wird die Eingabe des Users verarbeitet, dann wird die Logik des Spiels aktualisiert und zuletzt werden die Grafiken auf den Bildschirm gerendert. Die Schleife arbeitet dabei mit einem Timer, um die Aktualisierungen der Logik auf einem konstanten Level zu halten. Dies ist notwendig, damit das Spiel auf jedem Rechner mit der gleichen Geschwindigkeit läuft, und nicht etwa auf einem schnelleren PC die Raumschiffe eine höhere Geschwindigkeit haben. Außerdem werden die Bilder pro Sekunde beschränkt, um Rechenkapazität zu sparen. Durch die Unabhängigkeit der Logik-Updates und dem Rendern kann es zu einem Problem kommen: Werden die Bilder mit einer höheren Frequenz gerendert, nimmt der User die Bilder trotzdem nur in der Frequenz der Logik-Updates war, weil zwischen zwei Logik-Updates keine Veränderung in der Position von Objekten stattfindet. Um dieses Problem zu lösen finden wir zum Zeitpunkt des Renderns heraus, wo wir uns zwischen zwei Logik-Updates befinden (Was einem Wert zwischen 0 und 1 entspricht) und addieren beim Rendern von Objekten ihre Geschwindigkeit mal diesen Wert auf ihre Position. So entsteht eine weiche Bewegung. Weiterhin nutzen wir das Prinzip von Game-States. Dabei handelt es sich um ein Konzept, bei dem verschiedene Zustände des Spiels in verschiedenen Child-Klassen einer abstrakten Game-State Klasse gespeichert werden. Die abstrakte Klasse hat dabei abstrakte Funktionen für den User-Input, Logik-Updates und Rendern des Spiels. Die abgeleiteten Klassen beschreiben dann konkret, was in diesen Funktionen passieren soll. Durch dieses Konzept ist es möglich, einen Stack aus Game-States zu speichern, der die verschiedenen Zustände des Spiels verwaltet. In der Game-Loop wird dabei immer der oberste Zustand des Stapels als aktiver Zustand behandelt. Neue Zustände müssen also einfach auf den Stapel geschoben gelegt werden oder gelöscht werden, wenn der vorherige Zustand eintreten soll. Auf diese Weise ist es sehr einfach möglich, neue Zustände zu schreiben. Ein einfaches Beispiel ist das Hauptmenü des Spiels und ein Level im Spiel. Startet der User das Spiel, wird der Hauptmenü-Zustand auf den Stack gelegt. Startet er jetzt ein Level, wird der Level-Zustand auf den Stack gelegt und wird so automatisch zum aktiven Zustand. Hat der Spieler das Level jetzt beendet oder will ins Hauptmenü zurückkehren, muss der Level-Zustand nur vom Stapel genommen werden.