Bereits bevor die konkrete Spielidee ausgearbeitet wurde, hatten wir die Ambition unsere Spielwelt dreidimensional darzustellen. Ein Blick auf die Liste der erlaubten Java Pakete schränkte unsere Möglichkeiten auf ein einziges Paket ein:
Die Lightweight Java Game Library bietet dabei, wie ihr Name vermuten lässt, lediglich einen Zugang zu den mächtigen Grafik und Audio Schnittstellen openGL bzw. openAL im Java Stil. Es ist kein Framework und enthält keinerlei Werkzeuge oder Tools.
In den ersten Tagen gilt es also sich mit openGL auseinanderzusetzen und abzuschätzen ob unser Vorhaben in der geplanten Zeit umsetzbar ist. Gewappnet mit den Dokumentationen und diversen Tutorials starten wir ein "proof of concept" mit folgenden Zielen:
Die grösste Herausforderung welche es zu meistern gab, war der Umgang mit den Shadern. Ein Shader ist ein Stück Programmcode, welcher direkt auf der Grafikkarte ausgeführt wird und somit um ein vielfaches schneller ist, als die gleiche Berechnung mit der CPU. OpenGL unterscheidet zwischen zwei Shadern:
Ist der Umgang mit Shadern erarbeitet, eröffnen sich beeindruckende Möglichkeiten. Licht, Schatten, Nebel und sogar Partikel Effekte können so ihren Weg in unser Spiel finden.
Eine Kamera ist nichts anderes als eine 4D Transformations-Matrix welche die Position, Rotation und Skalierung der einzelnen Objekte manipuliert. Wenn wir die Kamera 2 Felder nach rechts bewegen wollen, dann transformieren wir die ganze Welt einfach 2 Felder nach links. Das selbe für Neigung, Rotation und Zoom. Im Diagramm auf der rechten Seite bezeichnen wir diese Kamera-Transformations-Matrix mit "View Matrix".
Um die Kamera-Koordinaten schlussendlich zu den Bildschirm-Pixeln umzurechnen, können wir ebenfalls eine Transformations-Matrix benutzen: Die sogenannte "Projection Matrix".
Diese wird generiert aus folgenden Daten:
Um uns die ganzen Matrizen und Vektor Transformationen etwas zu erleichtern benutzen wir die Java Bibliothek "JOML", welche uns Matrix und Vektor Objekte mit fertig implementierten mathematischen Methoden zur Verfügung stellt.
Noch während wir am proof of concept arbeiteten, haben wir uns auf ein Spielkonzept geeinigt. In unserem ersten Tagebuch-Eintrag könnt ihr alles dazu lesen. Wir erweitern das proof of concept um zwei kritische Punkte:
Glücklicherweise sind Würfel die simpelste 3D Form und vergleichsweise einfach im Umgang. Um die Blöcke "buddelbar" zu machen, müssen wir lediglich eine Liste mit den Blöcken unterhalten welche von Renderer berechnet werden sollen.
Unser Block wird im Code etwa wie folgt definiert:
Die Superklasse für alle Objekte enthält:
Die Abstrakte Klasse für alle Blöcke ist von Entity abgeleitet und enthält:
Jeder Block Typ wird von der abstrakten Block Klasse abgeleitet und enthält:
Nach erfolgreicher Implementation unseres proof of concept sind wir zuversichtlich, dass wir das ganze Spiel in dieser Engine entwickeln können. Der nächste essentielle Schritt war, die Welt begeh- und interagierbar zu machen.
In der Welt von openGL gibt es verschiedene Möglichkeiten die Kollisionsabfragen zu gestalten. Da unsere Welt sehr simpel ist und viele der Objekte kubisch sind, entscheiden wir uns für eine eigene, vereinfachte Implementation von Bounding Boxes:
Jedes Objekt (Entity) erhält 6 Koordinaten welche einen Würfel um das Objekt simulieren. Nun können wir Mittels simpler Geometrie feststellen, wenn zwei dieser Würfel sich überschneiden. In unserer konkreter Implementation findet ein Grossteil der Kollisionsabfragen sogar nur in 2 Dimensionen statt. Die Z position der Blöcke und der Spielfigur is weitgehend statisch.
Das buddeln von Blöcken geht im gleichen Schritt wie die Kollision. Immer wenn ein Spieler mit einem Block kollidiert, wird diesem Block "Schaden" zugefügt und in der Block Instanz gespeichert. Übersteigt der Schaden die Härte des Blocks, ist dieser zerstört.
Unsere Physik Engine ist noch sehr simpel. Jedes Objekt, welches nicht mit einem Objekt unter sich kollidiert, fällt mit einer beschleunigenden Geschwindigkeit gleich der Schwerkraft pro Sekunde. Springt ein Spieler in die Höhe, wird von seiner vertikalen Geschwindigkeit jede Sekunde die Schwerkraft-Konstante abgezogen (berechnet pro Frame) bis er wieder mit etwas unter sich kollidiert. Horizontale Bewegungen sind noch ohne Beschleunigung. Blöcke sind grundsätzlich fix und bewegen sich nicht. Wir planen jedoch die schweren Steinblöcke der Schwerkraft zu unterwerfen.
Platzieren von Dynamit per Mauszeiger.
Wir sind mit der Engine bereits an einem Punkt wo wir Netzwerk und Spiellogik implementieren können. Parallel dazu werden wir aber die Engine weiterentwickeln und neue Features hinzufügen. Fest geplant sind:
Die Transformation von einem Bildschirmpixel (z.B. der Mauszeiger) zu einem 3D Vektor in Welt-Koordinaten. Kollision von diesem Vektor mit einem Objekt oder dem Terrain gibt uns die 3D Position des Mauszeigers und lassen den Spieler etwa Objekte platzieren oder auswählen.
Anzeigen wie Chat, Lebensbalken oder Namensschilder. Der wichtigste Teil dieses Features - Generieren und Anzeigen von Text - ist bereits implementiert.
Falls Zeit übrig bleibt, werden wir uns über Partikel Effekte und Animationen Gedanken machen.