Page 1 of 1

Java Game (Actually the most efficient way to repaint) No page flicking... you get all control... if you want Rate Topic: ***** 3 Votes

#1 pbl  Icon User is offline

  • There is nothing you can't do with a JTable
  • member icon

Reputation: 8343
  • View blog
  • Posts: 31,890
  • Joined: 06-March 08

Posted 06 July 2009 - 09:43 PM

*
POPULAR

OK this is the basic in all page flickering and how to avoid flickering
First a basic concept:
- computer screens are refreshed 60 times a second in North America and 50 times a second in Europe
Java (and Swing) do a good job at it and refresh the screen the best way it can manage but some times it is nto the best way so sometimes, particularly in Games, you will need to do the job and help a little bit the Java GUI for best performance.

How to do it ? I'll teach you right away.
Sorry for my European and Asiatic friend, I'll do it the Obama way :D

The idea is to draw what you have to draw on 2 buffers, wait for the part of the GUI that repaint the screen, and tell it from which buffer in memory it has to redraw the image

I have 3 classes here... feel free to cut & paste them

OK the first one in to create the main frame... you are use to it
and we will use a Canvas to do our drawing... Canvas are needed because we can apply a BfferStartegy to them
BuffeStartegy is the bumber of buffers we will use to draw "offscreen".
Here is a first glitch/trap: before applying a buffer strategy to a Component this component has to be visible so here is my first class
For this example we will use the stupid "bouncing ball" program:
import java.awt.BorderLayout;
import javax.swing.*;


public class GameFrame extends JFrame {
	GameFrame() {
		// frame description
		super("Bouncing ball");
		// our Canvas
		GameCanvas canvas = new GameCanvas();
		add(canvas, BorderLayout.CENTER);
		// set it's size and make it visible
		setSize(600, 400);
		setVisible(true);		
		// now that is visible we can tell it that we will use 2 buffers to do the repaint
		// befor being able to do that, the Canvas as to be visible
		canvas.createBufferStrategy(2);
	}
	// just to start the application
	public static void main(String[] args) {
		// instance of our stuff
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				new GameFrame();
			}
		});
	}
}


To this JFrame we will add a Canvas, because Canvas support BufferStrategy
This class that extends Canvas will use a Timer to be recalled 60 times a second... this is everytime the hardware refresh the screen
60 times (in NorthAmerica) a second is every 16/1000 a second
There are no need to refresh the screen more often... if you do so
1) the human eye won't see it
2) you will ask the blitter to show something and then to show something else and only the last one will be displayed
import java.awt.*;
import java.awt.image.BufferStrategy;
import java.util.Random;

import javax.swing.Timer;

// OK this the class where we will draw
public class GameCanvas extends Canvas{
	// the initial position of the ball in the canvas that will be determine randomly
	int ballX, ballY;
	// the delta we apply to the x and y position at each repaint
	// here it is set to 1... would should have a larger value in "real" life
	// but for testing purpose that will give you the fastest possible value for a 1 pixel update
	int deltaX = +1, deltaY = +1;
	// the size of the ball in pixels
	final int BALLSIZE = 25;
	// a flag if repaint in progress (needed if our computation are to long)
	boolean repaintInProgress = false;
	// we use 2 pages to do our buffering
	// just for demo purpose we will change the background color everytime we repaint a frame
	// you will never do that in real life :-)
	Color[] back = {Color.YELLOW, Color.MAGENTA};
	// just used to determine on which buffer I am writting... will not be used in real life
	int page = 0;
	// a random object to determine where the initial position of the ball will be
	Random ran = new Random();
	// this is a Canvas but I wont't let the system when to repaint it I will do it myself
	GameCanvas() {
		// so ignore System's paint request I will handle them
		setIgnoreRepaint(true);
		// a random place to start the ball
		ballX = ran.nextInt(580);
		ballY = ran.nextInt(380);
		// build Chrono that will call me 
		Chrono chrono = new Chrono(this);
		// ask the chrono to calll me every 60 times a second so every 16 ms
		new Timer(16, chrono).start();
	}
	
