Java/Tutorials/Karteneditor Kapitel 5

Aus Scientia
Wechseln zu: Navigation, Suche

In diesen Kapitel behandeln wir wie wir die Karte im Fenster anzeigen lassen. Hierfür erstellen wir eine neue Klasse KartenAnsicht und leiten diese von JPanel ab. Wir übergeben wieder unser KartenFenster im Konstruktor, so dass wir auf die Karte zugreifen können. Weiterhin werden wir wieder die paintComponent()-Methode implementieren. Wir werden auch DoubleBuffered() aktivieren um flackern zu verhindern.


public class KartenAnsicht extends JPanel{
 
	KartenFenster fenster;
 
	public KartenAnsicht(KartenFenster kfenster)
	{
		fenster=kfenster;
		//verhindert Flackern
		setDoubleBuffered(true);
	}
 
	public void paintComponent(Graphics g)
	{
 
	}
}

Kommen wir nun zu einer Swing-Komponente die wir unbedingt brauchen: JScrollPane. Wir wissen, dass unsere Karten sehr groß wird und wenn wir alles auf einmal zeichnen lassen würden, würde unnötig Rechenleistung verbraucht. Deswegen gibt es genau zwei Gründe warum wir JScrollPane verwenden sollten. Erstens um uns auf unserer Karte mit Scrollbars zu bewegen und zweitens um Rechenleistung zu sparen. Über die Methode setViewport, sagen wir was wir betrachten wollen. In unseren Fall wollen wir das unsere Klasse selbst die zu betrachtende Komponente ist.


public class KartenAnsicht extends JPanel{
 
	KartenFenster fenster;
	JScrollPane scroll=new JScrollPane();
 
	public KartenAnsicht(KartenFenster kfenster)
	{
		fenster=kfenster;
		scroll.setViewportView(this);
		//verhindert Flackern
		setDoubleBuffered(true);
	}
 
	public void paintComponent(Graphics g)
	{
 
	}
}


Nun brauchen wir noch den aktuellen Ausschnitt der Karte. Den bekommen wir mit der Methode getClipBounds().

public class KartenAnsicht extends JPanel{
 
	KartenFenster fenster;
	JScrollPane scroll=new JScrollPane();
 
	public KartenAnsicht(KartenFenster kfenster)
	{
		scroll.setViewportView(this);
		fenster=kfenster;
		//verhindert Flackern
		setDoubleBuffered(true);
	}
 
	public void paintComponent(Graphics g)
	{
		Rectangle r=g.getClipBounds();
	}
}

Die Klasse Rectangle beschreibt ein Rechteck. Nun wollen wir anfangen zu zeichnen. Wir benötigen die Position der linken oberen Ecke, da wir nicht die komplette Karte zeichnen wollen, sondern nur einen Teil. Würden wir die komplette Karten zeichnen lassen, wäre das sehr rechenintensiv. Die Klasse Rectangle stellt uns diese Variablen alle zur Verfügung. Zuerst holen wir uns die Koordinaten x und y der linken oberen Ecke. Anschließend holen wir uns die Breite (width) und die Höhe (height). Diese verrechnen wir anschließend mit x und y, so dass wir am Ende zwei Punkte haben. Zusätzlich rechnen wir noch 1 Wert drauf. Dies machen wir um eine halbe Tile neben dem Scrollbalken anzuzeigen. Da unser JScrollPane keine feste Größe hat und wir nicht nur das letzte Tile anzeigen lassen wollen, sondern auch den Anfang des nächsten Tile.

public class KartenAnsicht extends JPanel{
 
	KartenFenster fenster;
	JScrollPane scroll=new JScrollPane();
 
	public KartenAnsicht(KartenFenster kfenster)
	{
		scroll.setViewportView(this);
		fenster=kfenster;
		//verhindert Flackern
		setDoubleBuffered(true);
	}
 
	public void paintComponent(Graphics g)
	{
		Rectangle r=g.getClipBounds();
		int startx=r.x;
		int starty=r.y;
		int endx=startx+r.width;
		int endy=starty+r.height;
 
		startx=startx/32;
		starty=starty/32;
		endx=endx/32;
		endy=endy/32;
		if(endx < fenster.aktuelleKarte.karte.length)
		{
			endx++;
		}
 
		if(endy < fenster.aktuelleKarte.karte[0].length)
		{
			endy++;
		}
	}
}

Diese Teilen wir durch die Tilegröße, also 32, so dass wir mit ihrer Hilfe eine For-Schleife durchlaufen können. Als Startwert geben wir nicht 0, sondern startx an, so dass nicht die komplette Karte gezeichnet wird, sondern nur ein Teilausschnitt. Über die Klassevariable fenster holen wir uns unsere Karte und wir können die Methode getTileImage verwenden um uns ein Tile auf der Karte zu zeichnen. Anschließend zeichnen wir das Tile in unserer Ansicht.

public class KartenAnsicht extends JPanel{
 
