Java/Tutorials/Karteneditor Kapitel 6
| Entwicklung eines einfachen Karteneditors | |
|---|---|
| Autor | |
| Thematik | Java |
| Vorraussetzungen | Programme: Fähigkeiten: |
| Andere Teile |
Material |
Nun kommen wir zum Zeichnen unserer Tiles. Wir erweitern unsere Klasse KartenAnsicht um die Methode zeichneTile(x,y), da wir als Übergabewerte die Mausposition im JPanel erhalten, müssen wir diese durch 32 Teilen um rauszufinden wo wir gerade in unserem IntegerArray geklickt haben. Nun holen wir uns mit fenster.aktuelleKarte.karte[x][y] die aktuelle Kartenposition und setzen die aktuelle TileID über unsere Klassenvariable von unserer Paletten-Klasse.
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); } public void zeichneTile(int x, int y) { x=x/32; y=y/32; fenster.aktuelleKarte.karte[x][y]=fenster.palette.aktuelleTile; } }
Nun können wir zwar unser Tile setzen, aber es wird in der Karte nicht aktualisiert. Wir könnten zwar nun ein einfaches repaint(); in unsere Methode zeichneTile() einsetzen, nur muss wirklich der komplette Inhalt neu gezeichnet werden? Natürlich nicht. Es wäre ja auch zu rechenintensiv, da ja jedes Mal der komplette JScrollPane-Inhalt gezeichnet werden muss. Um dies zu umgehen gibt es zwar die Methode repaint(x,y,breite,höhe), nur dies ist eine AWT-Methode und der Bildschirminhalt wird nicht neugezeichnet. Die Swing-Klasse stellt uns aber einen RepaintManager zur Verfügung, womit man Regionen im Fenster neu zeichnen kann. Darum legen wir uns eine Klassenvariable vom Typ RepaintManager an und anschließend holen wir uns den RepaintManager von unsere KartenFenster-Klasse. Die RepaintManager-Klasse gibt es nur für Fensterklassen von Swing, daher nur JFrame, JWindow. Darum müssen wir uns zunächst die Position unserer JScrollPane holen, das unser JPanel beinhaltet. Dies erfolgt mit scroll.getLocation und den x- und y-Wert. Wir benötigen aber auch noch die Höhe der Titelleiste unseres Fensters, sowie den linken Rand des Fensters. Daher holen wir uns diese Angaben mit getInsets(), wobei top für die Titelleiste steht und left die Größe der Umrandung links beinhaltet. Nun benötigen wir nur noch die aktuelle Position in unserem JScrollPane, wenn wir diese haben. Nun addieren wir diese Werte und ziehen die Position des JScrollPane ab und rufen über unseren RepaintManager die Methode addDirtyRegion auf, wo wir dann unser Fenster an der geklickten Position neu zeichnen lassen.
public class KartenAnsicht extends JPanel{ KartenFenster fenster; JScrollPane scroll=new JScrollPane(); RepaintManager m; public KartenAnsicht(KartenFenster kfenster) { scroll.setViewportView(this); fenster=kfenster; //verhindert Flackern setDoubleBuffered(true); changeKarte(); m=RepaintManager.currentManager(fenster); } 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); } public void zeichneTile(int x, int y) { x=x/32; y=y/32; fenster.aktuelleKarte.karte[x][y]=fenster.palette.aktuelleTile; Rectangle r=scroll.getViewport().getViewRect(); int dx=this.scroll.getLocation().x+fenster.getInsets().left-r.x; int dy=this.scroll.getLocation().y+fenster.getInsets().top-r.y; m.addDirtyRegion(fenster , dx+x*32, dy+y*32, 32, 32); } }
Nun kommen wir zu den Mausereignissen. Die Tiles sollen gezeichnet werden wenn wir auf einen Punkt klicken und wenn wir die Maustaste gedrückt halten und über die Karte fahren lassen. Daher fügen wir unserem JPanel noch ein MouseListener und ein MouseMotionListener hinzu, wo wir dann anschließend die Methode zeichneTile aufrufen und mit e.getX() und e.getY() die Position der Maus auf unseren JPanel übermitteln. Da wir nicht alle Mausereignisse verarbeiten müssen, verwenden wir nicht MouseListener sondern MouseAdapter. Dadurch müssen wir nicht alle Mausereignisse implementieren. MouseDragged steht übrigens für das Bewegen der Maus mit gedrückter Maustaste.
public class KartenAnsicht extends JPanel{ KartenFenster fenster; JScrollPane scroll=new JScrollPane(); RepaintManager m; public KartenAnsicht(KartenFenster kfenster) { scroll.setViewportView(this); fenster=kfenster; //verhindert Flackern setDoubleBuffered(true); changeKarte(); m=RepaintManager.currentManager(fenster); addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { zeichneTile(e.getX(), e.getY()); } }); addMouseMotionListener(new MouseMotionAdapter() { @Override public void mouseDragged(MouseEvent e) { zeichneTile(e.getX(), e.getY()); } }); } 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); } public void zeichneTile(int x, int y) { x=x/32; y=y/32; fenster.aktuelleKarte.karte[x][y]=fenster.palette.aktuelleTile; Rectangle r=scroll.getViewport().getViewRect(); int dx=this.scroll.getLocation().x+fenster.getInsets().left-r.x; int dy=this.scroll.getLocation().y+fenster.getInsets().top-r.y; m.addDirtyRegion(fenster , dx+x*32, dy+y*32, 32, 32); } }
Um unseren Editor bereits vorab zu testen, muss man in der Klasse TilePalette die Klassenvariable aktuelleTile von 0 z.B. auf 2 setzen.
class TilePalette extends JPanel{ ... int aktuelleTile=2; ,.. }
Und wenn wir unser Programm starten, können wir nun ein Tile auf unserer Karte verändern.
Wie man das aktuell verwendete Tile ändern kann, folgt im nächsten Kapitel.