	// my own paint method that repaint off line and switch the displayed buffer
	// according to the VBL
	public void myRepaint() {
		// wasting too much time doing the repaint... ignore it
		if(repaintInProgress)
			return;
		// so I won't be called 2 times in a row for nothing
		repaintInProgress = true;
		// get actual Canvas size so I can check if I am out of bounds
		Dimension size = getSize();
		// test for all debordement possibilities on the X axis
		if(deltaX > 0) {
			if(ballX > size.width - BALLSIZE)
				deltaX = -deltaX;
		}
		else {
			if(ballX < 0)
				deltaX = -deltaX;
		}
		// check on the Y axis
		if(deltaY > 0) {
			if(ballY > size.height - BALLSIZE)
				deltaY = -deltaY;
		}
		else {
			if(ballY < 0)
				deltaY = -deltaY;
		}
		// update ball position
		ballX += deltaX;
		ballY += deltaY;
		// ok doing the repaint on the not showed page
		BufferStrategy strategy = getBufferStrategy();
		Graphics graphics = strategy.getDrawGraphics();
		// this is for testing purpose you would not do that in real life
		// we change the background color to that you will see that the page are flipped
		page++;
		page %= 2;
		// again testing purpose we flip backgound color every repaint
		graphics.setColor(back[page]);
		graphics.fillRect(0, 0, size.width, size.height);
		// now we draw the ball
		graphics.setColor(Color.BLACK);
		graphics.fillOval(ballX, ballY, BALLSIZE, BALLSIZE);
		if(graphics != null)
			graphics.dispose();
		// show next buffer
		strategy.show();
		// synchronized the blitter page shown
		Toolkit.getDefaultToolkit().sync();

		// ok I can be called again
		repaintInProgress = false;
	}
}


finally the Chrono class that recalls me 60 times a second
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

/** Will be called at each blitter page */
public class Chrono implements ActionListener {

	GameCanvas gc;
	// constructor that receives the GameCanvas that we will repaint every 60 milliseconds
	Chrono(GameCanvas gc) {
		this.gc = gc;
	}
	// calls the method to repaint the anim
	public void actionPerformed(ActionEvent arg0) {
		gc.myRepaint();
	}

}


This is the fastest way you can refresh a screen in Java
Change the deltaX and deltaY the ball will go faster

The keypoints:
- you can inform a Canvas that it should not do it's own repaint
- you can overload that repaint()
- if you do it by hand call the toolkit synch() method to synchronize the VBL

Hope this helps

Is This A Good Question/Topic? 9
  • +

Replies To: Java Game (Actually the most efficient way to repaint)

#2 pbl  Icon User is offline

  • There is nothing you can't do with a JTable
  • member icon

Reputation: 8343
  • View blog
  • Posts: 31,890
  • Joined: 06-March 08

Posted 09 July 2009 - 08:37 PM

If your computations take more than 1 milli second I produce a new version that performs calculations and the actual repaint() in 2 different threads

A little bit more efficient and if your calculation are really complex, this new version just skip a VBL (60 milliseconds) and repaint at the next monitor refresh

http://www.dreaminco...topic113977.htm
Was This Post Helpful? 1
  • +
  • -

#3 DaneAU  Icon User is offline

  • Great::Southern::Land
  • member icon

Reputation: 286
  • View blog
  • Posts: 1,619
  • Joined: 15-May 08

Posted 11 October 2009 - 11:37 PM

Very nice tutorial pbl, i will keep this in mind for sure :pirate:
Was This Post Helpful? 0
  • +
  • -

#4 marvin lumanas  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 1
  • Joined: 15-October 09

Posted 15 October 2009 - 01:40 AM

View Postpbl, on 6 Jul, 2009 - 08:43 PM, said:

OK this is the basic in all page flickering and how to avoid flickering
First a basic concept:
- computer screens are refreshed 60 times a second in North America and 50 times a second in Europe
Java (and Swing) do a good job at it and refresh the screen the best way it can manage but some times it is nto the best way so sometimes, particularly in Games, you will need to do the job and help a little bit the Java GUI for best performance.

