intro Java Spiel 7_vasabii_Fotolia_70882828

Java Spiel Programmieren Tutorial – Teil 7: Die Spiellogik unseres Java Spiels implementieren


In der siebten Lektion unseres Java Spiel Programmieren Tutorials werden wir die Spiellogik für unser Java Spiel implementieren.

Dazu werden wir den Quellcode der Klasse GamePanel an mehreren Stellen überarbeiten und erweitern. Die Klasse GamePanel übernimmt in unserem Java Spiel die grafische Darstellung der Spielfläche mit den Spielobjekten und stellt außerdem die Spiellogik bereit.

Da sich die vorzunehmenden Änderungen über den gesamten Quellcode der GamePanel-Klasse verteilen, werden wir die sie schrittweise durchführen. Dieses Vorgehen ermöglicht es uns zudem, die jeweils vorgenommene Änderung direkt und ausführlich zu besprechen.

Nachdem wir die Spiellogik implementiert haben, werden wir unser Java Spiel in der NetBeans IDE ausführen, um es ausgiebig zu testen.

Dann wird unser Panzer-Spiel auch einen fortgeschrittenen Entwicklungsstand erreicht haben, in welchem sich sinnvoll auf Panzerjagd begeben werden kann. Wir kommen damit unserem Ziel, ein eigenes Spiel in Java zu programmieren, einen großen Schritt näher.

Nun wünschen wir euch viel Spaß beim siebten Teil unseres Java Spiel Programmieren Tutorials. Los geht’s!

1. Implementieren der Spiellogik in der GamePanel-Klasse

Wir werden nun die Spiellogik unseres Java Spiels implementieren. Dazu öffnen wir die Klasse GamePanel im Editor der NetBeans IDE.

Die GamePanel-Klasse ist das Zeichenbrett für die Spielobjekte unseres Java Spiels. Wir haben sie bereits in Teil 3 dieses Java Tutorials angelegt.

Sie ist außerdem für die Spiellogik unseres Java Spiels verantwortlich. Von ihr werden alle Spielobjekte verwaltet und sie berechnet zyklisch den aktuellen Spielzustand des Java Spiels.

Um die Spiellogik in derGamePanel-Klasse zu implementieren, werden wir einige Änderungen am Quellcode der Klasse vornehmen. Dazu werden wir die folgenden Arbeitsschritte ausführen:

  1. Einfügen der benötigten Import-Anweisungen – Damit wir mit Hilfe einer generischen Liste die Missile-Objekte verwalten können, müssen wir vorher die Klasse LinkedList und die beiden Interfaces List und Iterator aus der Paket java.util importieren.
  2. Deklarieren drei weiterer Membervariablen – Momentan verwenden wir Test-Variablen für die Geschosse, den Spielerpanzer und den gegnerischen Panzer. Diese Test-Variablen werden wir entfernen und stattdessen drei neue Membervariablen einfügen, die auf Instanzen der entsprechenden Spielobjekte referenzieren.
  3. Überarbeiten der initGame() Methode – In der initGame() Methode werden wir einige kleine Änderungen vor. Hauptsächlich das Anpassen des neuen Namen der Membervariablen des Spielerpanzers.
  4. Überarbeiten der createGameObjects() und initPlayersTank() Methoden – Auch diese beiden Methoden müssen überarbeitet werden. Alter Test-Quellcode muss entfernt und die neuen Membervariablen müssen initialisiert werden.
  5. Überarbeiten der doOnTick() Methode – Hier implementieren wir die Spiellogik. Was passiert wenn ein Panzer von einem Geschoss getroffen wird? Wann endet das Spiel? Wie viele Treffer kann ein Panzer einstecken?
  6. Überarbeiten der paintComponent() Methode – Hier sorgen wir dafür, dass die in den Membervariablen gespeicherten Spielobjekte auch auf der Spielfläche dargestellt werden. Zusätzlich muss alter Test-Quellcode entfernt werden.

