# Flackern trotz Double-Buffering



## Knuddel (13. Jul 2010)

Hallo liebe Java Community,
ich wende mich an euch, da ich nach stundenlangem ausprobieren nicht mehr weiter komme 
Ich hoffe ihr könnt mir weiterhelfen, ich bin daher für jede Hilfe dankbar.:toll:

Mein Problem liegt in der Animation von 3 Bildern die sich ineinader verschieben. Hierbei ist jedoch auffälig, dass 2 Bilder durch den Double-Buffer flüssig dargestellt werden, das dritte jedoch nicht 

Zuerst werden 2 Bilder aus einer Datei geladen, dass dritte Bild wird später durch einen CropImageFilter erstellt.

```
public void init()
{
       bild1 = getImage(getCodeBase(), "bild1.jpg");
       bild2 = getImage(getCodeBase(), "bild2.jpg");
       prepareImage(bild1, this);
       prepareImage(bild2, this);
       prepareImage(bild3, this);
}
```

Mein Double-Buffer

```
//Double-Buffer
    @Override
    public void update(Graphics g)
    {

      //Double-Buffer initialisieren
      if (dbImage == null) {
         dbImage = createImage(
            this.getSize().width,
            this.getSize().height
         );
         dbGraphics = dbImage.getGraphics();
      }
      //Hintergrund löschen
      dbGraphics.setColor(getBackground());
      dbGraphics.fillRect(
         0,
         0,
         this.getSize().width,
         this.getSize().height
       );
      //Vordergrund zeichnen
      dbGraphics.setColor(getForeground());
      paint(dbGraphics);
      //Offscreen anzeigen
      g.drawImage(dbImage,0,0,this);
    }
```

Hier werden die Bilder gezeichnet

```
@Override
    public void paint(Graphics g)
    {
        // Background zeichen
        g.drawImage(bild1, 0, 0, this);
        g.drawImage(bild2, 1200-x, 0, this); // Bild verschiebt sich durch Änderung des x-Wertes
        g.drawImage(bild3, 0, 0, this);
    }
```

Und zum Schluss noch die Erstellung des 3 Bildes, das leider flackert 

```
public void picture3create()
    {
       ImageFilter cropFilter = new CropImageFilter(x, 0, 1200-x, 800);
       bild3 = createImage(
       new FilteredImageSource(bild1.getSource(),cropFilter));
    }
```

Vielleicht ist es noch wichtig zu sagen das ich die picture3create hinter der Prozedur hängen muss die den x-wert verändert, nämlich wenn man wasd drückt, sodass man durchs bild steuert wobei sich dieses verändert! Schonmal Danke für eure Hilfe


----------



## Marco13 (14. Jul 2010)

Warum verwendest du AWT und nicht Swing? Bei Swing ist Double-Buffering schon eingebaut...
Und ... was genau versuchst du mit dem CroppedImageFilter zu erreichen? Ein neues Bild zu erstellen, das nur den gewünschten Ausschnitt anzeigt, dürfte einfacher sein...


----------



## KrokoDiehl (14. Jul 2010)

Außerdem sollte man nicht 
	
	
	
	





```
paint()
```
 überschreiben sondern 
	
	
	
	





```
paintComponent()
```
, oder hat das was mit AWT zutun? 
Letztere jedenfalls sollte man in Swing überschreiben.


----------



## Knuddel (14. Jul 2010)

Marco13 hat gesagt.:


> Und ... was genau versuchst du mit dem CroppedImageFilter zu erreichen? Ein neues Bild zu erstellen, das nur den gewünschten Ausschnitt anzeigt, dürfte einfacher sein...



Ich schneide aus dem Bild1 einen Auschnitt aus den ich dann an die Position (0/0) setze, das bild 1 an stelle (-x/0) zu setzen geht ja nicht oder? Dadurch ensteht dann eine Bewegung im Bild.

Wäre nett, wenn ihr mir anhand eines kurzen Beispiels zeigen könntet wie das mit dem swing geht, wie ich dann die Bilder einbinden muss etc.! Bin leider noch blutiger Anfänger mit Java


----------



## Marco13 (14. Jul 2010)

Eigentlich geht FAST alles genauso wie bei AWT. Die ganzen Components (Frame, Applet, Panel, Button) werden einfach durch das entsprechende aus Swing ersetzt (JFrame, JApplet, JPanel, JButton).