	KartenFenster fenster;
	JScrollPane scroll=new JScrollPane();
 
	public KartenAnsicht(KartenFenster kfenster)
	{
		scroll.setViewportView(this);
		fenster=kfenster;
		//verhindert Flackern
		setDoubleBuffered(true);
	}
 
	public void paintComponent(Graphics g)
	{
		Rectangle r=g.getClipBounds();
		int startx=r.x;
		int starty=r.y;
		int endx=startx+r.width;
		int endy=starty+r.height;
 
		startx=startx/32;
		starty=starty/32;
		endx=endx/32;
		endy=endy/32;
		if(endx < fenster.aktuelleKarte.karte.length)
		{
			endx++;
		}
 
		if(endy < fenster.aktuelleKarte.karte[0].length)
		{
			endy++;
		}
 
		for(int x=startx;x < endx;x++)
		{
			for(int y=starty;y < endy;y++){
				BufferedImage tile=fenster.aktuelleKarte.getTileImage(x, y);
				g.drawImage(tile, x*32, y*32, this);
			}
		}
	}
}

Nun fehlt nur noch, dass wir unserem JPanel eine feste Größe geben, so dass man mit unser JScrollPane auch richtig scrollen kann. Diese verpacken wir in eine extra Methode, so das wir später wenn wir die Karte wechseln, die Größe schnell ändern können. Diese Methode nennen wir changeKarte(). In dieser holen wir uns die Breite und Höhe des Karten Integer-Array und mutliplizieren dies mit unserer Pixelgröße, also 32, und verwenden wieder die Methode setPreferredSize(). Nun setzen wir noch scroll.setViewportView(this), den Viewport erneut, damit der ScrollPanel alle Änderungen an der Größe des JPanels mitbekommt.

public class KartenAnsicht extends JPanel{
 
	KartenFenster fenster;
	JScrollPane scroll=new JScrollPane();
 
	public KartenAnsicht(KartenFenster kfenster)
	{
		scroll.setViewportView(this);
		fenster=kfenster;
		//verhindert Flackern
		setDoubleBuffered(true);
		changeKarte();
	}
 
	public void paintComponent(Graphics g)
	{
		Rectangle r=g.getClipBounds();
		int startx=r.x;
		int starty=r.y;
		int endx=startx+r.width;
		int endy=starty+r.height;
 
		startx=startx/32;
		starty=starty/32;
		endx=endx/32;
		endy=endy/32;
		if(endx < fenster.aktuelleKarte.karte.length)
		{
			endx++;
		}
 
		if(endy < fenster.aktuelleKarte.karte[0].length)
		{
			endy++;
		}
 
		for(int x=startx;x < endx;x++)
		{
			for(int y=starty;y < endy;y++){
				BufferedImage tile=fenster.aktuelleKarte.getTileImage(x, y);
				g.drawImage(tile, x*32, y*32, this);
			}
		}
	}
 
	public void changeKarte()
	{
		//Hier wird nun eine Feste größe des JPanel gesetzt.
		int dx=fenster.aktuelleKarte.karte.length;
		int dy=fenster.aktuelleKarte.karte[0].length;
		setPreferredSize(new Dimension(dx*32,dy*32));
		scroll.setViewportView(this);
	}
}


Nun wollen wir uns doch mal unsere Karten Anzeigen lassen. Darum erweitern wir unsere Klasse KartenFenster. Wir legen eine Klassenvariable vom Typ KartenAnsicht an. Anschließend initialisieren wir unsere Klassenvariable wieder nach der Initialiserung von unseren aktuellen Karte und fügen unsere Ansicht dem Fenster in der Mitte(BorderLayout.CENTER) hinzu. Dabei ist zu beachten, dass wir nicht unsere Klassenvariable übergeben, sondern unsere JScrollPane. Daher schreiben wir in der Methode add() nicht ansicht sondern ansicht.scroll.

public class KartenFenster extends JFrame{
 
	Karte aktuelleKarte;
	TilePalette palette;
	KartenAnsicht ansicht;
 
	public KartenFenster() {
 
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		this.requestFocus();
		int karte[][]=new int[100][100];
		String name="Unser erste Karte";
		String tilesetDateiname="Tileset.png";
		aktuelleKarte=new Karte(karte, tilesetDateiname, name);
		palette=new TilePalette(this);
		//Initialisere KartenAnsicht
		ansicht=new KartenAnsicht(this);
		setLayout(new BorderLayout());
 
		add(palette, BorderLayout.WEST);
		add(ansicht.scroll , BorderLayout.CENTER);
		//größe des Fensters setzen
		setSize(1024, 768);
		//Setzen eines Fenstertitels
		setTitle("Karteneditor");
	}
}

Von nun an ist es nicht mehr sehr weit zu unserem fertigen Karteneditor.

Und mal wieder zum Vergleich:

Karteneditor-KartenAnsicht.png

zurück Kapitel 4 - Index - weiter Kapitel 6