Wie an der oberen Auflistung zu sehen ist, liegt einiges an Arbeit vor uns. Damit nichts vergessen wird, gehen wir dabei schrittweise vor.

Die folgende Änderung ist optional und muss nur von einigen Lesern befolgt werden:


Falls ihr das Java Spiel Programmieren Tutorial vor dem 10.01.2016 begonnen habt, müsst ihr noch eine kleine Änderung an der Klasse Tank vornehmen.

Geht dazu am besten direkt zu Teil 5 dieses Tutorials und ersetzt den Quellcode eurer paintTankStatusBars() Methode der Tank-Klasse mit dem in Teil 5 aufgeführten Quellcode der paintTankStatusBars() Methode. Erst dann werden die Energie-Balken über den Panzern korrekt dargestellt.

Solltet ihr Teil 5 des Tutorials nach dem 10.01.2016 ausgeführt haben, müsst ihr diese Änderungen nicht durchführen. Da ab diesem Zeitpunkt der Fehler im Tutorial bereits behoben wurde.


Nun beginnen wir mit dem ersten Arbeitsschritt, dem Einfügen der notwendigen Import-Anweisungen.

1.1 Einfügen der benötigten Import-Anweisungen

Die Klassendatei GamePanel.java ist bereits in unserem Java Projekt vorhanden.

Wir öffnen nun die Klassendatei GamePanel.java im Editorfenster von NetBeans und fügen die gelb markierten Zeilen in den bereits vorhandenen Quellcode im Bereich der Import-Anweisungen ein:


Wir verwenden in unserem Java Spiel eine generische Liste, um darin die Missile-Objekte komfortabel verwalten zu können. Dazu benötigen wir die beiden Interfaces List und Iterator, sowie die Klasse LinkedList.

Eine Liste ist eine Sammlung, die sich dynamisch beliebig in ihrer Größe ändern kann. Dies ist bei Reihungen (arrays) nicht der Fall, deren Größe muss beim Erzeugen vorgegeben werden und ist somit statisch.

In Java sind Standardlisten in dem Paket java.util als generische Klassen und Schnittstellen realisiert. Generische Klassen und Schnittstellen bieten die Möglichkeit, einen Datentyp für Objekte, die innerhalb der Klasse bzw. Schnittstelle verwendet werden, offen zu halten.

Bei generischen Listen ist also der Datentyp der Listenelemente frei wählbar. Daher die Bezeichnung generische Liste. Welcher Art (Datentyp) die Elemente der Liste sind, muss bei der Deklaration der Listen-Variable vorgegeben werden. Wie das erfolgt werden wir im nächsten Arbeitsschritt beschreiben.

Die folgenden Links führen zu den entsprechenden Referenzen in der Java API:

1.2 Deklarieren drei weiterer Membervariablen

Jetzt wird es etwas kompliziert, da sich in dem vorhandenen Quellcode der Klassendatei GamePanel.java noch der Test-Quellcode befindet.

Diesen müsst ihr entfernen und anschließend die gelb markierten Zeilen einfügen, so dass euer Quellcode exakt dem unten aufgeführten Code entspricht.

Falls ihr dabei durcheinander kommt, kein Grund zur Sorge. Am Ende dieser Arbeitsschritte führen wir noch einmal den kompletten Quellcode der GamePanel-Klasse auf. Diesen könnt ihr dann in Ruhe mit eurem vergleichen und dadurch mögliche Missverständnisse ausschließen.

Nehmt nun die notwendigen Änderungen an eurem Quellcode vor, so dass dieser exakt dem unten aufgeführten Code entspricht:


Im oberen Quellcode haben wir die drei neuen Membervariablen playersTank, enemyTank und missiles angelegt. Die ersten beiden Variablen repräsentieren den Spielerpanzer und den gegnerischen Panzer. Die dritte Variable ist eine generische Liste, welche Listenelemente vom Datentyp Missile in sich aufnimmt.