Nur bei der paint-Methode muss man aufpassen: Wo steht die im Moment? In einem Applet? Poste mal mehr zusammenhängenden (am besten Compilierbaren!) Code.

Ansonten geht's auch bei Swing mit g.drawImage(...) und so...


----------



## Knuddel (14. Jul 2010)

Ja hier mein kompletter Quellcode, nicht wundern wenn da alles bissl umständlich gemacht ist, jedoch könntest du mir hier sagen, wie es mit den swing dann funktionieren würde  Danke schonmal

```
import java.awt.*;
import java.applet.*;
import java.awt.event.*;
import java.util.*;
import java.awt.image.CropImageFilter;
import java.awt.image.FilteredImageSource;
import java.awt.image.ImageFilter;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;




public class game
extends Applet
implements KeyListener
{
    int feld[][] = new int[1200][800];
    Image background[][] = new Image[10][10];
    Image overlay;
    byte goup, godown, sprungart;
    int x= 25, y =245, t;
    boolean jump, released, releasea;

    //Double-Buffering
    private Image dbImage;
    private Graphics dbGraphics;
    //Game-Ticker
    final Timer timer = new Timer();
    String input = "";


    @Override
    public void init()
    {
       //Tastatur-Eingabe
       addKeyListener(this);
       released = true;
       releasea = true;
       //Grafikvorbereitung
       background[1][1] = getImage(getCodeBase(), "level1_teil1.jpg");
       background[1][2] = getImage(getCodeBase(), "level1_teil2.jpg");
       prepareImage(background[1][1], this);
       prepareImage(background[1][2], this);
       prepareImage(overlay, this);
       //Game-Ticker
       timer.scheduleAtFixedRate(task, 0, 5);
       //Game-Blocking
       for(int i=20;i<=453;i++)
       {
           feld[i][240]=1;
       }

    }

    /*public void loadWorld()
    {
        try{
            BufferedReader load =
            new BufferedReader(new FileReader("WORLD.txt"));

            String zeile = load.readLine();
            while (zeile != null)
            {
                String[] value = zeile.split("\\_");
                int takex =  Integer.parseInt(value[1]);
                int takey =  Integer.parseInt(value[2]);
                int takefunction =  Integer.parseInt(value[3]);
                feld[takex][takey] = takefunction;
            }
        }catch(IOException ioe){
        ioe.printStackTrace();
        }
    }*/

    public void bildchange()
    {
       //Grafik-Overlay
       ImageFilter cropFilter = new CropImageFilter(x, 0, 1200-x, 800);
       overlay = createImage(
       new FilteredImageSource(background[1][1].getSource(),cropFilter));
    }
    //Game-Ticker
    TimerTask task = new TimerTask() {
    public void run()
    {
        if(jump == true)
        {
            if(released == false)
                sprungart = 1;            
            if(releasea == false)
                sprungart = 2;

            switch(sprungart)
            {
                case 0:         //Sprung nach oben
                    if(goup < 50)
                        y--;
                    else
                        y++;
                    break;
                case 1:         //Schub-Sprung nach rechts
                    if(goup < 50)
                    {
                        y--;
                        x++;
                    }
                    else
                    {
                        y++;
                        x++;
                    }
                    break;
                case 2:         //Schub-Sprung nach links
                    if(goup < 50)
                    {
                        y--;
                        x--;
                    }
                    else
                    {
                        y++;
                        x--;
                    }
                    break;
            }

            if(goup < 50)
                goup++;
            else
                godown++;

            if(goup == 50 && godown == 50) // RESET
            {
                goup = 0;
                godown = 0;
                jump = false;
                sprungart = 0;
            }
        }
        else
        {
            switch(feld[x][y-5])
            {
                case 0: // Schwerkraft
                    ++y;
                    break;
            }
        }
        repaint();
    } };


    //Double-Buffering
    @Override
    public void update(Graphics g)
    {

      //Double-Buffer initialisieren
      if (dbImage == null) {
         dbImage = createImage(
            this.getSize().width,
            this.getSize().height
         );
         dbGraphics = dbImage.getGraphics();
      }
      //Hintergrund lÃ¶schen
      dbGraphics.setColor(getBackground());
      dbGraphics.fillRect(
         0,
         0,
         this.getSize().width,
         this.getSize().height
       );
      //Vordergrund zeichnen
      dbGraphics.setColor(getForeground());
      paint(dbGraphics);
      //Offscreen anzeigen
      g.drawImage(dbImage,0,0,this);
    }

    //Game-Grafik
    @Override
    public void paint(Graphics g)
    {
        // Background zeichen
        g.drawImage(background[1][1], 0, 0, this);
        g.drawImage(background[1][2], 1200-x, 0, this);
        
        // Need Buffering (keinFlackern)
        g.drawImage(overlay, 0, 0, this);

        // Person zeichnen
        g.setColor(Color.blue);
        g.fillOval (x,y,20,20);


    }


    public void keyPressed(KeyEvent event)
    {
        requestFocus();
        int key = event.getKeyChar();
        if (key == 'd')
        {
            released = false;   // Taste d wird gedrÃ¼ckt
            if(jump != true)
            {
                if(x < 1180)
                    x = x + 5;      // laufe nach rechts
            }
        }
        if (key == 'a')
        {
            releasea = false;   // Taste a wird gedrÃ¼ckt
            if(jump != true)
            {
                if(x > 3)
                    x = x - 5;      // laufe nach links
            }

        }
        if (jump == false && key == 'w')
        {
            jump = true;    // Springen
        }
        if (key == 's')
        {
            y= y +1;
        }
        bildchange();
        repaint();
    }


        public void keyReleased(KeyEvent e)
        {
            requestFocus();
            int key = e.getKeyChar();
            if (key == 'd')
                 released = true; //Taste d wird nicht gedrÃ¼ckt
            if (key == 'a')
                 releasea = true; //Taste a wird nicht gedrÃ¼ckt
        }

    public void keyTyped(KeyEvent e)
    {
        throw new UnsupportedOperationException("Not supported yet.");
    }

}
```


