# Flackern trotz DoubleBuffering



## moormaster (10. Jan 2007)

Hi,

ich versuche mich gerade daran, einen Ausschnitt eines Feldes aus Objekten zu zeichnen.

Dazu gibt es ein GameObject, welches update und paint Methoden definiert:


```
public class GameObject
 {
   int posX, posY, width, height;
  ...
  public void update(Graphics g)
  {
   if (background != null)
   {
    g.setColor(background);
    g.fillRect(0, 0, width, height);
   }
  
   paint(g);
  }
 
  public void paint(Graphics g)
  {
  }
  ...
 }
```

Davon erbt ein GameImageObject, welches direkt in der Lage ist ein Image zu zeichnen


```
public class GameImageObject extends GameObject
 {
  Image image;
  ...
  public void paint(Graphics g)
  {
   g.drawImage(image, 0, 0, width, height, null);
  }
  ...
 }
```

Nun gibt es noch ein weiteres Objekt, welches sozusagen das Spielbrett darstellt. Es merkt sich eine Menge von GameObject - Instanzen und zeichnet diese, wenn sie sich im Sichtfeld befinden.


```
public class Board extends GameObject
 {
  ...
  public boolean isVisible(Point p)
  {
   if (p.getX() < posX + offX || p.getX() > posX + width + offX)
    return false;
   if (p.getY() < posY + offY || p.getY() > posY + height +offY)
    return false;
    
   return true;
  }
 
  public boolean isVisible(Rectangle r)
  {
   if (isVisible(new Point((int)r.getX(), (int)r.getY())))
    return true;
   if (isVisible(new Point((int)r.getX() + (int)r.getWidth(), (int)r.getY())))
    return true;
   if (isVisible(new Point((int)r.getX(), (int)r.getY() + (int)r.getHeight())))
    return true;
   if (isVisible(new Point((int)r.getX() + (int)r.getWidth(), (int)r.getY() + (int)r.getHeight())))
    return true;
   
   return false;
  }
 
  public boolean isVisible(GameObject go)
  {
   return isVisible(go.getRectangle());
  }
 
  public void paint(Graphics g)
  {
   Graphics gx = g.create(0, 0, width, height);
 
   for (int i=0;i<objects.size();i++)
   {
    GameObject go = objects.get(i);
   
    if (isVisible(go))
     go.paint(gx.create(go.getPosX() - offY, go.getPosY() - offY, go.getWidth(), go.getHeight()));
   }
  }
  ...
 }
```

Das ganze teste ich nun mit einem JFrame, wobei die Instanz von Board eigentlich zunächst in ein BufferedImage zeichnen sollte...



```
public class Frame1 extends JFrame
 {
  Board gameBoard;
  BufferedImage img;
  ...
  public void paint(Graphics g)
  {
   Insets ins = getInsets();
   Graphics gx = g.create(ins.left, ins.top, getWidth() - ins.left - ins.right, getHeight() - ins.top - ins.bottom);
  
   gameBoard.paint(gx);
  }
 
  public void update(Graphics g)
  {
   // DoubleBuffering...
   Graphics gx = img.createGraphics();

   // dieser blaue Hintergrund kommt manchmal flickernd zum Vorschein, obwohl das zeichnen ja eigentlich
   // im BufferedImage stattfindet und beim zeichnen des images das blau gar nicht mehr zu sehen sein dürfte
   gx.setColor(new Color(0,0,255));
   gx.fillRect(0, 0, getWidth(), getHeight());

   paint(gx);
  
   g.drawImage(img, 0, 0, null);
  }
  ...
  public static void main(String[] args)
  {
   ...

   // repaint Thread
   new Thread()
   {
    JFrame frame;
   
    public void run()
    {
     while (true)
      frame.repaint();
    }
   
    public Thread init(JFrame frame)
    {
     this.frame = frame;
    
     return this;
    }
   }.init(test).start();

   // scroll Thread
   new Thread()
   {
    Board board;
   
    public void run()
    {
     long lastTick = System.currentTimeMillis();
     int step = 15;
     
     while (true)
     {
      if (System.currentTimeMillis() - lastTick > step)
      {
       lastTick += step;
      
       int x = board.getOffX();
       int y = board.getOffY();
     
       x++;
       if (x >= 32)
        x = 0;

       y++;
       if (y >= 32)
        y = 0;
       
       board.setOffX(x);
       board.setOffY(y);
      }
     }
    }
   
    public Thread init(Board board)
    {
     this.board = board;
    
     return this;
    }
   }.init(gameBoard).start();
   ...
  }
 }
```