How to do it ? I'll teach you right away.
Sorry for my European and Asiatic friend, I'll do it the Obama way :D

The idea is to draw what you have to draw on 2 buffers, wait for the part of the GUI that repaint the screen, and tell it from which buffer in memory it has to redraw the image

I have 3 classes here... feel free to cut & paste them

OK the first one in to create the main frame... you are use to it
and we will use a Canvas to do our drawing... Canvas are needed because we can apply a BfferStartegy to them
BuffeStartegy is the bumber of buffers we will use to draw "offscreen".
Here is a first glitch/trap: before applying a buffer strategy to a Component this component has to be visible so here is my first class
For this example we will use the stupid "bouncing ball" program:
import java.awt.BorderLayout;
import javax.swing.*;


public class GameFrame extends JFrame {
	GameFrame() {
		// frame description
		super("Bouncing ball");
		// our Canvas
		GameCanvas canvas = new GameCanvas();
		add(canvas, BorderLayout.CENTER);
		// set it's size and make it visible
		setSize(600, 400);
		setVisible(true);		
		// now that is visible we can tell it that we will use 2 buffers to do the repaint
		// befor being able to do that, the Canvas as to be visible
		canvas.createBufferStrategy(2);
	}
	// just to start the application
	public static void main(String[] args) {
		// instance of our stuff
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				new GameFrame();
			}
		});
	}
}


To this JFrame we will add a Canvas, because Canvas support BufferStrategy
This class that extends Canvas will use a Timer to be recalled 60 times a second... this is everytime the hardware refresh the screen
60 times (in NorthAmerica) a second is every 16/1000 a second
There are no need to refresh the screen more often... if you do so
1) the human eye won't see it
2) you will ask the blitter to show something and then to show something else and only the last one will be displayed
import java.awt.*;
import java.awt.image.BufferStrategy;
import java.util.Random;

import javax.swing.Timer;

// OK this the class where we will draw
public class GameCanvas extends Canvas{
	// the initial position of the ball in the canvas that will be determine randomly
	int ballX, ballY;
	// the delta we apply to the x and y position at each repaint
	// here it is set to 1... would should have a larger value in "real" life
	// but for testing purpose that will give you the fastest possible value for a 1 pixel update
	int deltaX = +1, deltaY = +1;
	// the size of the ball in pixels
	final int BALLSIZE = 25;
	// a flag if repaint in progress (needed if our computation are to long)
	boolean repaintInProgress = false;
	// we use 2 pages to do our buffering
	// just for demo purpose we will change the background color everytime we repaint a frame
	// you will never do that in real life :-)
	Color[] back = {Color.YELLOW, Color.MAGENTA};
	// just used to determine on which buffer I am writting... will not be used in real life
	int page = 0;
	// a random object to determine where the initial position of the ball will be
	Random ran = new Random();
	// this is a Canvas but I wont't let the system when to repaint it I will do it myself
	GameCanvas() {
		// so ignore System's paint request I will handle them
		setIgnoreRepaint(true);
		// a random place to start the ball
		ballX = ran.nextInt(580);
		ballY = ran.nextInt(380);
		// build Chrono that will call me 
		Chrono chrono = new Chrono(this);
		// ask the chrono to calll me every 60 times a second so every 16 ms
		new Timer(16, chrono).start();
	}
	
