Java cooperation home
Tutorial
type and press Enter

Reduzierung des Bildschirmflackerns

Wie ihr in dem vorigen Beispiel sicherlich bemerkt habt, flackert der Ball sehr stark, was sich bei wirklich aufwendigen Graphiken noch verstärken würde. Der Grund dafür ist recht einfach. Vor jedem Aufruf der Methode paint() durch repaint() in unserem Thread wird das Bild vollständig gelöscht um dann erneut gezeichnet zu werden. Dadurch erscheint für den Bruchteil einer Sekunde ein vollkommen leerer Bildschirm, was wir als Flackern wahrnehmen. Um dieses Flackern des Bildes zu verhindern, gibt es zunächst drei verschiedene Möglichkeiten.

  1. Bildschirm nicht löschen
  2. Bildschirm nur dort löschen, wo es nötig ist
  3. Die Doppelpufferung

Bildschirm nicht löschen

Dieser Ansatz erscheint auf den ersten Blick als die Lösung aller Probleme. In unserem Fall würde es aber bedeuten, dass der Ball eine dicke, rote Linie hinterlassen würde. Wir wollen den Ball aber als bewegten Ball wahrnehmen und nicht als Objekt, dass eine rote Linie hinter sich herzieht. Das Löschen des Bildschirmes zu unterdrücken ist also nur für nicht bewegte Animationen sinnvoll.
Auch für den späteren Verlauf dieser Lektion ist es wichtig zu verstehen, dass jedesmal wenn repaint() aufgerufen wird, nicht gleich auch die Methode paint() aufgerufen wird. Stattdessen ist eine Methode namens update() dazwischengeschaltet. Wird diese nicht überschrieben, so ist diese Methode dafür verantwortlich, dass der Bildschirm mit einem leeren Bild überlagert und anschließend durch den Aufruf von paint() von der Methode update() aus, neu gezeichnt wird. Um also das Löschen des Bildschirms zu unterdrücken, muss man update() so überschreiben, dass sie nur noch paint() aufruft. Dies ist in drei Zeilen zu bewerkstelligen.

public void update(Graphics g)
{
    paint(g);
}

Wie gesagt ist diese Möglichkeit nur bei statischen Animationen wirklich praktikabel und wird somit nur selten verwendet.

Bildschirm nur dort löschen, wo es nötig ist

Die Idee dieses Ansatzes besteht darin, den Bildschirm nur dort zu löschen, wo sich im letzten Schritt ein Graphikelement befunden hat, sich nun aber keines mehr befindet. Dieser Ansatz lässt sich z. B. bei dem sehr bekannten Spiel Snakin' ganz gut umsetzen, indem man als letztes Glied der Schlange ein Element in der Farbe des Hintergrundes, und somit unsichtbares Element, anhängt. Dadurch wird immer beim nächsten Schritt das vorher letzte Element überlagert. Da sich diese Methode nur in bestimmten Fällen anwenden läßt und dann immer nach einer, auf das spezifische Problem abgestimmten, Vorgehensweise verlangt, möchte ich nicht näher auf diese Methode eingehen. Wenden wir uns stattdessen der sogenannten Doppelpufferung zu, die man als Standardmethode ohne Abwandlung in jedes Applet mit Animationen übernehmen und somit das Flakern des Bildes effektiv unterdrücken kann.

Die Doppelpufferung

Die Doppelpufferung kann in jedes Applet, ohne großen, programiertechnischen Aufwand eingebaut werden. Beim Doppelpuffern wird bei jedem Animationsschritt zunächst die gesamte Bildschirmausgabe in ein Offscreen-Image geschrieben. Erst wenn alle Ausgabeoperationen abgeschlossen sind, wird dieses Offscreen-Image auf die Fensteroberfläche kopiert. Im Detail sind dazu folgende Schritte erforderlich:

  1. Das Fensterobjekt beschaft sich durch Aufruf von createImage ein Offscreen-Image und speichert es in einer Instanzvariablen ( = Erzeugen eines leeren Bildes)
  2. Durch Aufruf von getGraphics wird ein Grafikkontext zu diesem Image beschafft
  3. Alle Bildschirmausgaben (inklusive Löschen des Bildschirms) gehen zunächst auf den Offscreen-Grafikkontext ( = Zeichnen des Bildes im Hintergrund)
  4. Wenn alle Ausgabeoperationen abgeschlossen sind, wird das Offscreen-Image mit drawImage in das Ausgabefenster kopiert. ( = Zeichnen des Bildes im Vordergrund)

Durch diese Vorgehensweise wird erreicht, daß das Bild komplett aufgebaut ist, bevor es angezeigt wird. Da beim anschließenden Kopieren die neuen Pixel direkt über die alten kopiert werden, erscheinen dem Betrachter nur die Teile des Bildes verändert, die auch tatsächlich geändert wurden. Ein Flackern, das entsteht, weil Flächen für einen kurzen Zeitraum gelöscht und dann wieder gefüllt werden, kann nicht mehr auftreten.

Einziger, gravierender Nachteil der Doppelpufferung ist jedoch, dass die Konstruktion des OffscreenImages ziemlich speicheraufwendig ist, und außerdem die Bilddaten zweimal geschrieben werden. In den meisten Fällen und auf schnellen Rechnern ist es jedoch weitaus vorteilhafter, in sehr kurzer Zeit die Doppelpufferung zu realisieren, als sich lange mit der Suche nach alternativen Möglichkeiten aufzuhalten.

So, aber nach der ganzen Theorie nun zur Implementierung der Doppelpufferung in unserem Ball - Applet aus dem letzten Kapitel!

Der Programcode zur Umsetzung der Doppelpufferung
// Definition zweier Instanzvariablen für die Doppelpufferung im Kopf des Programmes
private Image dbImage;
private Graphics dbg;

... anderer Programcode ...

/** Update - Methode, Realisierung der Doppelpufferung zur Reduzierung des Bildschirmflackerns */
public void update (Graphics g)
{
    // Initialisierung des DoubleBuffers
    if (dbImage == null)
    {
      dbImage = createImage (this.getSize().width, this.getSize().height);
      dbg = dbImage.getGraphics ();
    }

    // Bildschirm im Hintergrund löschen
    dbg.setColor (getBackground ());
    dbg.fillRect (0, 0, this.getSize().width, this.getSize().height);

    // Auf gelöschten Hintergrund Vordergrund zeichnen
    dbg.setColor (getForeground());
    paint (dbg);

    // Nun fertig gezeichnetes Bild Offscreen auf dem richtigen Bildschirm anzeigen
    g.drawImage (dbImage, 0, 0, this);
}

Wie schon erwähnt läßt sich der obige Programmcode in jedes Applet übernehmen, so auch in unser Ball - Beispiel von vorher.

SourceCode Applet download
SourceCode Doppelpufferung download
Applet ansehen

Nächstes Kapitel

Verhindern, dass der Ball sich aus dem Spielfeld bewegt
Fabian Birzele, 2001-2004.
web-design: Vadim Murzagalin, 2004.