Beispiellösung - Hochschule Niederrhein

Werbung
Hochschule Niederrhein
Fachbereich 03
Bachelor Informatik
Grundlagen der
Java Programmierung
SS 2015
Prof. Dr. Nitsche
Übung 7 – Beispiellösung
Grafik-Programmierung
In dieser Übung fahren wir mit den Grundlagen der Grafikprogrammierung aus Übung 6 fort
und wenden dies auf Spiele an.
Aufgabe 1: Zeichenbare Game-Objekte
Zunächst definieren wir ein Interface PaintableGameObject mit der Methode
paintComponent. Unsere Spielobjekte (vom Typ GameObject) sollen dieses Interface
implementieren und dadurch grafisch darstellbar sein. Die eigentliche Darstellung erfolgt
jeweils spezifisch für jedes Spielobjekt. In unserem Spiel brauchen wir uns dann nicht mehr
weiter darum zu kümmern, wie die Spielobjekte gezeichnet werden, da aufgrund der
dynamischen Bindung jedes Objekt „weiß“, wie es sich selbst zeichnen soll.
Wir erweitern danach das Interface GraphicsObject um dieses Interface, damit alle
Spielobjekte gezeichnet werden können.
Da durch diese Interfaces-Erweiterung die implementierenden Klassen abstrakt geworden
sind, müssen wir noch die Methode paintComponent jeweils spezifisch implementieren.
Hinweis: Falls Sie Ihre Lösung von Übung 4 nicht vollständig realisiert haben, können Sie von
der Webseite der Veranstaltung eine Beispielvorgabe der bereits in den Übungen
realisierten Interfaces und Klassen herunterladen.
a) Erstellen Sie ein Interface PaintableGameObject im Paket
games.basic.gameObjects.interfaces.
Diese soll die Methode
public void paintComponent(Graphics g)
bereitstellen.
b) Leiten Sie das Interface games.basic.gameObjects.interfaces.
GameObject (siehe Übung 4) neben SimpleGameObject auch von
PaintableGameObject ab.
c) Passen Sie dann die Klassen RectangularGameObject und
CircularGameObject aus dem Paket games.basic.gameObjects
entsprechend an, indem Sie dort jeweils die Methode paintComponent
implementieren.
S. 1 / 14
Hochschule Niederrhein
Fachbereich 03
Bachelor Informatik
Grundlagen der
Java Programmierung
SS 2015
Prof. Dr. Nitsche
Übung 7 – Beispiellösung
Dabei soll
• die Farbe des Rechteckes bzw. Kreises jeweils per Attribut wählbar sein,
•
ebenso ob die Figuren ausgefüllt sein sollen oder nicht.
Hinweis: Zum Zeichnen eines ausgefüllten Rechteckes bzw. Kreises können Sie die
Methoden
fillRect(x, y, width, height)
bzw.
fillOval(x, y, width, height)
aus der Klasse java.awt.Graphics verwenden (siehe
http://docs.oracle.com/javase/7/docs/api/index.html?java/awt/Graphics.html),
während drawRect bzw. drawOval jeweils nur den Rahmen der Figuren zeichnet.
Hinweis: Zum Festlegen der Strichdicke können Sie die Methode
setStroke(new BasicStroke( <<Strichdicke>> )
aus der Klasse Graphics2D verwenden.
d) Erstellen Sie eine (analog zu RectangularGameObject) eine von AbstractGameObject
abgeleitete Klasse CrossGameObject, welche ein Kreuz (d.h. 2 Linien) darstellt.
games.basic.gameObjects.interfaces.PaintableGameObject.java:
package games.basic.gameObjects.interfaces;
import java.awt.Graphics;
public interface PaintableGameObject {
public abstract void paintComponent(Graphics g);
}
games.basic.gameObjects.interfaces.GameObject.java:
package games.basic.gameObjects.interfaces;
public interface GameObject extends SimpleGameObject,
PaintableGameObject {
public
public
public
public
abstract
abstract
abstract
abstract
boolean
boolean
boolean
boolean
isLeftOf(GameObject other);
isRightOf(GameObject other);
isAboveOf(GameObject other);
isBelowOf(GameObject other);
public abstract boolean touches(GameObject other);
}
S. 2 / 14
Hochschule Niederrhein
Fachbereich 03
Bachelor Informatik
Grundlagen der
Java Programmierung
SS 2015
Prof. Dr. Nitsche
Übung 7 – Beispiellösung
games.basic.gameObjects.RectangularGameObject.java:
package games.basic.gameObjects;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Stroke;
import games.basic.position.interfaces.Positionable;
public class RectangularGameObject extends AbstractGameObject {
private int width;
private int height;
private Color color;
boolean filled;
final static float STROKE = 3.0f;
// Strichdicke
// Konstruktor
public RectangularGameObject(Positionable pos, int width, int height,
Color color, boolean filled) {
super(pos);
// Aufruf: AbstractGameObject(pos);
this.width = width;
this.height = height;
this.color = color;
this.filled = filled;
}
@Override
public int getWidth() {
return this.width;
}
@Override
public int getHeight() {
return this.height;
}
@Override
public void paintComponent(Graphics g) {
Color oldColor = g.getColor(); // alte Farbeeinstellung sichern
// Rechteck zeichnen
g.setColor(color);
if (filled) {
g.fillRect( getPos().getX(), getPos().getY(),
width, height);
} else {
Graphics2D g2d = (Graphics2D) g;
Stroke oldStroke = g2d.getStroke();
// alte Strichdicke sichern
g2d.setStroke( new BasicStroke(STROKE) );
g.drawRect( getPos().getX(), getPos().getY(),
width, height);
g2d.setStroke( oldStroke );
// Strichdicke wiederherstellen
S. 3 / 14
Hochschule Niederrhein
Fachbereich 03
Bachelor Informatik
Grundlagen der
Java Programmierung
SS 2015
Prof. Dr. Nitsche
Übung 7 – Beispiellösung
}
g.setColor(oldColor);
// Farbe wiederherstellen
}
public String toString() {
// verwende toString-Methode aus Positionable
return ("pos = " + this.getPos()
+ ", size = " + this.getWidth()
+ " x " + this.getHeight()+ ")");
}
public boolean equals(Object other) {
if (other == null || !(other instanceof RectangularGameObject))
return false;
RectangularGameObject otherRect = (RectangularGameObject)other;
Positionable thisPos = this.getPos();
// Beachte: this.pos funktioniert nicht, da Attribut pos
//
in Oberklasse private ist!
//
--> getPos() funktioniert, egal ob
//
Attribut pos hier oder in Oberklasse
//
definiert ist
Positionable otherPos = otherRect.getPos();
return
// vergleiche aktuelle Position
//
verwende equals-Methode von Positionable
thisPos.equals( otherPos )
// vergleiche Breite/Höhe
&& this.getWidth() == otherRect.getWidth()
&& this.getHeight() == otherRect.getHeight()
&& this.color == otherRect.color
&& this.filled == otherRect.filled;
}
}
games.basic.gameObjects.CircularGameObject.java:
package games.basic.gameObjects;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Stroke;
import games.basic.position.interfaces.Positionable;
public class CircularGameObject extends AbstractGameObject {
private int radius;
private Color color;
private boolean filled;
final static float STROKE = 3.0f;
// Strichdicke
// Konstruktor
public CircularGameObject(Positionable pos, int radius,
Color color, boolean filled) {
super(pos);
// Aufruf: AbstractGameObject(pos);
S. 4 / 14
Hochschule Niederrhein
Fachbereich 03
Bachelor Informatik
Grundlagen der
Java Programmierung
SS 2015
Prof. Dr. Nitsche
Übung 7 – Beispiellösung
this.radius = radius;
this.color = color;
this.filled = filled;
}
@Override
public int getWidth() {
return 2 * this.radius;
}
@Override
public int getHeight() {
return 2 * this.radius;
}
@Override
public void paintComponent(Graphics g) {
Color oldColor = g.getColor(); // alte Farbeeinstellung sichern
// Kreis zeichnen
g.setColor(color);
if (filled) {
g.fillOval( getPos().getX(), getPos().getY(),
2 * radius, 2 * radius);
} else {
Graphics2D g2d = (Graphics2D) g;
Stroke oldStroke = g2d.getStroke();
// alte Strichdicke sichern
g2d.setStroke( new BasicStroke(STROKE) );
g.drawOval( getPos().getX(), getPos().getY(),
2 * radius, 2 * radius);
g2d.setStroke( oldStroke );
// Strichdicke wiederherstellen
}
g.setColor(oldColor);
// Farbe wiederherstellen
}
public String toString() {
// verwende toString-Methode aus Positionable
return ("pos = " + this.getPos()
+ ", radius = " + this.radius + ")");
}
public boolean equals(Object other) {
if (other == null || !(other instanceof CircularGameObject))
return false;
CircularGameObject otherCirc = (CircularGameObject)other;
// Beachte: this.pos funktioniert nicht, da Attribut pos
//
in Oberklasse private ist!
//
--> getPos() funktioniert, egal ob
//
Attribut pos hier oder in Oberklasse
//
definiert ist
Positionable thisPos = this.getPos();
Positionable otherPos = otherCirc.getPos();
S. 5 / 14
Hochschule Niederrhein
Fachbereich 03
Bachelor Informatik
return
Grundlagen der
Java Programmierung
SS 2015
Prof. Dr. Nitsche
Übung 7 – Beispiellösung
// vergleiche aktuelle Position
// verwende equals-Methode von Positionable
thisPos.equals( otherPos )
// vergleiche Breite/Höhe
&& this.radius == otherCirc.radius
&& this.color == otherCirc.color
&& this.filled == otherCirc.filled;
}
}
games.basic.gameObjects.CrossGameObject.java:
package games.basic.gameObjects;
import games.basic.position.interfaces.Positionable;
import
import
import
import
import
java.awt.BasicStroke;
java.awt.Color;
java.awt.Graphics;
java.awt.Graphics2D;
java.awt.Stroke;
public class CrossGameObject extends AbstractGameObject {
private int width;
private int height;
private Color color;
boolean filled;
final static float STROKE = 3.0f;
// Strichdicke
public CrossGameObject(Positionable pos, int width, int height,
Color color, boolean filled) {
super(pos);
this.width = width;
this.height = height;
this.color = color;
this.filled = filled;
}
@Override
public int getWidth() {
return this.width;
}
@Override
public int getHeight() {
return this.height;
}
@Override
public void paintComponent(Graphics g) {
Color oldColor = g.getColor(); // alte Farbeeinstellung sichern
// Kreuz zeichnen
g.setColor(color);
if (filled) {
S. 6 / 14
Hochschule Niederrhein
Fachbereich 03
Bachelor Informatik
Grundlagen der
Java Programmierung
SS 2015
Prof. Dr. Nitsche
Übung 7 – Beispiellösung
g.fillRect( getPos().getX(), getPos().getY(),
width, height);
} else {
Graphics2D g2d = (Graphics2D) g;
Stroke oldStroke = g2d.getStroke();
// alte Strichdicke sichern
g2d.setStroke( new BasicStroke(STROKE) );
g.drawLine( getPos().getX(), getPos().getY(),
getPos().getX() + getWidth(),
getPos().getY() + getHeight());
g.drawLine( getPos().getX() + getWidth(),
getPos().getY(), getPos().getX(),
getPos().getY() + getHeight());
g2d.setStroke( oldStroke );
// Strichdicke wiederherstellen
}
g.setColor(oldColor);
// Farbe wiederherstellen
}
@Override
public String toString() {
return ("pos = (" + this.getPos().getX()
+ ", " + this.getPos().getY()
+ ", size = " + this.getWidth()
+ " x " + this.getHeight()+ ")");
}
@Override
public boolean equals(Object other) {
if (other == null || !(other instanceof CrossGameObject))
return false;
CrossGameObject otherCross = (CrossGameObject)other;
Positionable thisPos = this.getPos();
Positionable otherPos = otherCross.getPos();
return (thisPos.getX() == otherPos.getX())
&& (thisPos.getY() == otherPos.getY())
&& this.getWidth() == otherCross.getWidth()
&& this.getHeight() == otherCross.getHeight()
&& this.color == otherCross.color
&& this.filled == otherCross.filled;
}
}
Aufgabe 2: Grafik für Tic Tac Toe
Tic Tac Toe (auch „Drei gewinnt“ oder „Kreis und Kreuz“) ist ein Spiel für zwei Personen.
Dabei machen auf einem 3×3 Felder großen Spielfeld die beiden Spieler abwechselnd ihre
Zeichen (ein Spieler Kreuze, der andere Kreise). Der Spieler, der als erstes drei seiner Zeichen
in eine Reihe, Spalte oder eine der beiden Hauptdiagonalen setzen kann, gewinnt. [wikipedia]
S. 7 / 14
Hochschule Niederrhein
Fachbereich 03
Bachelor Informatik
Grundlagen der
Java Programmierung
SS 2015
Prof. Dr. Nitsche
Übung 7 – Beispiellösung
Implementieren Sie die grafische Darstellung für das Spiel Tic Tac Toe. Dabei sollen geeignete Spielobjekte jeweils (rote) Kreuze sowie (blaue) Kreise zur Darstellung der Züge der
beiden Gegner verwendet werden.
a) Erstellen Sie eine von JPanel abgeleitete Klasse TicPanel im Paket
games.examples.ticTacToe.
•
Übergeben Sie der Klasse eine Liste von darzustellenden Spielobjekten, d.h. von
Objekten vom Typ games.basic.gameObjects.interfaces.GameObject.
Hinweis: Für Listen bietet Java u.a. die generische Klasse ArrayList
(java.util.ArrayList<GameObject>, siehe
http://docs.oracle.com/javase/7/docs/api/index.html?java/util/ArrayList.html).
Diese besitzt u.a. die folgenden für diese Aufgabe relevanten Funktionen:
• new ArrayList<GameObject>() erzeugt eine leere Liste von
GameObject-Objekten.
• boolean add(GameObject elem) fügt ein Element zur Liste
hinzu
• for (GameObject elem : liste) { … } iteriert über alle
Elemente einer Liste von GameObject-Elementen.
Weitere wichtige Methoden aus ArrayList sind u.a.
• GameObject get(int index) liest das Element an Position index
aus
• int size() liefert die Anzahl der Elemente in der Liste
• boolean remove(GameObject elem) löscht das Element aus der
Liste, sofern es enthalten ist.
•
Überschreiben Sie dann die Methode
public void paintComponent(Graphics g)
und zeichne Sie dort zunächst die Trennlinen für das 3 x 3-Feld .
Zeichnen Sie ferner in das übergebene Graphics-Objekt g alle darzustellenden
S. 8 / 14
Hochschule Niederrhein
Fachbereich 03
Bachelor Informatik
Grundlagen der
Java Programmierung
SS 2015
Prof. Dr. Nitsche
Übung 7 – Beispiellösung
Spielobjekte (aus der ArrayList).
b) Erzeugen Sie schließlich (in einer Klasse games.examples.ticTacToe
.TicTacToe) ein JFrame-Fenster, fügen Sie ein Objekt der Klasse TicPanel zu
diesem JFrame hinzu und zeigen Sie das Fenster auf dem Bildschirm an.
c) Erzeugen Sie zum Testen eine Liste von Spielobjekten (Kreuze und Kreise), welche Sie
sich auf dem Panel darstellen lassen.
Hinweis: Zum Neuzeichnen des Fensters können Sie die Methode
frame.repaint()
aufrufen.
TicTacToe
TicPanel
objListe : ArrayList<GameObject>
main(String[] args) {
Jframe frame = …
…
TicPanel panel =
new TicPanel( objListe);
…
}
+paintComponent(Graphics g)
GameObject
+paintComponent(Graphics g)
AbstractGameObject
-pos: Positionable
CrossGameObject
CircularGameObject
-width, height: int
-color: java.awt.Color
-radius: int
-color: java.awt.Color
-filled: boolean
+paintComponent(Graphics g)
+paintComponent(Graphics g)
d) Nicht ausgelastet? Lassen Sie beim Zeichnen der Kreise und Kreuze einen Abstand
zum Rand des jeweiligen Feldes, d.h. zu den Rahmenlinien des 3x3-Feldes.
S. 9 / 14
Hochschule Niederrhein
Fachbereich 03
Bachelor Informatik
Grundlagen der
Java Programmierung
SS 2015
Prof. Dr. Nitsche
Übung 7 – Beispiellösung
games.examples.ticTacToe.Cross.java:
package games.examples.ticTacToe;
import java.awt.Color;
import games.basic.gameObjects.CrossGameObject;
import games.basic.position.interfaces.Positionable;
import games.basic.position.Position;
public class Cross extends CrossGameObject {
public static final Color CROSS_COLOR = Color.red;
private static final int GAP = TicTacToe.GAP;
public Cross(int x, int y, int size) {
super(new Position(x+GAP, y+GAP),
size - GAP*2, size - GAP*2,
CROSS_COLOR, false);
}
}
games.examples.ticTacToe.Dot.java:
package games.examples.ticTacToe;
import games.basic.gameObjects.CircularGameObject;
import games.basic.position.interfaces.Positionable;
import games.basic.position.Position;
import java.awt.Color;
import java.awt.Graphics;
public class Dot extends CircularGameObject {
public static final Color CIRC_COLOR = Color.blue;
private static final int GAP = TicTacToe.GAP;
public Dot(int x, int y, int size) {
super(new Position(x+GAP, y+GAP),
size - GAP*2, CIRC_COLOR, false);
}
}
games.examples.ticTacToe.TicPanel.java:
package games.examples.ticTacToe;
import java.util.ArrayList;
import javax.swing.JPanel;
S. 10 / 14
Hochschule Niederrhein
Fachbereich 03
Bachelor Informatik
Grundlagen der
Java Programmierung
SS 2015
Prof. Dr. Nitsche
Übung 7 – Beispiellösung
import java.awt.Color;
import java.awt.Graphics;
import games.basic.gameObjects.interfaces.*;
class TicPanel extends JPanel {
private ArrayList<GameObject> components;
private int componentSize;
public TicPanel(int componentSize, ArrayList<GameObject> components)
{
this.componentSize = componentSize;
this.components = components;
}
@Override
public void paintComponent(Graphics g) {
// Zeichne Trennlinien für 3x3-Feld
g.setColor(Color.black);
g.drawLine(componentSize, 0, componentSize, componentSize * 3);
g.drawLine(componentSize * 2, 0,
componentSize * 2, componentSize * 3);
g.drawLine(0, componentSize, componentSize * 3, componentSize);
g.drawLine(0, componentSize * 2,
componentSize * 3, componentSize * 2);
// Zeichne Komponenten
for (GameObject component : components) {
component.paintComponent(g);
}
}
} // class TicPanel
games.examples.ticTacToe.TicTacToe.java:
package games.examples.ticTacToe;
import games.basic.gameObjects.interfaces.GameObject;
import
import
import
import
java.awt.Color;
java.awt.Graphics;
java.util.ArrayList;
java.util.Scanner;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class TicTacToe {
public static final int GAP = 5;
private ArrayList<GameObject> components =
new ArrayList<GameObject>();
private int componentSize = 60;
private int xframesize = componentSize*3 + 10;
S. 11 / 14
Hochschule Niederrhein
Fachbereich 03
Bachelor Informatik
Grundlagen der
Java Programmierung
SS 2015
Prof. Dr. Nitsche
Übung 7 – Beispiellösung
private int yframesize = componentSize*3 + 30;
JFrame frame;
public static void main(String[] args) {
TicTacToe prog = new TicTacToe();
prog.run();
}
public void run() {
frame = new JFrame("Tic Tac Toe");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
TicPanel panel = new TicPanel();
frame.add( panel );
frame.setSize(xframesize, yframesize);
frame.setVisible(true);
play();
}
public void play() {
Dot meinZug1 = new Dot(0, 0, componentSize);
Cross gegnerZug1 =
new Cross(componentSize, componentSize, componentSize);
components.add(meinZug1);
components.add(gegnerZug1);
frame.repaint();
Dot meinZug2 = new Dot(0, componentSize*2, componentSize);
Cross gegnerZug2 =
new Cross(componentSize*2, componentSize*2,
componentSize);
components.add(meinZug2);
components.add(gegnerZug2);
frame.repaint();
Scanner scanner = new Scanner(System.in);
System.out.print("x = ");
int x = scanner.nextInt();
System.out.print("y = ");
int y = scanner.nextInt();
Dot meinZug =
new Dot(componentSize * x, componentSize * y,
componentSize);
components.add(meinZug);
frame.repaint();
}
class TicPanel extends JPanel {
@Override
public void paintComponent(Graphics g) {
// Zeichne Trennlinien für 3x3-Feld
g.setColor(Color.black);
S. 12 / 14
Hochschule Niederrhein
Fachbereich 03
Bachelor Informatik
Grundlagen der
Java Programmierung
SS 2015
Prof. Dr. Nitsche
Übung 7 – Beispiellösung
g.drawLine(componentSize, 0,
componentSize, componentSize * 3);
g.drawLine(componentSize * 2, 0,
componentSize * 2, componentSize * 3);
g.drawLine(0, componentSize,
componentSize * 3, componentSize);
g.drawLine(0, componentSize * 2,
componentSize * 3, componentSize * 2);
// Zeichne Komponenten
for (GameObject component : components) {
component.paintComponent(g);
}
}
} // inner class TicPanel
}
Zusatz-Aufgabe 3: Spielsteuerung für Tic Tac Toe
Erweitern Sie die Klasse TicTacToe um die Spielsteuerung für das Spiel TicTacToe.
Dazu können Sie von Tastatur jeweils das Feld einlesen und abwechselnd (für jeden Spieler)
Kreuze bzw. Punkte in dieses Feld setzen. Wer alle Felder einer Zeile, Spalte oder Diagonale
besetzt hat, hat das Spiel gewonnen.
S. 13 / 14
Hochschule Niederrhein
Fachbereich 03
Bachelor Informatik
Grundlagen der
Java Programmierung
SS 2015
Prof. Dr. Nitsche
Übung 7 – Beispiellösung
Zusatz-Aufgabe 4 (für Java-Experten): Java-Rätsel
Was gibt das folgende Java-Programm aus – und warum verhält es sich so seltsam?
package click;
package hack;
import click.CodeTalk;
public class CodeTalk {
public void doIt() {
printMessage();
}
void printMessage() {
System.out.println("Click");
}
public class TypeIt {
private static class ClickIt extends CodeTalk {
void printMessage() {
System.out.println("Hack");
}
}
public static void main(String[] args) {
ClickIt clickit = new ClickIt();
clickit.doIt();
}
}
}
Ausgabe:
Click
Lösung:
•
•
•
•
•
Die Methode printMessage ist nicht public, sondern (ohne expliziten
Sichtbarkeitsmodifizierer) nur innerhalb des jeweiligen Paketes sichtbar.
Die Klassen CodeTalk und dessen Unterklasse ClickIt (als innere Klasse von
TypeIt) liegen jedoch in verschiedenen Paketen (click bzw. hack).
Folglich ist die Methode printMessage in der Klasse ClickIt nicht
überschrieben (da die geerbte Methode nicht sichtbar ist, handelt es sich hier aus
Sicht des Aufrufers um eine neue Methode!).
Die Methode doIt verwendet deshalb die Methode printMessage aus
CodeTalk (mit der Ausgabe „Click“) und nicht die Methode aus der Unterklasse
ClickIt (mit der Ausgabe „Hack“).
Ausgabe: Click (und nicht wie vielleicht vermutet Hack)
S. 14 / 14
Herunterladen