Dort gibt es zwei Threads... einer, der in regelmäßigen Abständen das offset des Boards verändert und so die enthaltenen Objekte scrollt und ein anderer, welcher immer wieder repaint() aufruft.

Wer es mal selbst ausprobieren möchte:

http://www.egoshare.com/7017bd0f997c0add3da67c4c2b0746b8/bombermanzip.html

Am lustigsten finde ich, dass das Programm auf meinem Pentium IV 100% CPU Last verursacht und auf nem Core 2 Duo T5500 gerade 6-7% Auslastung beider Kerne hervorbringt...

Ausserdem finde ich, dass das zeichnen dieser paar Objekte ruhig etwas flüssiger sein könnte... Wo kann man da was verbessern?


----------



## Wildcard (10. Jan 2007)

Das ist kein Applet, sondern eine Applikation.
Das war mal ein Haufen Arbeit umsonst. Wenn du vorher etwas Literatur gelesen hättest, dann wüsstest du das JFrame Swing ist, und Swing bereits doppelt gepuffert ist.
Ich kann dir nur sehr davon abraten update zu überschreiben, das macht man in AWT.
Deine while Schleife musst du auch ändern. Du musst da ein sleep einbauen, sonst verbrätst du zu viele Resourcen.


----------



## moormaster (10. Jan 2007)

Wildcard hat gesagt.:
			
		

> Das ist kein Applet, sondern eine Applikation.



Hab ich im letzten Moment auch noch gemerkt 



> Das war mal ein Haufen Arbeit umsonst. Wenn du vorher etwas Literatur gelesen hättest, dann wüsstest du das JFrame Swing ist, und Swing bereits doppelt gepuffert ist.



Das heisst wenn ich nur paint überschreibe, habe ich automatisch eine Doppelpufferung in Swing?

Kann mir das auch Performance beim Zeichnen klauen, wenn ich in Swing anstatt in AWT zeichne?



> Ich kann dir nur sehr davon abraten update zu überschreiben, das macht man in AWT.
> Deine while Schleife musst du auch ändern. Du musst da ein sleep einbauen, sonst verbrätst du zu viele Resourcen.



Scheint sogar flüssiger zu werden, wenn ich ein Thread.sleep(step) im scroll thread nehme anstatt das über die Systemzeit zu synchronisieren... Der repaint-Thread soll aber durchaus so schnell wie möglich neu zeichnen... da nehme ich dann auch eine hohe Auslastung in Kauf.

Nur komisch, dass der Core 2 Duo sich dabei vorher ausgeschlafen hat und erst jetzt wo ich das Thread.sleep im Scroll-Thread mit drin hat, gibts da auch ne richtige Auslastung


----------



## Wildcard (10. Jan 2007)

Ich empfehle dir nicht paint zu überschreiben, sondern ein JPanel auf den JFrame zu adden und dort paintComponent zu überschreiben.


----------



## moormaster (10. Jan 2007)

Wildcard hat gesagt.:
			
		

> Ich empfehle dir nicht paint zu überschreiben, sondern ein JPanel auf den JFrame zu adden und dort paintComponent zu überschreiben.



Hab auf dem P4 ungefähr die Hälfte CPU Last, wenn ich in ein Frame mit selbst implementiertem DoubleBuffering anstatt in ein JFrame zeichne... Was wäre in diesem Fall Deine Empfehlung, wenn ich nicht paint des Frames überschreiben soll?

Hab es soweit versucht in ein Panel (welches einem Frame hinugefügt wurde) zu zeichnen... nur leider bewirkt [Frame].repaint() nicht, dass das Panel immer wieder neu gezeichnet wird. Statt dessen wird es nur einmal gezeichnet und dann immer nur die Stellen, welche z.B. durch andere Fenster verdeckt wurden.

Des weiteren gibt es im Panel keine paintComponent Methode (die gibts ja nur im JPanel) also musste ich dort die paint Methode überschreiben.


----------



## Wildcard (10. Jan 2007)

Ich hab ja auch JPanel gesagt.
Zum ständigen neuzeichnen kannst du einen Thread der auf dem JPanel repaint aufruft und anschließend x Millisekunden schläft.


----------



## moormaster (10. Jan 2007)

Ok funzt... thx


----------

