Skip to main content

Tile Run – LibGDX Tutorial (Pilot)

Tile Run ist das erste vollwertige Spiel, dass ich mit LibGDX entwickel. Ich möchte mit diesem Tutorial das Vorgeplänkel aus dem Einstiegstutorial zu einem „richtigen“ Spiel umsetzen. Im Vordergrund dieses Tutorials steht die Verwendung von TiledMaps (Kachel-Karten). Dieses Prinzip ist die konzeptionelle Basis dieses Spiels, das ich in einer Reihe von Tutorials um immer neue Funktionen erweitern werden. Das aktuelle Spiel, aus dem diese Tutorial-Reihe hervorgeht, kannst du hier begutachten und ausprobieren. Am Ende dieses Tutorials hast du einen Charakter der sich hüpfend und rutschend durch eine selbst erstellte Karte bewegt. Außerdem zeige ich dir, wie du ohne viel Mühe per Drag’nDrop neue Karten für das Spiel erstellen kannst.

Vorraussetzung: Dieses Tutorial setzt eine einsatzbereite Installation einer IDE mit LibGDX sowie rudimentäres Verständnis der Arbeitsweise von LibGDX oder ähnlichen Frameworks vorraus. Installationshinweise findest du hier (engl). Grundlagen und erste Schritte mit LibGDX findest du in LibGDX – Einstieg ins Spieleframework.

Übersicht

Die Kernelemente vieler Spiele dieses Types sind:

Steuerung, Grafik, Karten/Level.

Die Steuerung halten wir zunächst sehr einfach es gibt eine Taste (bzw. Touchbereich) zum Springen und eine zum Ducken. Es wird noch schwierig genug diese beiden Fähigkeiten bei hohen Geschwindigkeiten richtig zu koordinieren. Aber auch eine einfache Steuerung will Programmiert werden.

Die Grafik möchte ich an diesem Punkt etwas zurück stellen, auch wenn es ein wichtiger Punkt ist um ein Spiel, dass in seiner Mechanik wenig neues mitbringt, interessant zu machen. Aber in diesem Tutorial geht es ums Programmieren, daher greife ich auf das freie Platformer-Asset-Pack von kenney.nl zurück (als Assets werden in diesem Tutorial alle Medien- und Spieldateien wie Level, Grafiken, Sound usw. bezeichnet). Das BasePack, das in dieser Sammlung enthalten ist, würde im Prinzip auch reichen, aber so liegen direkt noch einige weitere Variationen zum herumspielen dabei. Die Grafiken sind zu einem großen Teil Kacheln, die wir im nächsten Kapitel zu einer Map zusammenbauen werden.

Das Ziel ist für jedes Level das gleiche, den Ausgang erreichen, ohne mit Hindernissen zu Kollidieren. Eine Vielzahl verschiedener Level ist für diesem Spieltyp also wichtig, deshalb greifen wir auf die Kacheltechnik zurück, die uns erlaubt mit einem Editor ganz einfach neue Level zu erstellen (ganz ohne Programmiercode).

Um die Level zu wechseln etc. brauchen wir auch noch ein Menü und um solche Bereiche vom eigentlich Spiel zu trennen werden wir Screens verwwenden. Was das bedeutet und wie das Funktioniert kommt in einem der Nächsten Kapitel.

Projekt erstellen

Wie bereits im Grundlagen Tutorial erstellen wir unser neues Projekt in dem wir die libgdx-setup.jar Datei ausführen. Das Projekt heißt in meinem Fall „Tile Run“ aber ihr könnte natürlich auch einen eigenen Namen wählen. Wenn du die java-Class umbenennst, kannst du die Code Beispiele teilweise nicht mehr einfach kopieren. Die Details können dem Screenshot entnommen werden. Auf ein HTML-Build verzichte ich ebenso auf das iOS build, da ich unter Linux entwickle. Box2d habe ich erstmal aktiviert, da ich darauf eventuell in den Folge-Tutorials zurückgreife. Der Pfad zum Android SDK hängt natürlich davon ab, wohin es installiert wurde.

Nachdem dem Klick auf den Generate Button dauert es ein paar Sekunden, bis wir die Erfolgsnachricht mit weiteren Anweisungen lesen können. Denen leisten wir folge: In meinem Fall öffne ich Intellij IDEA und wähle direkt aus dem Popup -> „Open“ (wenn noch kein Projekt besteht) oder unter dem Menüpunkt „File“ ->  „Open Project“. Zum öffnen suchen wir in dem Verzeichnis in dem wir unser Projekt erstellt haben die Datei build.gradle (open as project). Es erscheint eventuell noch ein Dialog zum Import über Gradle. Darin steht bei mir alles auf den voreingestellten Werten (vgl. Screenshot 2). Sollte unter Gradle JVM keine JavaVersion zur Auswahl stehen, kann/muss diese in den Einstellungen nachträglich gesetzt werden. Wir bestätigen den Import aller Module warten bis alle Gradle Imports durchgelaufen sind.

