Ü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.

Motivation


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.

Warum SDL?

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

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.

Vorgehen


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.


Camera Position

Kamera Position beim Rendern.

Modellierung

Erstes Schiff

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.

Übereinander liegende Faces

übereinander Liegende Faces


Ship4

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 Loop Example

Beispiel eines Edge Loops

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.

Edge Loop Cut

Edge Loop Cut

Edge Loop Cut gif

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.

Edge Loop select gif

Edge Loop select

Modifikatoren

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 Subdivision Surface modifier

Der Oberflächenunterteilungsmodifikator oder auch Subdivision surface modifier ist ein nützliches Tool um Objekte glatter zu machen. Hier ein Mesh ohne den modifier:

ohne subsurf modifier mit subsurf modifier

Hier mit Subdivision surface modifier

Catmull Clark Subdivision Surface

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

[2]

- Verbinde jeden neuen Punkt mit den neuen edge points.
- Definiere die neuen faces die durch die edges geschlossen werden.

[1] Unterschied zwischen Catmull Clark und simple

Hier der Unterschied am Beispiel zwischen dem simple und dem Catmull-Clark subdivision surface modifier


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.

smooth

Smooth Shading am Beispiel. Interpoliert die Punkte.


Stufen des modifiers

Außerdem lassen sich verschiedene Stufen der Unterteilung einstellen.


Der Mirror modifier

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.

Ohne Mit

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.

Optionen beim mirror modifier:

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.

Merging

Clipping: Verhindert dass sich vertices durch die Ebenen des Spiegels bewegen

Clipping

Animation

Roboter Animation

Einer unser Gegner animiert.



Galaxie Animation

Versuch einer animierten Galaxie

Sternen Partikel

Verwendung von Sternenpartikel mit unterschiedlichen Emissionsfarben

Umsetzung der Galaxie

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.



Laser Schuss

Eine Laser Schuss Animation unseres Bossgegners.

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.

SDL Programmierung


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.

Ausblick


Test Text

Quellen


[2] Wikipedia contributors. Catmull–Clark subdivision surface [Internet]. Wikipedia, The Free Encyclopedia; 2017 Jul 2, 12:05 UTC [cited 2017 Aug 23 ]. Available from: https://en.wikipedia.org/w/index.php?title=Catmull%E2%80%93Clark_subdivision_surface&oldid=788611747
[1] Catmull, E.; Clark, J. (1978). "Recursively generated B-spline surfaces on arbitrary topological meshes" (PDF). Computer-Aided Design. 10 (6): 350. https://people.eecs.berkeley.edu/~sequin/CS284/PAPERS/CatmullClark_SDSurf.pdf