Der Datentyp der Listenelemente wird in spitzen Klammern angegeben (). Bei Arrays ist das etwas anders. Dort würde man Missile[] schreiben.

Da List nur ein Interface (Schnittstelle) ist, können wir sie nicht instanziieren. Dies ist auch gar nicht gewollt, denn dafür sind spezielle Klassen in dem Package java.util vorgesehen, welche die Schnittstelle List komplett implementieren.

Wir haben dabei die Auswahl zwischen den Klassen Vector, ArrayList und LinkedList. Welche wir davon nehmen, müssen wir an dieser Stelle, der Membervariablen-Deklaration, noch nicht entscheiden. Und genau das ist der große Vorteil von Interfaces. Wir können später festlegen welche Listenklasse unsere Liste konkretisieren soll, da alle drei mit dem Interface List kompatibel sind.

Kommen wir nun zum dritten Arbeitsschritt.

1.3 Überarbeiten der initGame() Methode

Als Nächstes werden wir den Quellcode der initGame() Methode überarbeiten.

Dabei muss hauptsächlich der neue Name der Membervariable (playersTank) anstelle der Test-Variable eingesetzt werden. Zudem muss die Anweisung für das Abfeuern eines Geschosses, in Zeile 40, angepasst werden, da die Missile-Objekte jetzt mit Hilfe einer generischen Liste verwaltet werden.

In dem unten aufgeführten Quellcode ist die Methode initGame() gelb markiert. Der bisherige Quellcode dieser Methode muss nun durch den neuen, gelb markierten komplett ersetzt werden:


In Zeile 40 fügen wir der generischen Liste missiles ein neues Listenelement vom Typ Missile hinzu. Dazu lassen wir den Spielerpanzer, durch Aufrufen der Methode shoot(), schießen. Dabei erhalten wir als Rückgabewert das Missile-Objekt, welches das gerade abgefeuerte Geschoss repräsentiert.

Mit Aufrufen der Methode add() auf der Listenvariable missiles wird das Missile-Objekt schließlich der generischen Liste angefügt.

Das war auch schon alles. Kommen wir nun zu dem vierten Arbeitsschritt.

1.4 Überarbeiten der createGameObjects() und initPlayersTank() Methoden

In diesem Schritt instanziieren wir die drei neu eingefügten Membervariablen playersTank, enemyTank und missiles. Außerdem initialisieren wir den Spielerpanzer.

In dem unten aufgeführten Quellcode sind die beiden zu überarbeitenden Methoden createGameObjects() und initPlayersTank() bereits gelb markiert und enthalten den neuen Quelltext. Der bisherige Quellcode dieser Methoden muss nun durch den neuen, gelb markierten komplett ersetzt werden:


In der Methode createGameObjects() ist die Zeile 11 besonders interessant. An dieser Stelle erzeugen wir ein neues Listenobjekt mit missiles = new LinkedList<>().

Als Listenklasse haben wir uns für die LinkedList-Klasse entschieden, da sie beim Löschen von Listenelementen leistungsfähiger als die ArrayList-Klasse ist.

Auf diese Weise haben wir uns erst beim Erstellen des Listenobjekts entschieden, welche konkrete Listenklasse verwendet werden soll. Wir könnten dadurch später sehr einfach auf eine andere Listenklasse wechseln, bspw. Vector oder ArrayList.

Den Datentyp der Listenelemente müssen wir an dieser Stelle nicht explizit angeben, sondern können den Diamond Operator <> verwenden. Dies ist auch absolut logisch, da wir den zu verwendenden Datentyp der Listenelemente bereits bei der Deklaration der missiles Variable vorgegeben haben.

Somit haben wir die Vorbereitung abgeschlossen und können nun im nächsten Arbeitsschritt die Implementierung der Spiellogik unseres Java Spiels beginnen.

1.5 Überarbeiten der doOnTick() Methode