Anschließend im Reiter Gradle einmal die Einstellungen neu zu laden, schadet nicht und löst ggf. auftretende Fehler.

Nun haben wir schon ein Basisprojekt mit der Dateistruktur und wichtigen Dateien, die wir aus dem Einstiegs-Tutorial kennen. Wir könnten schon ein Build starten und die .apk Datei auf unserem Android Gerät würde uns eine App mit unserem Projektnamen installieren, die jedoch nichts weiter als das LibGDX-Logo bzw. einen Smilie auf rotem Grund anzeigen würde. Da wir während der Entwicklungsphase die App sehr oft starten werden, sollte entweder ein Android Emulator, oder ein entsprechend konfiguriertes per USB verbundenes Smartphone laufen, damit unser Spiel direkt gestartet werden kann ohne es jedes Mal erst auf dem Gerät zu installieren.

Außerdem lohnt es sich (wenn parallel für Desktop entwickelt wird sowieso) einen neuen „Run Befehl“ anzulegen. Für Android ist ein solcher „Schnellstart-Befehl“ bereits angelegt (vgl. Screenshot 3). Dazu wählen wir im Menü unter dem Punkt  Run -> Edit Configurations.... Erstellen eine neue (grünes Plus oben links) Application mit dem Namen Desktop. Alle Einträge sind in Screenshot 3 zu sehen. Wählt man zuerst classpath -> Android (weiter unten) aus, wird einem MainClass beim durchsuchen direkt vorgeschlagen. Und als Workdir wählen wir den Android Assets Ordner aus, damit wir unsere Assets (Bilder/Sound/Level etc) nicht an mehreren Orten hinterlegen müssen. Wir könnten natürlich auch einen anderen Ordner verwenden und unser Android-Build auf jenen verweisen, aber da wir sowieso in erster Linie für Android entwickeln, ist der Android Ordner eine gute Wahl für unsere Assets denke ich. Apply -> OK

Oben Rechts in unserer IDE finden wir jetzt die Option „Desktop“ als Ziel um die App auszuführen, klicken wir auf Play öffnet sich ein Fenster mit Smilie auf rotem Grund (wenn die gleiche Version der libgdx-setup.jar verwendet wurde). Jetzt sind wir bereit den Beispielcode zu löschen und unsere eigene App zu entwickeln.

 

 

Grafiken vorbereiten

Bevor wir voll durchstarten, legen wir uns erst einmal unsere Grafiken im Android Assets Ordner zurecht. Dies ist in diesem Fall sehr leicht getan (Danke Kenney Group):

Um Zeit zu sparen kannst du auch dieses Archiv direkt in den android/assets/ ordner entpacken und zum nächsten Kapitel springen. Oder du sammelst dir die Bildchen selbst zusammen, falls du noch was austauschen oder ergänzen möchtest:

Wir kopieren die base_pack/tiles/tiles_spritesheet.png in unseren android/assets/img Ordner und kürzen den Namen auf tiles.png. Der Ordner img muss wahrscheinlich zuerst angelegt werden. Du kannst natürlich auch andere Assets downloaden oder deine eigenen erstellen. In dieser Datei sind eine ganze Reihe Kacheln gesammelt, aus denen wir später die Map zusammen setzen. Jetzt brauchen wir noch ein Hintergrund und einen Charakter. Als Charakter suchen wir uns einen der drei Aliens aus dem Ordner Base pack/Player aus und Kopieren die Sprite- und die Text-Datei (zB. p3_spritesheet.png und .txt) diese nennen wir player.png und player.txt. Die txt ist nur für uns als Notiz, da steht drin, wo welches Bild / Frame der Animation steht.

Als Hintergrund habe ich die Datei Mushroom Expensions/Backgounds/bg_grasslands.png ausgewählt und in android/assets/img/background.png gespeichert. Das Badlogic oder LibGDX logo im Asset Ordner kann übrigens gelöscht werden. Es ist erstaunlich, aber wir können mit diesen 3 Bildern von insgesamt unter 200kB tatsächlich ein 2D Spiel mit annehmbarer Grafik zaubern!

 