	// my own paint method that repaint off line and switch the displayed buffer
	// according to the VBL
	public void myRepaint() {
		// wasting too much time doing the repaint... ignore it
		if(repaintInProgress)
			return;
		// so I won't be called 2 times in a row for nothing
		repaintInProgress = true;
		// get actual Canvas size so I can check if I am out of bounds
		Dimension size = getSize();
		// test for all debordement possibilities on the X axis
		if(deltaX > 0) {
			if(ballX > size.width - BALLSIZE)
				deltaX = -deltaX;
		}
		else {
			if(ballX < 0)
				deltaX = -deltaX;
		}
		// check on the Y axis
		if(deltaY > 0) {
			if(ballY > size.height - BALLSIZE)
				deltaY = -deltaY;
		}
		else {
			if(ballY < 0)
				deltaY = -deltaY;
		}
		// update ball position
		ballX += deltaX;
		ballY += deltaY;
		// ok doing the repaint on the not showed page
		BufferStrategy strategy = getBufferStrategy();
		Graphics graphics = strategy.getDrawGraphics();
		// this is for testing purpose you would not do that in real life
		// we change the background color to that you will see that the page are flipped
		page++;
		page %= 2;
		// again testing purpose we flip backgound color every repaint
		graphics.setColor(back[page]);
		graphics.fillRect(0, 0, size.width, size.height);
		// now we draw the ball
		graphics.setColor(Color.BLACK);
		graphics.fillOval(ballX, ballY, BALLSIZE, BALLSIZE);
		if(graphics != null)
			graphics.dispose();
		// show next buffer
		strategy.show();
		// synchronized the blitter page shown
		Toolkit.getDefaultToolkit().sync();

		// ok I can be called again
		repaintInProgress = false;
	}
}


finally the Chrono class that recalls me 60 times a second
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

/** Will be called at each blitter page */
public class Chrono implements ActionListener {

	GameCanvas gc;
	// constructor that receives the GameCanvas that we will repaint every 60 milliseconds
	Chrono(GameCanvas gc) {
		this.gc = gc;
	}
	// calls the method to repaint the anim
	public void actionPerformed(ActionEvent arg0) {
		gc.myRepaint();
	}

}


This is the fastest way you can refresh a screen in Java
Change the deltaX and deltaY the ball will go faster

The keypoints:
- you can inform a Canvas that it should not do it's own repaint
- you can overload that repaint()
- if you do it by hand call the toolkit synch() method to synchronize the VBL

Hope this helps

how can i copy its to my netbeans??
Was This Post Helpful? 0
  • +
  • -

#5 ts230  Icon User is offline

  • D.I.C Head

Reputation: 11
  • View blog
  • Posts: 225
  • Joined: 11-July 09

Posted 21 October 2009 - 07:58 AM

I like your idea. I will definitely use it from time to time.
Was This Post Helpful? 0
  • +
  • -

#6 pbl  Icon User is offline

  • There is nothing you can't do with a JTable
  • member icon

Reputation: 8343
  • View blog
  • Posts: 31,890
  • Joined: 06-March 08

Posted 30 October 2009 - 03:41 PM

View Postmarvin lumanas, on 15 Oct, 2009 - 12:40 AM, said:

how can i copy its to my netbeans??

Just cut & paste it
Was This Post Helpful? 1
  • +
  • -

#7 Dophert  Icon User is offline

  • D.I.C Head

Reputation: 10
  • View blog
  • Posts: 60
  • Joined: 19-October 09

Posted 13 November 2009 - 08:40 AM

This is not the fastest way, but it's a lot better than just overriding the paint method.
Was This Post Helpful? -1
  • +
  • -

#8 espirator  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 10
  • Joined: 07-March 10

Posted 13 March 2010 - 05:14 AM

I find it's a good example but. Yes there is also a BUT :D

How can we make the background constant. i mean i would like to see it in the same color every minute.

If u help im gonna be thankful to you . Thanks :)
Was This Post Helpful? 0
  • +
  • -

#9 NeoTifa  Icon User is online

  • Whorediot
  • member icon





Reputation: 2778
  • View blog
  • Posts: 15,880
  • Joined: 24-September 08

Posted 12 August 2010 - 11:26 AM

Don't do the whole back[page] thing? XD

And stupid boucing ball thing? That's not like my peebles. :(
Was This Post Helpful? 0
  • +
  • -

#10 stalks  Icon User is offline

  • New D.I.C Head

Reputation: 8
  • View blog
  • Posts: 20
  • Joined: 07-October 10

Posted 07 October 2010 - 02:09 PM

Nice tutorial.

Just one point about it: as the javax.swing.Timer tasks will always be called from the event dispatch thread, there's no need for the repaintInProgress flag or functionality. The actionPerformed method will only ever be called by a single thread.
Even if it was used in code where it was called by multiple threads it wouldn't actually be thread safe anyway though.
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1