----------



## Marco13 (14. Jul 2010)

Grundsätzlich ist DAfür nicht viel zu ändern

```
// From [url]http://www.java-forum.org/awt-swing-swt/103239-flackern-trotz-double-buffering.html#post658032[/url]

import java.awt.*;
import java.applet.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.JPanel;
import javax.swing.JApplet;
import java.awt.image.CropImageFilter;
import java.awt.image.FilteredImageSource;
import java.awt.image.ImageFilter;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;




public class SomeGame
extends JApplet
implements KeyListener
{
    int feld[][] = new int[1200][800];
    Image background[][] = new Image[10][10];
    Image overlay;
    byte goup, godown, sprungart;
    int x= 25, y =245, t;
    boolean jump, released, releasea;

    //Game-Ticker
    final Timer timer = new Timer();
    String input = "";


    @Override
    public void init()
    {
       //Tastatur-Eingabe
       addKeyListener(this);
       released = true;
       releasea = true;
       //Grafikvorbereitung
       background[1][1] = getImage(getCodeBase(), "image01.png");
       background[1][2] = getImage(getCodeBase(), "image02.png");
       prepareImage(background[1][1], this);
       prepareImage(background[1][2], this);
       prepareImage(overlay, this);
       //Game-Ticker
       timer.scheduleAtFixedRate(task, 0, 5);
       //Game-Blocking
       for(int i=20;i<=453;i++)
       {
           feld[i][240]=1;
       }
       getContentPane().add(new GamePanel());
    }

    /*public void loadWorld()
    {
        try{
            BufferedReader load =
            new BufferedReader(new FileReader("WORLD.txt"));

            String zeile = load.readLine();
            while (zeile != null)
            {
                String[] value = zeile.split("\\_");
                int takex =  Integer.parseInt(value[1]);
                int takey =  Integer.parseInt(value[2]);
                int takefunction =  Integer.parseInt(value[3]);
                feld[takex][takey] = takefunction;
            }
        }catch(IOException ioe){
        ioe.printStackTrace();
        }
    }*/

    public void bildchange()
    {
       //Grafik-Overlay
       ImageFilter cropFilter = new CropImageFilter(x, 0, 1200-x, 800);
       overlay = createImage(
       new FilteredImageSource(background[1][1].getSource(),cropFilter));
    }
    //Game-Ticker
    TimerTask task = new TimerTask() {
    public void run()
    {
        if(jump == true)
        {
            if(released == false)
                sprungart = 1;
            if(releasea == false)
                sprungart = 2;

            switch(sprungart)
            {
                case 0:         //Sprung nach oben
                    if(goup < 50)
                        y--;
                    else
                        y++;
                    break;
                case 1:         //Schub-Sprung nach rechts
                    if(goup < 50)
                    {
                        y--;
                        x++;
                    }
                    else
                    {
                        y++;
                        x++;
                    }
                    break;
                case 2:         //Schub-Sprung nach links
                    if(goup < 50)
                    {
                        y--;
                        x--;
                    }
                    else
                    {
                        y++;
                        x--;
                    }
                    break;
            }

            if(goup < 50)
                goup++;
            else
                godown++;

            if(goup == 50 && godown == 50) // RESET
            {
                goup = 0;
                godown = 0;
                jump = false;
                sprungart = 0;
            }
        }
        else
        {
            switch(feld[x][y-5])
            {
                case 0: // Schwerkraft
                    ++y;
                    break;
            }
        }
        repaint();
    } };


    private class GamePanel extends JPanel
    {
        //Game-Grafik
        @Override
        public void paintComponent(Graphics g)
        {
            super.paintComponent(g);

            // Background zeichen
            g.drawImage(background[1][1], 0, 0, this);
            g.drawImage(background[1][2], 1200-x, 0, this);

            // Need Buffering (keinFlackern)
            g.drawImage(overlay, 0, 0, this);

            // Person zeichnen
            g.setColor(Color.blue);
            g.fillOval (x,y,20,20);
        }
    }


    public void keyPressed(KeyEvent event)
    {
        requestFocus();
        int key = event.getKeyChar();
        if (key == 'd')
        {
            released = false;   // Taste d wird gedrÃ¼ckt
            if(jump != true)
            {
                if(x < 1180)
                    x = x + 5;      // laufe nach rechts
            }
        }
        if (key == 'a')
        {
            releasea = false;   // Taste a wird gedrÃ¼ckt
            if(jump != true)
            {
                if(x > 3)
                    x = x - 5;      // laufe nach links
            }

        }
        if (jump == false && key == 'w')
        {
            jump = true;    // Springen
        }
        if (key == 's')
        {
            y= y +1;
        }
        bildchange();
        repaint();
    }


        public void keyReleased(KeyEvent e)
        {
            requestFocus();
            int key = e.getKeyChar();
            if (key == 'd')
                 released = true; //Taste d wird nicht gedrÃ¼ckt
            if (key == 'a')
                 releasea = true; //Taste a wird nicht gedrÃ¼ckt
        }

    public void keyTyped(KeyEvent e)
    {
        //throw new UnsupportedOperationException("Not supported yet.");
    }

}
```