In der doOnTick() Methode implementieren wir die Spiellogik unseres Java Spiels.

Die Spiellogik haben wir zum besseren Verständnis extra sehr einfach gehalten. Sie kann später von euch natürlich erweitert werden.

Die doOnTick() Methode ist für das Zeichnen der Spielfläche, durch Aufruf der repaint() Methode, das Ausführen der Spielzüge und die Spiellogik verantwortlich. Sie wird alle 20 ms automatisch vom Timer unseres Java Spiels aufgerufen.

Bisher enthält die doOnTick() Methode hauptsächlich Test-Quellcode. Diesen werden wir nun durch die Spiellogik unseres Spiels ersetzen.

Dazu ersetzen wir die komplette doOnTick() Methode des bisherigen Quellcodes mit der im unteren Quellcode aufgeführten Methode:


Die Methode doOnTick() enthält jetzt die Spiellogik unseres Java Spiels. Die Spiellogik regelt die folgenden Spielsituationen:

  • Einem Geschoss geht der Treibstoff aus – Ein Geschoss wird von der Spielfläche entfernt, wenn die maximale Flugdistanz zurückgelegt wurde.
  • Der Spielerpanzer wird von einem Geschoss getroffen – Bei einem eingesteckten Treffer wird die Energie des Spielerpanzers um den Wert 1 reduziert. Falls der Wert zum Zeitpunkt des Treffers 1 betrug, wird die Panzerenergie auf 0 gesetzt und das Spiel beendet.
  • Der gegnerische Panzer wird von einem Geschoss getroffen – Wird der gegnerische Panzer getroffen, verringern wir dessen Energie um den Wert 1. Falls der Wert zum Zeitpunkt des Treffers 1 betrug, wird ein neuer Panzer erstellt, welcher den bisherigen gegnerischen Panzer ersetzt. Die Startposition und Maße des neuen gegnerischen Panzers werden zufällig festgelegt. Außerdem erhöhen wir den Wert der Variable tanksDestroyedCounter um 1.

In dem oberen Quelltext sind einige Zeilen besonders interessant. Diese möchten wir nun kurz besprechen.

Mit der for-Schleife in den Zeilen 7 bis 38 durchlaufen wir alle in der generischen Liste gespeicherten Missile-Objekte. Wir nutzen dabei das Interface Iterator, mit dessen Hilfe wir sehr komfortabel die Liste durchlaufen können.

Wir können uns mit der Anweisung itMissiles.next() das nächste Missile-Objekt holen und mit itMissiles.remove() das aktuell angefragte Element aus der Liste entfernen lassen. Ob noch weitere Elemente in der Liste vorhanden sind, können wir mit itMissiles.hasNext() erfragen.

Mit den if-Abfragen in den Zeilen 12 bis 21 und Zeilen 22 bis 37 prüfen wir, ob eine Kollision zwischen Panzerobjekt und Geschoss aufgetreten ist. Dabei nutzen wir die verbesserte Kollisionsabfrage der Klasse Tank.

Außerdem stellen wir sicher, dass das Geschoss beim Treffer nicht die maximale Flugdistanz erreicht hat. Dieser Sonderfall ist sehr tückisch, da er zum Absturz unseres Java Spiels führen würde. Es würde eine NullPointerException auftreten, da wir mit unserem Quellcode versuchen würden ein und dasselbe Geschoss zweimal aus der Liste zu löschen. Dies wird durch den zweiten Teil der if-Abfrage verhindert.

Mit den vorgenommenen Änderungen sind wir nun weit voran geschritten. Es fehlt nur noch ein letzter Arbeitsschritt.

1.6 Überarbeiten der paintComponent() Methode

Als letzten Arbeitsschritt werden wir die paintComponent() Methode überarbeiten.

An der Methode müssen zwar nur geringe Änderungen vorgenommen werden, dennoch ist es am besten den Quelltext der gesamten Methode paintComponent() mit dem unteren Quellcode zu ersetzen:


In der paintComponent() Methode zeichnen wir die Spielobjekte. Besonders interessant ist die for-Schleife in den Zeilen 19 bis 21, mit deren Hilfe wir die Missile-Objekte auf der Spielfläche darstellen lassen.

Für das Darstellen der Geschosse wird eine erweiterte for-Schleife verwendet, womit Standardlisten sehr komfortabel durchlaufen werden können.

Mit den vorgenommenen Änderungen an der paintComponent() Methode haben wir alle sechs Arbeitsschritte ausgeführt und die Spiellogik für unsere Java Spiel vollständig implementiert.

Achtung! Nicht vergessen die oben besprochene optionale Änderung an der Tank.java Klassendatei vorzunehmen. Falls der Energiebalken eurer Panzer im Spiel nicht abnimmt, muss eine kleine Änderung an der Tank-Klasse vorgenommen werden. Diese Änderung ist optional und betrifft nur die wenigsten Leser dieses Tutorials. Siehe Infokasten ganz oben.

1.7 Der komplette Quellcode der GamePanel-Klasse

Nun haben wir alle Änderungen an der GamePanel-Klasse vorgenommen. In dem unten angegebenen Quellcode ist die gesamte GamePanel-Klasse zur Kontrolle für euch aufgeführt.

Der vollständiger Quelltext der Klasse GamePanel mit markierten Änderungen:


In dem oberen Quellcode wurden die überarbeiteten Zeilen gelb markiert. Insgesamt haben wir folgende Änderungen an der GamePanel-Klasse vorgenommen:

  • Die benötigten Import-Anweisungen wurden eingefügt.
  • Drei weiterer Membervariablen wurden deklariert.
  • Die Methoden initGame(), createGameObjects() und initPlayersTank() wurden überarbeitet.
  • Die Spiellogik wurde in der doOnTick() Methode implementiert.
  • Die paintComponent() Methode wurde überarbeitet.

In NetBeans sollte die Klasse GamePanel nun wie folgt aussehen:

java spiel gamepanel final changes

Der Quellcode der Klasse GamePanel mit allen vorgenommenen Änderungen

In der oberen Abbildung haben wir die in den Quellcode der GamePanel-Klasse die vorgenommenen Änderungen jeweils mit einem blauen Rahmen markiert:

  • A: Einfügen der benötigten Import-Anweisungen – Import der benötigten Klassen und Interfaces.
  • B: Deklarieren drei weiterer Membervariablen – Anlegen neuer Membervariablen für die Spielobjekte.
  • C: Überarbeiten der initGame() Methode – Membervariable des Spielerpanzers eingefügt.
  • D: Überarbeiten der createGameObjects() und initPlayersTank() Methoden – Initialisieren der neuen Membervariablen und Startzustand des Spielerpanzers definieren.
  • E: Überarbeiten der doOnTick() Methode – Hier wurde die Spiellogik des Java Spiels implementiert.
  • F: Überarbeiten der paintComponent() Methode – Darstellen der Spielobjekte auf der Spielfläche.

1.8 Die Klassendateien unseres Java Projekts zum Download

Unser Java Spiel ist nun schon etwas komplexer und besteht aus insgesamt 8 Klassen. Damit es nicht zu Missverständnissen kommt, haben wir unten alle Klassendateien zum Download bereitgestellt.

Ihr solltet noch einmal in Ruhe den kompletten Quellcode der aufgelisteten Klassendateien unseres Java Spiels anschauen und mit eurem vergleichen:

PanzerHQ.java
GameWindow.java
GamePanel.java
Coordinate.java
GameObject.java
Missile.java
Tank.java
EnemyTank.java

Als Nächstes werden wir unser Java Spiel starten und die neu implementierte Spiellogik testen.

2. Testen der Spiellogik unseres Java Spiels

Wir führen nun unser Java Projekt mit einem Klick auf das Run Project-Symbol aus.