Grafiken anzeigen

Als nächstes wollen wir unseren Hintergrund und eins der Playerbilder darauf bewegen. Dazu ersetzen wir den Beispiel Code in unserer GameClass. Diese Klasse wird durch die Launcherscripte für die jeweiligen Plattformen gestartet und befindet sich in Core -> src -> de.sarbot.tilerun -> TileRunGame. Namen können je nach Setup abweichen. Im Bereich Core befindet sich alles, was nicht plattformspezifisch ist, also im wesentlichen der Code für unser Spiel. Wir bearbeiten die Datei TileRunGame.java.

Der Beispielcode (falls im Beispielcode irgendetwas unklar ist, sollte noch einmal das Einstiegstutorial überflogen werden) zeigt ja bereits die Darstellung eines Bildes bzw. einer Texture. Da wir unseren Workspace auf den Assets-Ordner gesetzt haben, reicht die Pfadangabe relativ zu diesem Ordner. Der Code kann genauso unser Hintergrundbild in seiner originalen Größe darstellen, dabei muss nur den Pfad zur Texture in "img/background.png" geändert werden. Nun liegen die anderen Bilder leider (eigentlich eher „zum Glück“) nicht alle als einzelne Dateien vor. Also laden wir die „gesammelten Werke“ als ganzes und klamüsern sie dann in unserer create() Methode auseinander. Wir benennen unsere Variablen ab jetzt etwas eindeutiger:

TileRunGame
package de.sarbot.tilerun;

import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;

public class TileRunGame extends ApplicationAdapter {
	SpriteBatch batch;
	Texture background;
	Texture playerTexture;
	TextureRegion[][] playerRegions;
	
	@Override
	public void create () {
		batch = new SpriteBatch();
		background = new Texture("img/background.png");
		playerTexture = new Texture("img/player.png");
		playerRegions = new TextureRegion(playerTexture).split(72,96);
	}

	@Override
	public void render () {
		Gdx.gl.glClearColor(1, 0, 0, 1);
		Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
		batch.begin();
		batch.draw(background, 0, 0);
		batch.draw(playerRegions[1][0],0,10);
		batch.end();
	}
	
	@Override
	public void dispose () {
		batch.dispose();
		background.dispose();
	}
}

 

So könnte unser Code inzwischen aussehen, die TextureRegion Klasse muss an dieser Stelle noch nicht unbedingt verstanden werden, da wir sie erst für die Animationen wirklich brauchen. Außerdem habe ich etwas geschlampt, da ich die Texture in gleichmäßige Bereiche von 72x98px geteilt habe, obwohl nicht alle Tiles exakt diese Maße haben, wenn man einen genaueren Blick in die player.txt wirft. Aber für ein Bild soll das erstmal genügen. Auf dem Screenshot links sieht man das resultierende Bild.

Um unseren Charakter auf der x-Achse (links/rechts) zu bewegen, können wir den x-Wert, den wir jetzt auf 0 gesetzt haben durch eine Variable namens positionX ersetzen, dann brauchen wir nurnoch den Wert der Variable verändern, um damit die Position des Spielers zu verändern.

batch.draw(playerRegions[1][0],positionX,10); Wir deklarieren die Variable mit int positionX; und setzen sie am Anfang (in der create()-Methode) auf 0: positionX = 0;

Die Methode render() wird im Vergleich zur create() für permanent aufgerufen jedes Frame wird in dieser Methode einzeln berechnet. Addieren wir also einen festen Wert auf die positionX, dann hängt die Bewegung von der Geschwindigkeit mit der das Spiel läuft ab. Um dies zu umgehen können wir den Wert mit der vergangenen Zeit seit dem letzten Frame multiplizieren um so eine feste mittlere Geschwindigkeit (unabhängig von den fps) zu erzeugen. Innerhalb der render()-Methode können wir dies mit dem Befehl positionX += Gdx.graphics.getDeltaTime() * 200; Die Zahl, die wir multiplizieren gibt in diesem Zusammenhang an, wie viele Pixel unser Charakter sich pro Sekunde bewegt. Je höher desto schneller also. Ein Ausführen unseres Codes lässt den Charakter aus dem Bildauschnitt laufen, in den nächsten Kapiteln wird es unter anderem darum gehen, dem Spieler mit der Camera zu folgen.

Hier ist der gesamte Code unserer TimeRunGame.java im Gist.

Weiter geht es mit dem nächsten Kapitel TiledMaps

 

 

 

TODO: was passiert bei resize -> verweis auf viewport camera und resolutions

Quellen:

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.