Double Buffering implementation

Hello everyone, I made a game (not finished yet), and I want to implement the double Buffering to solve the tearing/flashing effect.

Problem, all the tutorials I tried don’t work because I don’t use a common way to paint on the screen (I think :’( )

What can I do (simpliest way) to implement Double Buffering ?

Sorry for my bad English, I’m French. Thanks

Here’s my code (simplified) :

My Main :

   public static JFrame frame = new JFrame("The wake");


                frame.add(new Box());
		frame.setSize(LargeurFenetre+5,HauteurFenetre+27);  // les bords de la fenetre  &  l'en tete de la fenetre 
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setLocationRelativeTo(null);
		frame.setVisible(true);
		frame.setResizable(false);
		frame.createBufferStrategy(2);

Class Box :

package hugo;
import java.awt.*;
import java.awt.event.*;
import javax.swing.ImageIcon;
import javax.swing.JPanel;
import javax.swing.Timer;




public class Box extends JPanel implements ActionListener,MouseListener,MouseMotionListener{




	
	static Image Hero= new ImageIcon(Main.InstallPath+"hero/herocrouch.png").getImage();
        ... loading few images.
	

	float alphacomposite = 0;

	Timer time;

	Font font = new Font("Arial", Font.PLAIN, 20);
	static boolean handCursor = false;
	static boolean beating = false;

	static int crouchdecal = 0;

	static inputBoy Input = new inputBoy();

	Objects[] visible;
	human[] humans;

	static Particle[] particles;
	static Particle[] Bloodparticles;
	static Particle[] RainParticles;

	private double rainDarkness = 0.60f;
	private Color SkyColor = new Color(129, 107, 112);
	private Color flashColor = new Color(253,239,74);



	public Box(){

		addKeyListener(new AL());
		addMouseListener(this);
		addMouseMotionListener(this);
		setFocusable(true);
		setBackground(Color.BLACK);


		time = new Timer (Main.FPStiming, this);

		time.start();
		Main.maps.setCurrentMap(Main.maps.getCurrentMap().getID());

		particles = new Particle[20];
		Bloodparticles = new Particle[20];
		RainParticles = new Particle[600];




	}

	public void actionPerformed(ActionEvent e) { 

		repaint();

	}

	public void paint(Graphics g){



		super.paint(g);
		Graphics2D g2d = (Graphics2D) g;
		
		g2d.clearRect(0, 0, 1200, 500);		//On supprime le fond
		g2d.setColor(SkyColor);
		g2d.fillRect(0, 0, 1200, 200);     //On dessine le fond
		for (int i=0;i<250;i++){

			g2d.setColor(new Color(SkyColor.getRed()+i/2,SkyColor.getGreen()+i/2,SkyColor.getBlue()+i/2));
			g2d.fillRect(0, 200+i, 1200, 2);
			i++;
		}
		g2d.setColor(Color.BLACK);
		
		ThunderEffect(g2d);


	
	}

	private void ThunderEffect(Graphics2D g2d){

		if (Nature.Thunder!=null && Nature.Thunder.alive){

			Nature.Thunder.ttl--;
			Stroke s = g2d.getStroke();

			for (int i = 0; i<9;i++){

				//System.out.println("point en:"+Nature.Thunder.getTree()[i+1][0]+" , "+Nature.Thunder.getTree()[i+1][1]+ "case :"+i);


				g2d.setStroke(new BasicStroke(5));
				if (!(Nature.Thunder.getTree()[i+1][0]==0)){
					g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,1f));
					g2d.setColor(Color.WHITE);
					g2d.drawLine(Nature.Thunder.getTree()[i][0], Nature.Thunder.getTree()[i][1], Nature.Thunder.getTree()[i+1][0], Nature.Thunder.getTree()[i+1][1]);
					//lumièe autour du l'éclair
					g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,0.6f));
					g2d.setStroke(new BasicStroke(10));
					g2d.drawLine(Nature.Thunder.getTree()[i][0], Nature.Thunder.getTree()[i][1], Nature.Thunder.getTree()[i+1][0], Nature.Thunder.getTree()[i+1][1]);
					//lumière lointaine
				}

				if (Nature.Thunder.getTree()[i+1][1]>300){	// quand l'eclair touche le sol
					g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,0.09f));
					g2d.fillRect(0, 0,1200, 505);
					g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,1f));
				}

			}




			g2d.setStroke(s);
			g2d.setColor(Color.BLACK);

			if (Nature.Thunder.ttl<1){
				Nature.Thunder.alive=false;
			}
		}




	}

	public void FadeOut(Graphics2D g2d){
	}

	public void paintMenu(Graphics2D g2d){
}

	public void paintDeath(Graphics2D g2d){
	}

	public void paintSettings(Graphics2D g2d){
	}

	public void paintInventory(Graphics2D g2d){
	}

[quote]Problem, all the tutorials I tried don’t work because I don’t use a common way to paint on the screen
[/quote]
If you mean that g2d using methods, as long you pass along same Graphics2D object (which you got from one thing) then okay.

Have you try Canvas?

No, I didn’t tried Canvas. In fact, I was thinking about using JPanel.

Every tutorial about Double Buffering use “MyClass extends Frame”

I use MyClass extends JPanel.

Is there any better way (more stable/fast) to paint on screen?

My game works fine on my laptop but run with a non decent framerate on friends pc (with good hardware)

http://cdn.imghack.se/images/69265158a72713cae5835675e2fee255.png