Aber das ist wohl wieder mal ein Fall, wo man sieht dass es Java einem zu leicht macht. Mit 15 Zeilen hat man ein JPG auf dem Bildschirm, da kann zu einem Jump'n'Run ja nicht mehr viel fehlen :reflect: :autsch:

Wie auch immer. Was das mit dem CropImage soll, ist mir noch nicht klar.


----------



## Knuddel (14. Jul 2010)

Das ist super Danke, behebt aber nicht das Problem, daher werde ich die vorgehensweise mit dem CropImageFilter mal genauer erläutern, weil das Flackern wohl genau dadurch entsteht.



> Aber das ist wohl wieder mal ein Fall, wo man sieht dass es Java einem zu leicht macht. Mit 15 Zeilen hat man ein JPG auf dem Bildschirm, da kann zu einem Jump'n'Run ja nicht mehr viel fehlen


Tut mir leid, wenn das total dumme Fehler sind die ich mache. Ich habe bis vorgestern auschließlich mit C++ programmiert. Nun wollte ich aber mal Java lernen, da es da doch ein paar tolle Features gibt, die C++ so nicht aufweist. Ein kleines Jump&Run sollte es dann werden  Es funktioniert ja auch alles wunderbar sieht nur halt bischen unbeholfen aus, wenn das Bild aus dem CropImageFilter flackert^^.



> Wie auch immer. Was das mit dem CropImage soll, ist mir noch nicht klar.


Nun aber hier zu . Du hast ja bereits erkannt, dass es sich um ein Jump&Run Spiel handelt. Hierbei ist es nötig, dass das Bild weiterläuft, sobald man sich mit seiner Figur weiterbewegt.
Bild1 ist unser Ausgangsbild, dass beim Start des SPiel geladen wird. Wenn sich jetzt die Figur nach rechts bewegt(auf der x Achse), dann verschiebt sich das Bild. Auf der rechten Seite werden neue Spielinhalte sichtbar, dies ist Bild2. Daher auch 
	
	
	
	





