Problem beim Zeichnen



  • Hallo Leute

    ich habe ein kleines Problem mit dem Graphics Sachen. Es geht um einen Risiko-Klon. Ich benutze zwei direkt übereinander liegende Panels zum zeichnen. Das hintere zeichnet nur die Landkarte. Das Vordere zeichnet informationen zu den Ländern und beim anklicken eines Landes animierte Pfeile zu den Nachbarländern. Abgesehen davon muss der Rest aber durchsichtig bleiben. Wenn die Pfeilanimation jetzt läuft, sieht man die ganze Zeit über die zwischendurch gezeichneten Bilder (ganz viele Pfeilspitzen) erst wenn die animation beendet ist verschwinden diese. Ich habe schon mehrfach versucht irgendwie das Bild zu leeren aber alles was ich erreiche ist das gesamte füllen mit einer Farbe wie es clear() macht. Ich hab schon versucht die Farbe auf Null zu setzen und einen Alpha wert von 0 anzugeben aber nichts hat geklappt. Kennt einer von euch ne möglichkeit wie ich das Bild wieder komplett leer bekomme?

    meine Zeichenmethode sieht so aus:

    @Override
    	public void paint(Graphics g) {
    		Graphics2D g2 = (Graphics2D)g;
    		g2.setStroke(new BasicStroke(10f));
    		g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    		for(Landrepresentation landRep : this.landrepresentations) {
    			Point offset = landRep.getOffset();
    			Land land = landRep.getLand();
    			int armeeCount = land.getArmeeCount();
    			Color playerColor = this.getColorByPlayer(land.getOwner());
    			g.setColor(playerColor);
    			g2.fillOval(offset.x, offset.y, 20, 20);
    			g.setColor(Color.WHITE);
    			g.drawString("" + armeeCount,
    							offset.x + 4,
    							offset.y + 14);
    			if(land == this.selectedland 
    					&& land.getOwner() == Game.getInstance().getCurrentPlayer()) {
    				for (Land neighbor : land.getNeighborLands()) {
    					if(neighbor.getOwner() == land.getOwner()) {
    						g.setColor(Color.BLUE);
    					}
    					else {
    						g.setColor(Color.RED);
    					}
    					Point neighborOffset = this.getOffsetForLand(neighbor);
    
    					Point lineStart = new Point(0, 0);
    					Point lineEnd = new Point(0, 0);
    					Point origin = new Point(0, 0); 
    
    					Point2D.Double direction = new Point2D.Double(offset.x - neighborOffset.x,
    													offset.y - neighborOffset.y);
    					double length = origin.distance(direction);
    					direction.x /= length;
    					direction.y /= length;
    
    					Point animationEnd = new Point(neighborOffset.x + (int)(direction.x * 25) + 10,
    												   neighborOffset.y + (int)(direction.y * 25) + 10);
    
    					lineStart.x = offset.x - (int)(direction.x * 20) + 10;
    					lineStart.y = offset.y - (int)(direction.y * 20) + 10;
    
    					double directionFactor = (lineStart.distance(animationEnd) / 2000) * this.animationRuntime;
    
    					lineEnd.x = offset.x - (int)(direction.x * directionFactor) + 10;
    					lineEnd.y = offset.y - (int)(direction.y * directionFactor) + 10;
    
    					Point2D.Double ortho = new Point2D.Double();
    					ortho.x = direction.y;
    					ortho.y = -direction.x;
    
    					Point triangle1 = new Point((int)(lineEnd.x - 3 * direction.x + 10 * ortho.x),
    												(int)(lineEnd.y - 3 * direction.y + 10 * ortho.y));
    					Point triangle2 = new Point((int)(lineEnd.x - 3 * direction.x - 10 * ortho.x),
    												(int)(lineEnd.y - 3 * direction.y - 10 * ortho.y));
    
    					Polygon triangle = new Polygon();
    					triangle.addPoint((int)(lineEnd.x - 20 * direction.x), 
    									  (int)(lineEnd.y - 20 * direction.y));
    					triangle.addPoint(triangle1.x, triangle1.y);
    					triangle.addPoint(triangle2.x, triangle2.y);
    
    					g2.draw(new Line2D.Double(lineStart, lineEnd));
    					g2.fill(triangle);
    				}
    			}
    		}
    	}
    


  • Ohne das ich jetzt einen genaueren Blick auf deinen Code geworfen hab, kannst du einfach die repaint() Methode ausführen und dann in der paint() Methode einfach nichts zeichnen. Also ungefähr so:

    public class Zeichenflaeche extends Component {
    
    private boolean clear = false;
    
    public void clearAll() {
    this.clear = true;
    this.repain();
    }
    
    public void draw() {
    this.clear = false;
    }
    
    public void paint(Graphics g) {
    if(clear) return;
    else {
    // hier wird dann gezeichnet ...
    }
    }
    


  • Mir erscheint der Ansatz insgesamt fragwürdig. Würde man sowas normalerweise nicht mit Double-Buffering lösen, indem man pro Frame zunächst den Hintergrund und dann die einzelnen Elemente darauf zeichnet?

    Das geht sehr komfortabel mit AWT's BufferStrategy:
    http://java.sun.com/docs/books/tutorial/extra/fullscreen/bufferstrategy.html

    Ich arbeite derzeit auch an einem kleinen Framework, das davon Gebrauch macht.

    Edit:

    Michamab schrieb:

    Ohne das ich jetzt einen genaueren Blick auf deinen Code geworfen hab, kannst du einfach die repaint() Methode ausführen und dann in der paint() Methode einfach nichts zeichnen. [...]

    Das funktioniert IMHO nicht, weil die alten Bildbereiche dabei nicht überschrieben werden.



  • @Dasd:
    Ich sollte vielleicht noch dazu sagen das ich ein JPanel benutze kein awt.Panel und das auf doubleBuffered = true gesetzt habe.
    Am Anfang hab ich alles in ein Panel gezeichnet. Das Problem daran war, dass das Hintergrundbild an 2000 X 2000 Pixel groß ist, wenn ich das mitzeichne schaff ichs auf meinem schon recht alten Notebook grade mal auf 2-3 Bilder in der 2 Sekunden Animation. Deswegen versuch ich das ja auszulagern.



  • Wenn du besser/schneller zum Ziel kommen willst, kann ich dir http://www.piccolo2d.org/ empfehlen. Ist ein 2D Szenegraph. Oder https://scenegraph.dev.java.net/ ist aber unter der GPL.



  • Niemals paint() überschreiben! Immer paintComponent() ⚠



  • byto schrieb:

    Niemals paint() überschreiben! Immer paintComponent() ⚠

    Das stimmt im Fall von JComponent zwar, paintComponent() existiert in der Klasse Component allerdings nicht.

    Dasd schrieb:

    Edit:

    Michamab schrieb:

    Ohne das ich jetzt einen genaueren Blick auf deinen Code geworfen hab, kannst du einfach die repaint() Methode ausführen und dann in der paint() Methode einfach nichts zeichnen. [...]

    Das funktioniert IMHO nicht, weil die alten Bildbereiche dabei nicht überschrieben werden.

    Das funktioniert sehr wohl, weil ja durch den Aufruf von repaint() wieder paint() aufgerufen wird und alles komplett neu gezeichnet wird.



  • Da ich ein JPanel habe hätte ich paintComponent überschreiben können und ich weis auch dass man das so macht. Da das Jpanel aber eh nichts anderes tut als zeichnen und vorallem niemals irgendwelche Components beinhalten wird, wars mir schlicht und einfach egal.

    @Michamab:
    Das wird nicht funktionieren da ich nur kleine Bereiche des Bildes zeichne und nicht das ganze, da der Rest ja durchsichtig bleiben muss.

    @DEvent:
    Ich wollte hier eigentlich nur auf Java-Bordmittel zurückgreifen aber ich werd mir das mal ansehen. Danke.


Anmelden zum Antworten