Instead of this…


      g2d.setColor(SkyColor);
      g2d.fillRect(0, 0, 1200, 200);     //On dessine le fond
      for (int i=0;i<250;i++){

         g2d.setColor(new Color(SkyColor.getRed()+i/2,SkyColor.getGreen()+i/2,SkyColor.getBlue()+i/2));
         g2d.fillRect(0, 200+i, 1200, 2);
         i++;
      }

…you can use a GradientPaint instead of a Color in setColor().

Syntax for GradientPaint:
GradientPaint(startX, startY, startColor, endX, endY, endColor, true)

startX - It represents the X-coordinate of the starting point.
startY - It represents the Y-coordinate of thestarting point.
startColor - It is used to set the starting color.
endX - It represents the X-coordinate of the ending point.
endY - It represents the Y- coordinate of the ending point.
endColor - It is used to set the ending color.
true - It is a boolean value mentioned for the cyclic presentation of color in the shape mean the starting color and the ending color are repeated up to the size of the shape.

Thx ultroman, but do you think using graphics2D as I do is good for a “complex” game?

Not really. I’d go with libgdx. That’s what I did, after doing (and trashing) my first platform shooter. After climbing the learning curve, you’ll be spitting out games in no time. But it IS a good idea to do something complicated in Java2D, to learn WHY you should go with a library.

Give me a sec, and I’ll PM you a project I made to explain a Java2D setup for a friend of mine. It is well documented, and includes space for menu-panels in the sides (which are easily removed).

Thanks !

So i need to recode my game entirely? Can i use JOGL/LWJGL instead?

Yeah you can use LWJGL too. In fact Libgdx uses LWJGL underhood. For JPanel double buffer, you can swapping between BufferedImage.

Ok, thanks, i’ll try LWJGL so. It seems good. I hope this isn’t much harder than g2d wich I found easy to use.

What are the advantages to use LWJGL instead of graphics2D ?

Huge performance. But I can’t say it’s easier than using g2d :wink: To use LWJGL feature but with g2d API, use Slick2D.

Ok, thanks ! So i’ll try LWJGL and if my brain explode, I’ll go for Slick2D…

Pray for me =)

Heheh, good luck. You won’t need that example, then.

The advantage is, that when you’re using OpenGL you’re communicating with the graphics device in your computer, instead of throwing most of it at the CPU. It will be a tough start, but we’re here to help :slight_smile:

Now there’s a daft idea! :stuck_out_tongue: JPanel is double buffered by default, so ignoring a potentially hardware accelerated buffer strategy (using VolatileImages under the hood) for your own using BufferedImage makes no sense!

hug0x - why are you creating a buffer strategy and then not using it? This might be screwing up the inbuilt double buffering. You should use either use BufferStrategy or the repaint() method.

I’m not trying to dissuade you from looking at an LWJGL option (I’d recommend libgdx over Slick2D), but that will take you a while - there are some quick wins in using the Java2D API correctly which might give you enough of a performance boost for now.

the BufferStrategy line wasn’t here 10minutes before posting, I just forgot to remove it.

You mean the repaint(); method is already doublebuffered?Because I use it with ease =)

I just want to choose a faster way to paint on screen… LWJGL seems a bit hard for me x) and slick2D don’t convice me…

Can you lie to me and tell me that I can make it work in few days… (I keep my old files, i just change my graphic classes).

With slick2d, you should be able to make a full move in very little time.
The others…not so much. You’re in for a lot of prototyping and trying out simple ways of drawing images and doing resource management :slight_smile:

It is, though it doesn’t always mean you don’t see tearing - also depends on vsync, OS, etc.

Also, repaint() doesn’t call your render methods immediately, coupled with the fact that the Swing timer isn’t that accurate.

Try switching to an active rendering loop, something like this one from ra4king - http://www.java-gaming.org/topics/about-on-my-main-loop/26222/msg/229028/view.html#msg229028 You should be able to keep all your rendering code. This approach should give you the best performance you can out of Java2D.

Thanks, I will test both of the solutions!

Man u are using java2D and it implement natively the doble buffering in the jpanel
you don’t need to use other libraries is absolutely easy

look hire this pice of code and chek in the java doc if u don’t belive


public abstract class AbstractGameJPanel implements Game {

	
	// off screen rendering
	protected Graphics2D dbg = null; 
	protected Image dbImage = null;	
	protected JPanel gamePanel = null;
	protected int pWidth,pHeight;	//size of the panel
	
	public void renderGame() throws Exception {

		if (dbImage == null){
			dbImage = gamePanel.createImage(pWidth, pHeight);  //LOOK THIS it is automatically double buffering
			if (dbImage == null) {
				System.out.println("dbImage is null");
				return;
			}
			else
				dbg = (Graphics2D) dbImage.getGraphics();
		}
		
		internalRenderGame(dbg);
	
	}	

and this is the code that paint on the screen


	public void paintScreen() {
		Graphics2D g;
		try {
			g = (Graphics2D) gamePanel.getGraphics();

			g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);

			if ((g != null) && (dbImage != null)){
				
				g.drawImage(doFinalEffect(dbImage), 0, 0,pWidth,pHeight, null);
			}

			
			// Sync the display on some systems.
			// (on Linux, this fixes event queue problems)
			Toolkit.getDefaultToolkit().sync();
			g.dispose();
		}
		catch (Exception e){   // quite commonly seen at applet destruction
			System.out.println("Graphics error: " + e);  
		}
	}

That’s why I suggested (a correct implementation of ) Canvas first. I myself never use raw JPanel (or whatever you call it, I mean by not using Canvas+BufferStrategy).