java spiel entwickeln projekt starten

Starten unseres Java Projekts über den Run Projekt Button

Im Hintergrund wird jetzt unser Java Spiel von der NetBeans IDE erstellt und anschließend ausgeführt.

Diesmal begegnen wir mehreren gegnerischen Panzern. Wir können den Spielerpanzer mit den Pfeiltasten steuern. Den Panzerturm können wir mit den Tasten W und E nach links bzw. rechts drehen. Geschosse werden mit der Leertaste abgefeuert.

Jeder Panzer kann mehrere Treffer einstecken, bevor er als zerstört gilt. Die verbleibende Energie wird über dem Panzer dargestellt. Pro abgeschossenen gegnerischen Panzer wird der Zähler Tanks destroyed um 1 erhöht.

java spiel spiellogik

Das Spielfenster unseres Java Spiels wir können nun gegen mehrere gegnerische Panzer kämpfen

In der oberen Abbildung ist der Spielerpanzer am linken Spielfeldrand zu sehen. Dieser hat gerade ein Geschoss abgefeuert und damit den Nachladevorgang gestartet. Dies erkennt man an dem schmalen grauen Balken über der Energieanzeige des Panzers.

Auch der gegnerische Panzer rechts hat ein Geschoss abgefeuert. Beide Panzer wurden schon mehrfach getroffen. Dies ist an dem nicht mehr vollständigen Energiebalken über den Panzern zu erkennen. Sobald der kritische Zustand erreicht wird, färbt sich der Energiebalken Rot.

2.1 Video – Funktionspräsentation unseres Java Spiels

In dem unteren Video ist das Spielfenster unseres selbst programmierten Java Spiels zu sehen. Wir sind in der Lage den Spielerpanzer selbst über die Spielfläche zu steuern. Auch die verbesserte Kollisionsabfrage ist jetzt implementiert, mit deren Hilfe wir Panzerabschüsse exakter erkennen können.

Neu hinzugekommen ist die Spiellogik, durch die wir nun gegen mehrere Panzer kämpfen können. Auch die Abschüsse werden mitgezählt und dem Spieler unten links angezeigt.

Die neuen Funktionen unseres Java Spiels haben wir im unteren Video vorgeführt:

In dem oberen Video haben wir ein kleine Partie PanzerHQ gespielt. Es ist uns gelungen drei gegnerische Panzer zu eliminieren, bevor der Spielerpanzer den finalen Treffer einstecken musste und zerstört wurde.

In dem Video sind alle Funktionen unseres selbst programmierten Java Spiels zu sehen. Es ist natürlich noch kein vollständiges Spiel, aber dennoch schon sinnvoll spielbar.

Zusammenfassung

In der siebten Lektion unseres Java Spiel Programmieren Tutorials haben wir die Spiellogik implementiert und dabei die Klasse GamePanel überarbeitet.

Dazu haben wir den Quellcode der Klasse GamePanel an mehreren Stellen verändert und erweitern. Die notwendigen Änderungen haben wir schrittweise vorgenommen, da sie sich über den gesamten Quellcode der Klasse erstreckten.

Nachdem wir die Implementierung der Spiellogik abgeschlossen hatten, haben wir unser Java Spiel in der NetBeans IDE ausgeführt und es ausgiebig getestet.

Unser Panzer-Spiel hat nun einen fortgeschrittenen Entwicklungsstand erreicht, in welchem sich sinnvoll auf Panzerjagd begeben werden kann. Wir sind damit unserem Ziel, ein eigenes Spiel in Java zu programmieren, einen großen Schritt näher gekommen.

In der nächsten Lektion werden wir einen Dialog erstellen, mit dessen Hilfe es möglich sein wird, die Farben des Spielerpanzers zu wählen.


Comments 1

  1. Pingback: Java Spiel Programmieren Tutorial - Das Java Projekt anlegen

Hinterlasse eine Antwort

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