Die Spielfläche wird in unser Spielfenster, das wir in der vorherigen Lektion erstellt hatten, eingefügt werden und auf ihr werden die Panzer unseres Panzer-Shooters fahren.
Die Panzer sollen später auf verschiedenen Untergründen fahren können. Die Untergründe lassen wir durch Hintergrundbilder darstellen, die wir in einem eigenen Verzeichnis abspeichern.
Um dies zu realisieren, werden wir ein weiteres Paket (Package) für unser Java Projekt anlegen und in diesem die Bilder ablegen. Anschließend werden wir die Java Klasse, die unsere Spielfläche repräsentiert, erstellen.
Diese neue Klasse besitzt zwei wichtige Funktionen. Sie ist zum einen das Zeichenbrett, auf welches wir unsere Spielobjekte zeichnen und zum anderen die Spiellogik unseres Java Spiels.
In dieser Lektion lernt ihr:
- Neues Paket für das Projekt erstellen
- Die GamePanel-Klasse erstellen
- Spielfläche im Spielfenster anzeigen
- Auf Fensterereignisse reagieren
- Das Spielmenü erweitern
- Ändern des Hintergrundbildes
Lektionen des Java Tutorials:
- Java Spiel Programmieren Tutorial
- Teil 1 – Java Projekt erstellen
- Teil 2 – Spielfenster erstellen
- Teil 3 – Spielfläche erstellen
- Teil 4 – Spielobjekte erstellen
- Teil 5 – Panzer und Spielsteuerung
- Teil 6 – Gegnerische Panzer erstellen
- Teil 7 – Die Spiellogik implementieren
- Teil 8 – Einstellungsdialog erstellen
Andere hilfreiche Beiträge:
Zu Beginn wird diese neue Klasse hauptsächlich als Zeichenbrett eingesetzt werden, später werden wir ihr noch die Spiellogik unseres Panzerspiels hinzufügen.
Nun wünschen wir euch viel Spaß beim dritten Teil unseres Java Spiel Programmieren Tutorials. Los geht’s!
1. Ein neues Paket (Package) für das Java Projekt erstellen
Nun werden wir als Erstes ein neues Paket für unser Java Projekt anlegen. Das neue Paket wird ein Unterpaket des bereits bestehenden Pakets sein und die Hintergrundbilder unserer Spielfläche aufnehmen.
Durch das Erstellen eines Unterpakets (subpackage) können wir sehr komfortabel die Klassendateien unseres Java Projekts von den Bilddateien trennen.
Dabei entspricht das Unterpaket einem Unterverzeichnis in unserem Quellcode-Verzeichnis. Wir können die Bilder daher einfach in den entsprechenden Ordner kopieren.
Um das neue Paket anzulegen und die Hintergrundbilder darin abzulegen, werden wir die folgenden Arbeitsschritte ausführen:
1.1 Erstellen des neuen Pakets
Wir möchten nun ein neues Paket in das bereits vorhandene Paket de.programmierenlernenhq.panzerhq
einfügen. Das neue Paket wird somit eine Unterpaket des vorhandenen Pakets sein.
Mit folgenden Arbeitsschritten legen wir das neue Paket mit dem Namen images
an:
- Mit der rechten Maustaste auf den Paketnamen
de.programmierenlernenhq.panzerhq
in der linken Projektleiste klicken. - Anschließend auf den
New
-Eintrag im Kontextmenü klicken. - Und schließlich auf
Java Package...
klicken, um den New Java Package-Dialog zu öffnen.
Nun öffnet sich der New Java Package-Dialog, der uns beim Erstellen des neuen Pakets für unser Java Projekt hilft.
Wir geben als Paketnamen in das Textfeld Package Name de.programmierenlernenhq.panzerhq.images
ein und klicken anschließend auf den Finish
-Button.
Sobald wir auf den Finish
-Button geklickt haben, wird ein physikalischer Ordner auf unserer Festplatte erstellt, der das neu erstellte Paket auf unserem Datensystem repräsentiert. Den Pfad zu diesem neu erstellten Ordner können wir bereits im New Java Package-Dialog ablesen. Wir haben ihn in der oberen Abbildung mit einer blauen Linie unterstrichen.
Das neue Paket sollte nun auch bei euch in der Projektleiste von NetBeans als Unterpaket von de.programmierenlernenhq.panzerhq
, wie in der unteren Abbildung dargestellt, angezeigt werden.
Das neue Paket wird noch mit Hilfe eines ausgegrauten Symbols dargestellt, da es momentan keine Dateien enthält. Dies werden wir als Nächstes ändern und die Hintergrundbilder unseres Java Spiels in das Verzeichnis des Pakets kopieren.
1.2 Kopieren der Hintergrundbilder in den Paketordner
Dieser Schritt ist sehr einfach. Wir müssen nur drei Bilder in den entsprechenden Ordner des neu erstellten Pakets auf unserer Festplatte kopieren. Dazu müsst ihr zuerst die folgenden drei Hintergrundbilder herunterladen und auf eurem Rechner speichern.
Klickt dazu mit der rechten Maustaste einzeln auf jeden der drei folgenden Links und wählt jeweils im Kontextmenü den Eintrag Ziel speichern unter...
aus:
Legt die drei Bilddateien in einem passenden Ordner auf eurer Festplatte ab, von dort werden wir die Bilder nun in das Verzeichnis unseres neu erstellten Pakets kopieren.
Dazu müsst ihr nun in den Quelldateien-Ordner des Java Projekts im Explorer navigieren. In unserem Beispielfall lautet der Pfad zum Quellordner E:\Programming\JavaProjekte\PanzerHQ\src
. Bei euch wird er wahrscheinlich etwas anders aussehen.
In diesem Quellordner befindet sich der Ordner de
. Dieser enthält den Unterordner programmierenlernenhq
. Dieser wiederum den Unterordner panzerhq
, in dem sich bereits zwei Klassendateien befinden und der Ordner images
des eben erstellten Pakets.
In den images
-Ordner werden wir nun die drei heruntergeladenen Hintergrundbilder kopieren. In unserem Beispielfall lautet der komplette Pfad zum Paketordner:
E:\Programming\JavaProjekte\PanzerHQ\src\de\programmierenlernenhq\panzerhq\images
Bei euch sollten die Paketordner, ab der de
-Ordnerebene, identisch sein. Wir kopieren nun die drei Hintergrunddateien in dem images
-Ordner.
Bei euch sollte das Ergebnis jetzt wie folgt aussehen:
In der oberen Abbildung haben wir den Pfad zum images
-Ordner mit einer orangen Linie unterstrichen. Der erste Teil des Verzeichnispfades wird sich bei euch höchstwahrscheinlich von dem angegebenen unterscheiden. Der unterstrichene Teil des Pfades sollte jedoch mit eurem identisch sein.
Sobald die Bilder in den images
-Ordner kopiert worden sind, ändert sich die Darstellung des Pakets in NetBeans. Das Paket wird dann nicht mehr als leer dargestellt, sondern erhält ein farbiges Symbol und einen Erweiterungs-Button.
Klickt man auf diesen Button werden die Dateinamen der drei Hintergrundbilder in der Projektleiste mit aufgelistet, so wie in der unteren Abbildung dargestellt.
Ist dies auch bei euch der Fall, dann hat das Ablegen der Bilder in den neu erstellten Paketordner korrekt funktioniert. In dem nächsten Abschnitt werden wir die Java Klasse unserer Spielfläche erstellen und in ihr auf die drei eingefügten Hintergrundbilder zugreifen.
Zur Kontrolle könnt ihr in der unteren Abbildung die Paketordner unseres Java Projekts mit eurer Ordnerstruktur vergleichen. Man kann dabei schön erkennen, dass jedes Java Paket (Package) ein eigenes Verzeichnis auf der Festplatte erhält.
2. Die Klasse GamePanel, die Spielfläche unseres Java Spiels, erstellen
Nun ist es an der Zeit die neue Klasse, welche als Spielfläche unseres Java Spiels fungiert, zu erstellen.
Die neue Java Klasse wird den Klassennamen GamePanel tragen. Wir werden sie als Zeichenbrett für die Objekte unseres Java Spiels nutzen.
Außerdem ist sie für die Spiellogik unseres Java Spiels verantwortlich. Die neue Klasse GamePanel verwaltet alle Spielobjekte und berechnet zyklisch den aktuellen Spielzustand unseres Java Spiels.
Um die GamePanel-Klasse zu erstellen, werden wir die folgenden beiden Arbeitsschritte ausführen:
- Anlegen der Klassendatei in NetBeans – Der erste Schritt ist schnell gemacht. Wir erstellen mit Hilfe der Entwicklungsumgebung NetBeans IDE die Klasse GamePanel.
- Definieren der GamePanel-Klasse – Im zweiten Schritt programmieren wir die neu erstellte Klasse aus. Da die GamePanel-Klasse ein zentraler Bestandteil unseres Java Spiels ist, wird sie in zukünftigen Lektionen Stück für Stück von uns erweitert werden.
Beginnen wir nun mit dem ersten Arbeitsschritt, dem Anlegen der GamePanel.java
Klassendatei.
2.1 Anlegen der Klassendatei GamePanel.java in NetBeans
Die Klasse GamePanel legen wir wie folgt an:
- Wir klicken mit der rechten Maustaste auf den Haupt-Packagenamen in der Projektleiste.
- Danach auf den
New
-Eintrag im Kontextmenü klicken. - Anschließend auf den Eintrag
Java Class...
klicken.
Daraufhin öffnet sich der New Java Class-Dialog von NetBeans. In diesem Dialog geben wir als Namen der zu erstellenden Klasse GamePanel
ein.
Alle anderen Einstellungen lassen wir unverändert und bestätigen den Dialog mit einem Klick auf Finish
.
Jetzt generiert NetBeans eine neue Java-Datei mit dem Namen GamePanel.java
und fügt sie in das Hauptpaket unseres Java Projekts ein. Dies sollte bei euch wie folgt aussehen:
Bisher macht unsere neue Klasse GamePanel noch gar nichts. Das werden wir nun ändern und ihr einigen Quellcode hinzufügen.
2.2 Definieren der GamePanel-Klasse
Die Klassendatei GamePanel.java
haben wir nun mit Hilfe von NetBeans in unserem Java Projekt angelegt. Als Nächstes werden wir die GamePanel-Klasse ausprogrammieren, also den Klassenbereich mit Variablen und Methoden füllen.
Dazu fügen wir den folgenden Quellcode in die GamePanel-Klasse ein und ersetzen damit den generierten Code:
GamePanel.java
package de.programmierenlernenhq.panzerhq; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.net.URL; import javax.swing.ImageIcon; import javax.swing.JPanel; import javax.swing.Timer; public class GamePanel extends JPanel{ public static final String IMAGE_DIR = "images/"; private final Dimension prefSize = new Dimension(1180, 780); private ImageIcon backgroundImage; private final String[] backgroundImages= new String [] {"bg_mud.jpg", "bg_snow.jpg", "bg_sand.jpg"}; private boolean gameOver = false; private int tanksDestroyedCounter = 0; private Timer t; public GamePanel() { setFocusable(true); setPreferredSize(prefSize); initGame(); startGame(); } public boolean isGameOver() { return gameOver; } public void setGameOver(boolean gameOver) { this.gameOver = gameOver; } private void initGame () { setBackgroundImage(1); createGameObjects(); t = new Timer(20, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { doOnTick(); } }); } private void createGameObjects() { // hier werden wir später die Spielobjekte erzeugen } private void initPlayersTank() { // hier werden wir den Panzer des Spielers initialisieren } public void setBackgroundImage(int imageNumber) { String imagePath = IMAGE_DIR + backgroundImages[imageNumber]; URL imageURL = getClass().getResource(imagePath); backgroundImage = new ImageIcon(imageURL); } private void startGame() { t.start(); } public void pauseGame() { t.stop(); } public void continueGame() { if (!isGameOver()) t.start(); } public void restartGame() { tanksDestroyedCounter = 0; setGameOver(false); createGameObjects(); startGame(); } private void endGame() { setGameOver(true); pauseGame(); } private void doOnTick() { tanksDestroyedCounter++; if (tanksDestroyedCounter > 2015) endGame(); repaint(); } @Override public void paintComponent (Graphics g) { super.paintComponent(g); Graphics2D g2d = (Graphics2D) g; g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); backgroundImage.paintIcon(null, g, 0, 0); g.setFont(new Font(Font.MONOSPACED, Font.BOLD, 19)); g.setColor(Color.BLUE); g.drawString("Tanks destroyed: " + tanksDestroyedCounter, 22, prefSize.height-5); if (isGameOver()) { g.setFont(new Font(Font.MONOSPACED, Font.BOLD, 50)); g.setColor(Color.RED); g.drawString("GAME OVER!", prefSize.width/2 - 130, prefSize.height/5); } } }
Das ist eine Menge Quellcode auf einmal. Wir werden nun den oberen Quelltext von oben nach unten durchgehen und in Ruhe besprechen, was sich hinter den Anweisungen verbirgt.
Zu Beginn der Klassendefinition erfolgt die Paketdeklaration in Zeile 1. Dieser folgen die Import-Anweisungen in den Zeilen 3 bis 14.
In Zeile 17, die markiert ist, geben wir den Namen unserer Java Klasse vor. Mit dem Schlüsselwort extends nach dem Klassennamen legen wir fest, dass sich unsere GamePanel-Klasse von der Klasse JPanel der Swing-Bibliothek ableiten soll. Dies ist sehr wichtig, da wir auf diese Weise alle Eigenschaften und Fähigkeiten der JPanel-Klasse erben, bspw. ist es dadurch möglich in dem Programmfenster zu zeichnen.
Anschließend definieren wir in den Zeilen 19 bis 31 die Variablen und Konstanten der GamePanel-Klasse. Auf die Membervariablen werden wir von den Methoden unserer Klasse aus zugreifen. Sie sind als private deklariert, da wir einen Zugriff von außerhalb nicht benötigen und auch nicht möchten.
Im parameterlosen Konstruktor, Zeile 34 bis 40, unserer GamePanel-Klasse legen wir die bevorzugte Größe der Spielfläche fest und stellen sicher, dass sie den Benutzerfokus erhalten kann. Außerdem rufen wir die beiden Methoden initGame() und startGame() auf.
Mit den beiden Methoden isGameOver() und setGameOver() in den Zeilen 43 bis 49 können wir den Spielzustand abfragen bzw. den Spielzustand auf den gewünschten boolean-Wert setzen. Wir werden diese Methoden später benötigen, wenn wir die Spiellogik implementieren.
Nun kommen wir zu einer sehr wichtigen Methode unserer GamePanel-Klasse, der initGame() Methode, die wir in den Zeilen 51 bis 61 definieren. In dieser Methode initialisieren wir unser Java Spiel. Wir setzen das Hintergrundbild, erzeugen die Spielobjekte und den Timer.
Mit dem Timer werden wir unser Java Spiel steuern. Für das neu erzeugte Timer-Objekt registrieren wir mit Hilfe einer anonymen inneren Klasse einen ActionListener mit der überschriebenen Methode actionPerformed().
Diese Methode wird immer dann automatisch aufgerufen, wenn der Timer um 20 ms weitergelaufen ist. In dieser Methode rufen wir unsere eigene Methode doOnTick() auf, die wir weiter unten im Quellcode definieren werden. Sie ist dafür zuständig, den Spielzustand zu berechnen und das Zeichnen der Spielobjekte zu veranlassen.
Die folgenden beiden Methoden, createGameObjects() und initPlayersTank(), in den Zeilen 63 bis 69 sind noch nicht ausprogrammiert. Dazu werden wir in späteren Lektionen kommen, wenn es auch Klassen für die Spielobjekte gibt. Für den Moment bleiben die Methodenrümpfe der beiden Methoden leer.
Die Methode setBackgroundImage() in den Zeilen 71 bis 75 nutzen wir, um das Hintergrundbild für unser Java Spiel zu laden. Dazu bauen wir zuerst den relativen Pfad zu einem unserer in Abschnitt 1 eingefügten Bilder zusammen. Anschließend lassen wir uns ein URL-Objekt erstellen, welches den absoluten Pfad zur Bilddatei enthält. Mit diesem URL-Objekt als Argument erstellen wir dann in Zeile 74 unser Hintergrundbild in Form eines ImageIcon-Objekts.
Die nächsten fünf Methoden, startGame(), pauseGame(), continueGame(), restartGame() und endGame(), in den Zeilen 77 bis 99 sind selbsterklärend. Mit ihnen ändern wir den allgemeinen Spielzustand unseres Java Spiels. Dabei greifen wir auf das Timer-Objekt zu und starten bzw. stoppen den Timer unseres Spiels darüber.
Kommen wir nun zur Schrittmacher-Methode unseres Java Spiels. Mit der Methode doOnTick() in den Zeilen 101 bis 106 berechnen wir den internen Spielzustand und lassen die Spielfläche neu zeichnen. Um den Spielzustand zu berechnen, benötigen wir noch die Spiellogik unseres Java Spiels. Im Moment können wir diese noch nicht implementieren, da wir noch keine Spielobjekte erzeugen können. In den späteren Lektionen werden wir dies nachholen.
Um unser Spielfeld dennoch testen zu können, lassen wir probehalber den Punktezähler tanksDestroyedCounter
bis 2016 zählen. Pro Tick, also alle 20 ms, wird der Zähler um Eins erhöht. Sobald er 2016 erreicht hat, wird das Spiel beendet und dadurch der Timer gestoppt.
Mit der Anweisung in Zeile 105 lassen wir die Spielfläche neu zeichnen. Durch den Aufruf der geerbten repaint() Methode, wird auch die Callback-Methode paintComponent() aufgerufen. Diese Callback-Methode haben wir in den Zeilen 109 bis 126 überschrieben und lassen durch sie unsere Spielgrafik darstellen.
Da wir zum jetzigen Zeitpunkt noch keine Spielobjekte erstellen können, begnügen wir uns mit der Anzeige eines Strings. Mit dem String werden wir den aktuellen Wert des Punktezählers tanksDestroyedCounter
auf der Spielfläche anzeigen lassen. Zusätzlich lassen wir einen weiteren String mit dem Inhalt GAME OVER! anzeigen, sobald der Zähler den Wert 2016 erreicht hat.
Damit haben wir das Ende der Klassendefinition unserer Klasse GamePanel erreicht. In NetBeans sollte die GamePanel-Klasse nun wie folgt aussehen:
In der oberen Abbildung haben wir einige Markierungen vorgenommen. Wir möchten damit noch einmal für Programmierneulinge aufzeigen, aus welchen Bestandteilen sich eine Klasse in Java zusammensetzt. Unsere Klasse GamePanel besteht aus den folgenden Bereichen:
- A: Die Import-Anweisungen – Durch sie können wir auf andere Klassen der Java Bibliothek zugreifen, ohne den vollqualifizierten Namen der jeweiligen Klasse angeben zu müssen.
- B: Die Attribute (Felder) der Klasse – Auf diese Variablen können wir von jeder Stelle innerhalb der Klasse GamePanel aus zugreifen. Auch wenn die Variable als private deklariert ist.
- C: Der Konstruktor der Klasse – Unsere Klasse verfügt über genau einen Konstruktor. Der Konstruktor ist in unserem Fall parameterlos. In späteren Lektionen werden wir in anderen Klassen Konstruktoren mit Parametern definieren.
- D: Methodenbereich der Klasse – Unsere Klasse verfügt über mehrere Methoden. Die meisten Methoden unserer Klasse geben keinen Rückgabewert zurück. Dies ist am Rückgabe-Datentyp void (leer) zu erkennen.
- 1: Die Superklasse unserer Klasse – In Java kann eine Klasse die Eigenschaften einer anderen Klasse erben. Die Oberklasse (Superklasse) vererbt dabei ihr Eigenschaften an die Unterklasse (Subklasse). Oder anders ausgedrückt, die Unterklasse erweitert (extends) die Oberklasse um weitere Eigenschaften. In unserem Fall erbt die Klasse GamePanel alle Eigenschaften und Fähigkeiten der Klasse JPanel aus der Swing-Bibliothek von Java.
Soviel zu der neu erstellten Klasse GamePanel, der Spielfläche unseres Java Spiels. Damit die Spielfläche auch in unserem Spielfenster angezeigt wird, werden wir als Nächstes einige Änderungen an der Klasse GameWindow, dem Spielfenster unseres Java Spiels, vornehmen.
3. Die erstellte Spielfläche im Spielfenster anzeigen lassen
Wir öffnen nun die Klassendatei GameWindow.java
unseres Java Projekts im Editorfenster von NetBeans. Diese Java Klasse hatten wir in der vorherigen Lektion erstellt. Sie repräsentiert das Spielfenster unseres Java Spiels.
Damit unser Spielfenster auch die eben erstellte Spielfläche (GamePanel) anzeigt, müssen wir sie als grafische Komponente dem Spielfenster hinzufügen. Dazu werden wir nun folgende Änderungen am Quellcode der GameWindow-Klasse vornehmen:
- Einfügen der Membervariable
panzerGamePanel
vom Typ GamePanel in die GameWindow-Klasse. - Überarbeiten des Konstruktors der GameWindow-Klasse.
Anschließend werden wir unser Java Spiel testen und prüfen, ob die neue Spielfläche korrekt angezeigt wird.
Um die beiden oben genannten Änderungen vorzunehmen, fügen wir den markierten Quellcode in die Klassendatei der GameWindow-Klasse ein:
GameWindow.java
. . . public class GameWindow extends JFrame{ private final GamePanel panzerGamePanel; public GameWindow() { this.panzerGamePanel = new GamePanel(); registerWindowListener(); createMenu(); add(panzerGamePanel); pack(); setTitle("PanzerHQ"); setLocation(10, 10); setResizable(false); setVisible(true); } . . .
Der obere Quellcode enthält einen Teil der Klasse GameWindow. Es muss nur die Membervariable in Zeile 7 deklariert werden und der Konstruktor mit den Zeilen 9 bis 24 ersetzt werden. Es müssen keine weiteren Änderungen an der GameWindow-Klasse durchgeführt werden.
Durch die eben vorgenommenen Änderungen lassen wir die Spielfläche in dem Spielfenster unseres Java Spiels anzeigen. Die Spielfläche wird durch die Variable panzerGamePanel
realisiert, die vom Datentyp GamePanel ist.
Mit der Anweisung in Zeile 16 fügen wir die Spielfläche (das GamePanel-Objekt) dem Spielfenster hinzu. Die folgenden Anweisungen lassen das Spielfenster auf dem Bildschirm in der bevorzugten Abmessung anzeigen.
In NetBeans sollte der relevante Quellcode der GameWindow-Klasse nun folgendermaßen aussehen:
In der oberen Abbildung ist der Anfang der GameWindow-Klasse dargestellt. Die von uns vorgenommenen Änderungen am Quellcode wurden jeweils mit einem blauen Rahmen markiert. Der Rahmen mit der Markierung A umschließt die neu eingefügte Membervariable panzerGamePanel
. Der Rahmen mit der Markierung B umschließt den überarbeiteten Konstruktor GameWindow() unserer Klasse.
Es sind nun alle Änderungen an der Klasse GameWindow vorgenommen worden, die notwendig waren, damit unsere Spielfläche im Spielfenster angezeigt wird. Jetzt ist es an der Zeit unser Java Spiel zu testen und dabei zu prüfen, ob die Spielfläche korrekt dargestellt wird.
3.1 Testen der Spielfläche unseres Java Spiels
Wir führen nun unser Java Projekt mit einem Klick auf das Run Project-Symbol aus.
Im Hintergrund wird jetzt unser Java Spiel von der NetBeans IDE erstellt und anschließend ausgeführt. Als Ergebnis wird uns das Spielfenster mit der eingefügten Spielfläche angezeigt:
In der oberen Abbildung ist das Spielfenster (GameWindow) unseres Java Spiels dargestellt. Es enthält jetzt als grafische Komponenten die Spielfläche (GamePanel), die wir in dieser Lektion erstellt haben. Sobald unser Spiel gestartet wird, beginnt der Zähler tanksDestroyedCounter mit dem Hochzählen. Wird er Wert 2016 erreicht, stoppt der Zähler und der Hinweis GAME OVER! wird eingeblendet.
Da unser Java Spiel nun über einen eigenen Timer verfügt und mit diesem gestartet und gestoppt werden kann, ist es jetzt möglich Menüeinträge zum Steuern des allgemeinen Spielzustands einzufügen. Außerdem können wir jetzt auf weitere Fensterereignisse, bspw. dem Verlieren des Benutzerfokus, reagieren.
In den nächsten beiden Abschnitten werden wir daher die Menüleiste und den WindowListener entsprechend der jetzt verfügbaren Möglichkeiten erweitern.
4. Auf Fensterereignisse im Spiel reagieren
Nun werden wir den WindowListener unseres Spielfensters um zwei Funktionen erweitern. Und zwar soll der WindowListener das Spielgeschehen anhalten, sobald das Spielfenster den Benutzerfokus verliert und erst wieder fortsetzen, wenn das Fenster den Fokus wieder zurück erhält.
Die dafür notwendigen Änderungen sind sehr einfach und kurz. Wir müssen nur in der bereits vorhandenen Methode registerWindowListener() der GameWindow-Klasse zwei Anweisungen einfügen. Das Rahmenwerk dafür benötigte Rahmenwerk hatten wir bereits in der vorherigen Lektion angelegt.
Wir fügen nun den markierten Quellcode in die Methode registerWindowListener() der Klasse GameWindow ein:
GameWindow.java
private void registerWindowListener() { addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { System.exit(0); } @Override public void windowDeactivated(WindowEvent e) { // hier wird das Spiel pausiert panzerGamePanel.pauseGame(); } @Override public void windowActivated(WindowEvent e) { // hier wird das Spiel wieder fortgesetzt panzerGamePanel.continueGame(); } }); }
Mit der Anweisung in Zeile 10 des oberen Quellcodes lassen wir das Spiel anhalten. Die Callback-Methode windowDeactivated() wird automatisch aufgerufen, sobald das Spielfenster den Benutzerfokus nicht mehr besitzt, also bspw. ein anderes Fenster in den Vordergrund rückt.
Die Anweisung in Zeile 15 setzt das Spiel wieder fort. Die Methode windowActivated() wird automatisch aufgerufen, sobald unser Spielfenster den Benutzerfokus zurück erhält.
In NetBeans sollte die Methode registerWindowListener() der GameWindow-Klasse nun wie folgt aussehen:
In der oberen Abbildung wurde die Methode registerWindowListener() mit einem blauen Rahmen (A) markiert. Die beiden eingefügten Anweisungen sind jeweils mit einer orangen Linie unterstrichen.
4.1 Testen der neuen Funktionen des WindowListeners
Wir führen nun unser Java Projekt mit einem Klick auf das Run Project-Symbol aus.
Im Hintergrund wird jetzt unser Java Spiel von der NetBeans IDE erstellt und anschließend ausgeführt. Als Ergebnis wird uns das Spielfenster mit der eingefügten Spielfläche angezeigt.
Der provisorische Zähler sollte nun stoppen, sobald ein anderes Fenster den Benutzerfokus erhält, und wieder fortgesetzt werden, sobald das Spielfenster den Benutzerfokus wieder zurück erhält.
Somit kann unser Java Spiel jetzt gezielt angehalten und wieder fortgesetzt werden. Im nächsten Abschnitt werden wir die obere Menüleiste um weitere Einträge erweitern, mit deren Hilfe wir unser Java Spiel pausieren, fortsetzen und neu starten lassen.
5. Das Spielgeschehen über das Spielmenü steuern
Wir möchten unser Java Spiel auch über die obere Menüleiste pausieren, fortsetzen und neu starten können. Dazu werden wir die Kategorie Game der oberen Menüleiste um drei neue Einträge (Pause, Continue und Restart) erweitern.
Um die Menüleiste entsprechend zu erweitern, werden wir die folgenden beiden Arbeitsschritte ausführen:
- Definieren der Methode addGameMenuItems() – Unserer Klasse GameWindow werden wir die neue Methode addGameMenuItems() hinzufügen, die für das Anlegen der drei Menüeinträge Pause, Continue und Restart verantwortlich ist.
- Aufrufen der Methode addGameMenuItems() in der createMenu() Methode – Damit die drei neuen Menüeinträge auch tatsächlich angelegt werden, muss die neue Methode addGameMenuItems() auch aufgerufen werden. Den entsprechenden Methodenaufruf werden wir in der bereits vorhandenen createMenu() Methode der GameWindow-Klasse ausführen.
5.1 Definieren der Methode addGameMenuItems()
Wir werden nun die Methode addGameMenuItems() in unserer GameWindow-Klasse definieren. Dazu fügen wir die folgenden Methodendefinitionen direkt nach der addFileMenuItems()-Methode in den Methodenbereich der GameWindow-Klasse ein:
GameWindow.java
private void addGameMenuItems(JMenu gameMenu) { JMenuItem pauseItem = new JMenuItem("Pause"); gameMenu.add(pauseItem); pauseItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { panzerGamePanel.pauseGame(); } }); JMenuItem continuetItem = new JMenuItem("Continue"); gameMenu.add(continuetItem); continuetItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { panzerGamePanel.continueGame(); } }); gameMenu.addSeparator(); JMenuItem restartItem = new JMenuItem("Restart"); gameMenu.add(restartItem); restartItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { panzerGamePanel.restartGame(); } }); }
Mit der im oberen Quelltext definierten Methode addGameMenuItems() fügen wir dem Game-Menü die drei Menüeinträge Pause, Continue und Restart hinzu.
Dabei wird für jeden Menüeintrag, also für jedes JMenuItem-Objekt, ein ActionListener registriert. Wir definieren die ActionListener-Objekte auch diesmal wieder mit Hilfe von anonymen inneren Klassen.
In den überschriebenen actionPerformed()-Methoden der registrierten ActionListener haben wir die Anweisungen zum Steuern unseres Java Spiels platziert. Mit der Anweisung in Zeile 7 bspw. halten wir das Spielgeschehen an. Dazu rufen wir die pauseGame() Methode über das GamePanel-Objekt panzerGamePanel
auf und stoppen damit den Timer unseres Java Spiels.
Damit die drei neuen Menüeinträge auch tatsächlich angelegt werden, muss die neue Methode addGameMenuItems() auch aufgerufen werden. Den entsprechenden Methodenaufruf werden wir nun in der bereits vorhandenen createMenu() Methode der GameWindow-Klasse ausführen.
5.2 Aufrufen der Methode addGameMenuItems()
Die createMenu() Methode ist bereits in der GameWindow-Klasse vorhanden. Wir fügen nun der createMenu() Methode den Methodenaufruf addGameMenuItems(gameMenu); hinzu.
Dazu fügen wir die markierte Zeile in die createMenu() Methode unserer GameWindow-Klasse ein:
GameWindow.java
private void createMenu() { JMenuBar menuBar = new JMenuBar(); this.setJMenuBar(menuBar); JMenu fileMenu = new JMenu("File"); JMenu gameMenu = new JMenu("Game"); JMenu prefMenu = new JMenu("Preferences"); menuBar.add(fileMenu); menuBar.add(gameMenu); menuBar.add(prefMenu); addFileMenuItems(fileMenu); addGameMenuItems(gameMenu); }
Mit dem Aufruf der addGameMenuItems() Methode in Zeile 15 werden die drei Menüeinträge Pause, Continue und Restart in das Game-Menü unseres Spielfensters eingefügt. Als Argument übergeben wir der addGameMenuItems() Methode das gameMenu-Objekt vom Datentyp JMenu.
In NetBeans sollte der relevante Quellcode der GameWindow-Klasse nun folgendermaßen aussehen:
In der oberen Abbildung sind die vorgenommenen Änderungen markiert. Der blaue Rahmen (A) umschließt die neu definierte Methode addGameMenuItems(). Der, in die createMenu() Methode, eingefügte Methodenaufruf ist mit einer blauen Linie (B) unterstrichen.
Als Nächstes werden wir unser Java Projekt ausführen und die neuen Menüeinträge ausprobieren.
5.3 Testen der drei neuen Menüeinträge unseres Spielfensters
Wir führen nun unser Java Projekt mit einem Klick auf das Run Project-Symbol aus.
Im Hintergrund wird jetzt unser Java Spiel von der NetBeans IDE erstellt und anschließend ausgeführt.
Wenn wir nun auf die drei neuen Menüeinträge Pause, Continue und Restart klicken, können wir den allgemeinen Spielzustand unseres Java Spiels ändern.
In der oberen Abbildung sind die drei neuen Menüeinträge zu sehen. Durch Klicken auf einen der drei Menüeinträge kann unser Java Spiel nun angehalten, wieder fortgesetzt oder komplett neu gestartet werden.
6. Ändern des Hintergrundbildes unserer Spielfläche
Unser Java Spiel soll drei verschiedene Spielfelduntergründe besitzen, von denen der Spieler eine als Spielfläche auswählen kann. Realisieren werden wir dies mit Hilfe eines Hintergrundbildes, auf das wir später die Spielobjekte zeichnen.
Dazu werden wir nun die obere Menüleiste um drei Einträge und ein Untermenü (submenu) erweitern, mit deren Hilfe der gewünschte Spielfelduntergrund ausgewählt werden kann. Momentan wird das Hintergrundbild bg_snow.jpg
standardmäßig bei Spielstart angezeigt.
Um die Menüeinträge für das Auswählen des Hintergrundbildes in unser Spielfenster einzufügen, werden wir die folgenden beiden Arbeitsschritte durchführen:
- Definieren der Methode addPrefMenuItems() – Unserer Klasse GameWindow werden wir die neue Methode addPrefMenuItems() hinzufügen, die für das Anlegen der drei Menüeinträge Mud Area, Snow Area, Desert Area und dem Untermenü Choose Background verantwortlich ist.
- Aufrufen der Methode addPrefMenuItems() in der createMenu() Methode – Damit die drei neuen Menüeinträge und das Untermenü auch tatsächlich angelegt werden, muss die neue Methode addPrefMenuItems() auch aufgerufen werden. Den entsprechenden Methodenaufruf werden wir in der bereits vorhandenen createMenu() Methode der GameWindow-Klasse ausführen.
6.1 Definieren der Methode addPrefMenuItems()
Wir werden nun die Methode addPrefMenuItems() in unserer GameWindow-Klasse definieren. Dazu fügen wir die folgenden Methodendefinitionen direkt nach der addGameMenuItems()-Methode in den Methodenbereich der GameWindow-Klasse ein:
GameWindow.java
private void addPrefMenuItems(JMenu prefMenu) { JMenu submenu = new JMenu("Choose Background"); prefMenu.add(submenu); JMenuItem menuItem = new JMenuItem("Mud Area"); submenu.add(menuItem); menuItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { panzerGamePanel.setBackgroundImage(0); repaint(); } }); menuItem = new JMenuItem("Snow Area"); submenu.add(menuItem); menuItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { panzerGamePanel.setBackgroundImage(1); repaint(); } }); menuItem = new JMenuItem("Desert Area"); submenu.add(menuItem); menuItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { panzerGamePanel.setBackgroundImage(2); repaint(); } }); }
Mit der im oberen Quelltext definierten Methode addPrefMenuItems() fügen wir dem Preferences-Menü das Untermenü Choose Background mit den drei Menüeinträge Mud Area, Snow Area und Desert Area hinzu.
Dabei wird für jeden der drei Menüeintrag, also für jedes JMenuItem-Objekt, ein ActionListener registriert. Wir definieren die ActionListener-Objekte auch diesmal wieder mit Hilfe von anonymen inneren Klassen.
In den überschriebenen actionPerformed()-Methoden der registrierten ActionListener haben wir die Anweisungen zum Festlegen des Hintergrundbilds für unseres Java Spiel platziert. Anschließend lassen wir das Spielfenster neu zeichnen, damit das ausgewählte Hintergrundbild sofort dargestellt wird.
Mit der Anweisung in Zeile 11 bspw. wählen wir das Hintergrundbild bg_mud.jpg
aus. Dazu rufen wir die setBackgroundImage() Methode über das GamePanel-Objekt panzerGamePanel
auf. Als Argument übergeben wir der setBackgroundImage() Methode den Wert 0.
Damit das Untermenü mit den drei neuen Menüeinträge auch tatsächlich angelegt wird, muss die neue Methode addPrefMenuItems() auch aufgerufen werden. Den entsprechenden Methodenaufruf werden wir nun in der bereits vorhandenen createMenu() Methode der GameWindow-Klasse ausführen.
6.2 Aufrufen der Methode addPrefMenuItems()
Die createMenu() Methode ist bereits in der GameWindow-Klasse vorhanden. Wir fügen nun der createMenu() Methode den Methodenaufruf addPrefMenuItems(prefMenu); hinzu.
Dazu fügen wir die markierte Zeile in die createMenu() Methode unserer GameWindow-Klasse ein:
GameWindow.java
private void createMenu() { JMenuBar menuBar = new JMenuBar(); this.setJMenuBar(menuBar); JMenu fileMenu = new JMenu("File"); JMenu gameMenu = new JMenu("Game"); JMenu prefMenu = new JMenu("Preferences"); menuBar.add(fileMenu); menuBar.add(gameMenu); menuBar.add(prefMenu); addFileMenuItems(fileMenu); addGameMenuItems(gameMenu); addPrefMenuItems(prefMenu); }
Mit dem Aufruf der addPrefMenuItems() Methode in Zeile 16 wird das Untermenü Choose Background mit den drei Menüeinträge in das Preferences-Menü unseres Spielfensters eingefügt. Als Argument übergeben wir der addPrefMenuItems() Methode das prefMenu-Objekt vom Datentyp JMenu.
In NetBeans sollte der Quellcode der GameWindow-Klasse nun folgendermaßen aussehen:
In der oberen Abbildung sind die, in diesem Abschnitt, vorgenommenen Änderungen markiert. Der blaue Rahmen (A) umschließt die neu definierte Methode addPrefMenuItems(). Der, in die createMenu() Methode, eingefügte Methodenaufruf ist mit einer blauen Linie (B) unterstrichen.
Zudem enthält die obere Abbildung den gesamten Quellcode der GameWindow-Klasse. An dem Quellcode wird sich in den nächsten Lektionen dieses Java Spiel Programmieren Tutorials nur noch sehr wenig ändern.
Als Nächstes werden wir unser Java Projekt ausführen und mit Hilfe der neuen Menüeinträge den Spielfelduntergrund ändern.
6.3 Testen der neuen Menüeinträge und Ändern des Spielfelduntergrunds
Wir führen nun unser Java Projekt mit einem Klick auf das Run Project-Symbol aus.
Im Hintergrund wird jetzt unser Java Spiel von der NetBeans IDE erstellt und anschließend ausgeführt.
Wenn wir nun auf einen der drei neuen Menüeinträge Mud Area, Snow Area und Desert Area des Untermenüs Choose Background klicken, können wir das Hintergrundbild unseres Java Spiels ändern.
In der oberen Abbildung ist das Untermenü mit den drei neuen Menüeinträge zu sehen. Durch Klicken auf einen der drei Menüeinträge wird das Hintergrundbild und somit der Spielfelduntergrund unseres Java Spiels geändert.
6.4 Die drei Klassendateien unseres Java Projekts zum Download
Somit haben wir alle Änderungen am Quellcode unseres Java Spiels für diese Lektion vorgenommen. Da wir diesmal an zwei Dateien Änderungen vorgenommen haben und dabei auch noch an verschiedenen Stellen im Quelltext, ist es wahrscheinlich nicht ganz so einfach den Anweisungen des Tutorials immer zu folgen und es kann leicht zu Missverständnissen kommen.
Daher solltet ihr noch einmal in Ruhe den kompletten Quellcode der drei Klassendateien unseres Java Spiels anschauen und mit eurem vergleichen:
PanzerHQ.javaGameWindow.java
GamePanel.java
Wir sind nun am Ende der dritten Lektion unseres Java Spiel Programmieren Tutorials angekommen. Mittlerweile besitzt das Spielfenster unseres Java Spiele schon etwas Funktionalität, die wir als Nächstes in einem kleinen Video vorstellen werden.
6.5 Video – Funktionspräsentation unseres Java Spiels
In dem unteren Video ist das Spielfenster unseres selbst programmierten Java Spiels zu sehen. Momentan besteht das Spielfenster aus einer oberen Menüleiste und einer Spielfläche (JPanel), auf der sich aber noch nicht viel tut.
Bisher kann der allgemeine Spielzustand über die Menüleiste gesteuert und der Untergrund der Spielfläche verändert werden. Beides haben wir im unteren Video vorgeführt:
Wie man schön im Video sehen kann, ist nun das Rahmenwerk für unser Java Spiel gelegt. Wir haben ein Spielfenster mit einem Menü und einer Spielfläche. Über einen internen Taktgeber können wir von Spielzustand zu Spielzustand wechseln. Es wird also Zeit, dass die Spielobjekte ihren Weg ins Java Spiel finden.
Zusammenfassung
In der dritten Lektion unseres Java Spiel Programmieren Tutorials haben wir die Spielfläche für unser Java Spiel erstellt. Die Spielfläche haben wir anschließend in unser Spielfenster eingefügt. Auf ihr werden später die Panzer unseres Java Spiels fahren.
Damit die Panzer auf verschiedenen Untergründen fahren können, haben wir ein Unterpaket (subpackage) erstellt und drei Hintergrundbilder darin abgelegt.
Weiterhin haben wir die obere Menüleiste unseres Spielfensters um mehrere Menüeinträge erweitert, so dass es jetzt möglich ist, den allgemeinen Spielzustand zu steuern und das Hintergrundbild der Spielfläche zu ändern.
In der nächsten Lektion unseres Java Spiele Programmieren Tutorials werden wir unser erstes Spielobjekt erstellen und auf der Spielfläche unseres Spielfensters darstellen.
Comments 20
Wie kann man das Hintergrundbild in voller Große anzeigen lassen. Ich habe eigene Bilder verwendet, jedoch wird nur ein Teil des Bilder angezeigt. Anscheinend wird Pixel für Pixel dargestellt. Vielen Dank!
Author
Hallo Fabian,
der einfachste Weg ist die Bilder auf die Maße 1200 x 800 px umzuwandeln. In dieser Abmessung liegen die 3 Beispielbilder vor.
Viele Grüße, Chris
Hallo,
bei mir hat „imageURL“ den wert null.
Ich bekomme diesen Fehler:
Exception in thread „main“ java.lang.NullPointerException
at javax.swing.ImageIcon.(Unknown Source)
at de.firstgame.panzerhq.GamePanel.setBackgroundImage(GamePanel.java:76)
at de.firstgame.panzerhq.GamePanel.initGame(GamePanel.java:54)
at de.firstgame.panzerhq.GamePanel.(GamePanel.java:40)
at de.firstgame.panzerhq.GameWindow.(GameWindow.java:23)
woran liegt das?
Author
Hallo Max,
die Bildquelle wird nicht gefunden. Einige Leser hatten diesbezügliche Probleme, weil sie eine andere Entwicklungsumgebung als die im Tutorial verwendete genutzt haben. Dann konnte der Pfad zum Bild nicht richtig aufgelöst werden.
Viele Grüße,
Chris
Ich benutze IntelliJ IDEA und es funktioniert nicht…
Muss der Pfad vielleicht anders geschrieben werden?
Author
Hallo WhiteS,
In Eclipse musste
statt
geschrieben werden. Funktioniert das auch in Intellij IDEA?
Viele Grüße,
Chris
Guten Abend!
in IntelliJ muss der Pfad wie folgt angegeben werden:
public static final String IMAGE_DIR = „./images/“;
Viele Grüße
Paul
Hallo Leute,
ich habe Teile dieses Programms (die Methoden Game Panel, isGameOver, setGameOver, initGame, createGameObjects, initPlayer (statt initPlayerTank), startGame, pauseGame, continueGame, restartGame, endGame, doOnTick und paintComponent; weiter die Klassen GameObject, Coordinate und Player (statt PlayerTank) in ähnlicher Weise) in Eclipse genutzt, weil ich ein ähnliches Spiel programmieren wollte (keine Panzer, sondern ein Pfeil, der Hindernissen ausweichen muss). Aber aus irgendeinem Grund funktioniert der Befehl repaint(); nicht; das überschriebene paintComponent wird nicht gestartet.
Ich hoffe, jemand kann mir helfen.
VG
Lindwurm
Hallo ich habe das gleiche Problem wie Tea, bin mir aber sicher die Bilder im richtigem Ordner abgescpeichert zu haben. Ich benutze Eclipse, hat das etwas damit zu tun?
Author
Hallo Zuroll,
das kann durchaus an Eclipse liegen. Ich benutze nur NetBeans zum Programmieren in Java. Bin daher kein Experte bei Eclipse. Vielleicht weiß ein Leser hier besser Bescheid und kann einen Tipp geben?
Viele Grüße, Chris
In Eclipse
public static final String IMAGE_DIR = „../images/“;
statt
public static final String IMAGE_DIR = „images/“;
schreiben. Dann funktioniert es.
Author
Hallo Paule,
danke für den Tipp!
Viele Grüße, Chris
Ich finde den Quelldatein Ordner meines Java Projektes nicht….also ich weiß nicht genau wo ich da suchen soll.Wäre nett wenn ihr mir dabei helfen könntet
Author
Hallo Jona,
in NetBeans kann man sich den Pfad zum Projekt-Ordner, in welchem auch die Quelldateien liegen, anzeigen lassen. Dazu bitte folgendermaßen vorgehen:
File
->Project Properties
Sources
alsCategory
ausgewählt werden.Project Folder
angezeigt. Die Quelldateien liegen in diesem Ordner in den Unterordner:src
und in dessen Unterordnern.Viele Grüße, Chris
Hallo Chris,
habe die GamePanel-Klasse erstellt und mit deinem Code befüllt und die entsprechenden Änderungen in der GameWindow-Klasse übernommen. Beim Starten des Programms sehe ich aber nur das Fenster mit dem gewählten Hintergrundbild. Der Text des Counters und der Text Game Over werden nicht angezeigt. Daher weiss ich nicht, ob der Counter überhaupt hoch läuft. Hast du ne Idee, was mein Problem sein könnte?
Danke für deine Hilfe!
Sorry, hab nicht lange genug gewartet und meine Bildschirmauflösung war zu klein gewählt.
Author
Hallo Seb,
kein Problem. Viel Spaß noch beim Tutorial!
Viele Grüße, Chris
Hallo, wenn ich den GamePanel Code einfüge, kommt bei mir folgende Fehlermeldung: Exception in thread „main“ java.lang.NullPointerException
at javax.swing.ImageIcon.(Unknown Source)
at panzerhq.GamePanel.setBackgroundImage(GamePanel.java:72)
at panzerhq.GamePanel.initGame(GamePanel.java:50)
at panzerhq.GamePanel.(GamePanel.java:36)
at panzerhq.GameWindow.(GameWindow.java:27)
at panzerhq.PanzerHQ.main(PanzerHQ.java:8)
Habe die Codes in Eclipse eingefügt, weil ich hauptsächlich damit arbeite.
Brauche Hilfe 🙁
Author
Hallo Tea,
es sieht so aus, als ob das Hintergrundbild nicht geladen werden kann. Also der Pfad zum Bild nicht korrekt ist und somit die Bilddatei nicht gefunden wird.
Das Bild muss an der korrekten Stelle abgespeichert sein und der Pfad auf diese Stelle verweisen, dann sollte auch die Fehlermeldung nicht mehr auftreten.
Viele Grüße, Chris
Pingback: Java Spiel Programmieren Tutorial - Das Java Projekt anlegen