```
g.drawImage(background[1][2], 1200-x, 0, this);
```
Soweit so gut mit diesen beiden Elementen(Bild1 und Bild2) entshet bereits eine Anmation(ohne Flackern). Diese reicht aber nicht aus, da sich das Bild auch auf der linken Seite bewegen muss, sodass der Eindruck entsteht, dass man weiter läuft. Und genau an dieser Stelle verwende ich den CropImageFilter. Erschneidet vom Bild1 ein weiter vorne liegendes Element aus und setzt es an die Position (0/0). Somit verschwinden bei der Fortbewegung nach rechts im linken Bildschirmrand die alten Bildelemente und im rechten werden die neuen sichtbar.
Nur wieso flackert es links aber rechts nicht. Liegt es dadran das Bild3 bei jeder Bewegung ein neues Bild erzeugt, welches dann nicht im DoubleBuffer bearbeitet werden kann, die anderen Bilder sind ja quasi konstanten, es sind immer dieselben Bilder nur an einer anderen Position im Bild.
So genug erzählt, vielleicht ist es auch völliger Schrott, umgekehrt weißt du jetzt wodrauf ich hinaus möchte??????;(


----------



## Marco13 (14. Jul 2010)

Knuddel hat gesagt.:


> Tut mir leid, wenn das total dumme Fehler sind die ich mache.


Nein, das meinte ich nicht - es kommt nur oft vor, dass jemand besagten Stand (ein Bild in einem JFrame oder so) erreicht hat, und dann meint, als nächstes Doom IIX programmieren zu können. War kein persönlicher Angriff.



> Hierbei ist es nötig, dass das Bild weiterläuft, sobald man sich mit seiner Figur weiterbewegt.
> Bild1 ist unser Ausgangsbild, dass beim Start des SPiel geladen wird. Wenn sich jetzt die Figur nach rechts bewegt(auf der x Achse), dann verschiebt sich das Bild. Auf der rechten Seite werden neue Spielinhalte sichtbar, dies ist Bild2.
> ...



OK, der Grobgranulare Ablauf sollte dann wohl sowas sein, dass immer ein Bild (das etwa Bildschirmbreite hat) von Rechts nach Links wandert, und dahinter ein neues Bild nachgeschoben, also sowas wie

```
verschiebeNachLinks(images[0]);
verschiebeNachLinks(images[1]);
male(images[0]);
male(images[1]);
if (images[0].istAußerhalbDesBildschirms())
{
    images[0] = images[1];
    images[1] = ladeNächstesBildDasJetztVonRechtsReinKommt();
}
```

Klingt... legitim und interessant, allerdings solltest du diesen Ablauf (falls er so gewünscht ist) etwas klarer beschreiben ... also, das ganze wirkt recht unbedarft - und was "feld" und Background (der Größe 10x10!?) sein soll, warum nur Background*[1]* verwendet wird und so... das erschließt sich alles nicht so.

In jedem Fall sollte es nicht nötig sein, dort während des Spiels noch an Bildern rumzuschnippeln und neue CroppedImages erstellen. Es sollte reichen, wenn man die Bilder passend malt. In einfachsten Fall sowas wie

```
graphics.drawImage(image, -100, 0, this);
```
dann sieht man eben die 100 linken Pixel nicht. Im Zweifelsfall kann man, statt ein neues Cropped Image zu erstellen, aber mit dieser Methode jeden beliebigen Ausschnitt eines Bildes an jede beliebige Stelle malen...


----------



## Knuddel (14. Jul 2010)

> und dann meint, als nächstes Doom IIX programmieren zu können


 Ne keine Angst

Wenn das geht, 

```
graphics.drawImage(image, -100, 0, this);
```
also negativen x-Werte angeben zu können, dann wäre Bild 3 überflüssig, dann müsste ich nur bild 1  immer weiter negativ setzen und es würde nach links verschwinden und gleichzeit würde bild2 rechts neu einlaufen 

Was meine Image-Array angeht, siehtn bischen scheiße aus ich weiß, das hab ich schonmal vordefiniert, werde ich noch später brauchen, da mein Jump&Run schon bischen größer als 2 Bilder sein wird. Die Definition ist: background[level][levelteil] 

Ich schau mal, ob dein Vorschlag funktioniert mit den negativen x-Werten, melde mich dann wieder


----------



## Knuddel (14. Jul 2010)

Fettes Dankeschön,  läuft jetzt supper ohne flackern  Vielen dank :-*


----------

