Graphics experts.. help me out.
I need to know the proper way to double buffer graphics to remove the flicker in animation.
Do you 1) Draw the graphics to separate images and then draw the images at different locations on the screen.
Or 2) do you draw all the graphics to one big image at different locations on the image, and then draw the image at a fixed location on the screen.
I tried number two, but the game I made to do this runs really slow. Number one works, but only with Microsoft VM with the Java Plug-in, it leaves a trail behind it.
Is there a different way to do this? Any suggestions, or "the way to do its" would be kool.
[638 byte] By [
javatypo] at [2007-9-27 20:19:38]

And the code for my game that I need double buffering for is at http://www.geocities.com/jvp02/CometBall.javaand the game: http://www.geocities.com/jvp02/cometball.html
I changed some code of yours, but I don't know if it works as good as you want :
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
public class CometBall extends Applet implements Runnable,KeyListener,ActionListener,MouseMotionListener {
int ballX = 300;
int ballY = 100;
int ballXOld=ballX,ballYOld=ballY;
int ballXspeed = 2;
int ballYspeed = -2;
int paddleX = 250;
int paddleXOld=paddleX;
int score = 0;
Thread t;
volatile boolean playing = false;
Image ball, paddle;
Button startbutton;
public void init() {
setBackground(Color.black);
addKeyListener(this);
startbutton = new Button("Start");
startbutton.addActionListener(this);
add(startbutton);
ball = createImage(20,20);
Graphics gfxball = ball.getGraphics();
gfxball.setColor(new Color(0,246,255));
gfxball.fillOval(3,3,10,10);
paddle = createImage(65,25);
Graphics gfx2 = paddle.getGraphics();
gfx2.setColor(Color.white);
gfx2.fillRoundRect(7,7, 50,5, 4,4);
}
public void startPlaying() {
ballXOld=ballX;
ballYOld=ballY;
ballX = 300;
ballY = 100;
playing = true;
t = new Thread(this);
t.start();
requestFocus();
}
public void paint(Graphics g) {
// draw the ball
/*ball = createImage(20,20);
Graphics gfxball = ball.getGraphics();
gfxball.setColor(new Color(0,246,255));
gfxball.fillOval(3,3,10,10);*/
g.drawImage(ball, ballX,ballY, this);
// draw the paddle
/*paddle = createImage(65,25);
Graphics gfx2 = paddle.getGraphics();
gfx2.setColor(Color.white);
gfx2.fillRoundRect(7,7, 50,5, 4,4);*/
g.drawImage(paddle, paddleX,460, null);
//score board and playing area
g.setColor(Color.red);
g.drawLine(525,20,525,495);
Image scoreboard = createImage(100,20);
Graphics gfx = scoreboard.getGraphics();
gfx.setColor(Color.red);
gfx.drawString("Score: "+score, 5,10);
g.drawImage(scoreboard, 530,20, this);
g.setColor(Color.red);
g.drawLine(20,495, 525,495);
}
public void run() {
while (playing) {
for (int i=0; i<=51; i++) {
if (ballY==448 && ballX==(((paddleX-4)+i)+4)) { ballYspeed*=-1; score+=5; }
}
ballXOld=ballX;ballYOld=ballY;
ballX += ballXspeed;
ballY += ballYspeed;
if (ballX == 470) ballXspeed *= -1;
if (ballX == 10) ballXspeed *= -1;
if (ballY == 480) playing = false;
if (ballY == 30) ballYspeed *= -1;
updateBall();
try { Thread.sleep(10); }
catch (Exception e) { e.printStackTrace(); }
}
}
/*public void update(Graphics g) {
paint(g);
}*/
public void keyPressed(KeyEvent ke) {
paddleXOld=paddleX;
if (ke.getKeyCode() == 39) {
if (paddleX <= 450) {
paddleX+=7;
updatePaddle();
}
}
if (ke.getKeyCode() == 37) {
if (paddleX >= 10) {
paddleX-=7;
updatePaddle();
}
}
}
public void keyReleased(KeyEvent ke) {}
public void keyTyped(KeyEvent ke) {}
public void actionPerformed(ActionEvent e) {
if (e.getSource() == startbutton) {
startPlaying();
}
}
public void mouseMoved(MouseEvent me) {
if (paddleX <= 450) {
paddleX=me.getX();
repaint();
}
}
public void mouseDragged(MouseEvent me) {}
public void updateBall(){
repaint(ballX,ballY,20,20);
repaint(ballXOld,ballYOld,20,20);
}
public void updatePaddle(){
repaint(paddleX,460,65,25);
repaint(paddleXOld,460,65,25);
}
}
There is no need in double buffering in this case, try this:
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
public class CometBall extends Applet implements Runnable,KeyListener,ActionListener,MouseMotionListener
{
volatile boolean playing = false;
int ballX= 300;
int ballY= 100;
int ballXspeed = 2;
int ballYspeed = -2;
int paddleX= 250;
int score= 0;
Thread t;
Image ball, paddle;
Button startbutton;
public void init()
{
setBackground(Color.black);
addKeyListener(this);
startbutton = new Button("Start");
startbutton.addActionListener(this);
add(startbutton);
}
public void startPlaying()
{
ballX = 300;
ballY = 100;
playing = true;
t = new Thread(this);
t.start();
requestFocus();
}
public void paint(Graphics g)
{
Rectangle r = g.getClipBounds();
g.setColor(Color.black);
g.fillRect(r.x,r.y,r.width,r.height);
// draw the ball
g.setColor(new Color(0,246,255));
g.fillOval(ballX,ballY,10,10);
// draw the paddle
g.setColor(Color.white);
g.fillRoundRect(paddleX,467,50,5,4,4);
//score board and playing area
g.setColor(Color.red);
g.drawLine(525,20,525,495);
g.drawString("Score: "+score, 530,30);
g.setColor(Color.red);
g.drawLine(20,495, 525,495);
}
public void run()
{
while (playing)
{
repaint(ballX-1,ballY-1,13,13);
if (ballY+10 > 467 && ballY+10 < 472 &&ballX+10 > paddleX && ballX+10 < paddleX+50)
{
ballYspeed*=-1; score+=5;
}
ballX += ballXspeed;
ballY += ballYspeed;
if (ballX == 470) ballXspeed *= -1;
if (ballX == 10) ballXspeed *= -1;
if (ballY == 480) playing = false;
if (ballY == 30) ballYspeed *= -1;
repaint(ballX-1,ballY-1,13,13);
try { Thread.sleep(10); }
catch (Exception e) { e.printStackTrace(); }
}
}
public void update(Graphics g)
{
paint(g);
}
public void keyPressed(KeyEvent ke)
{
if (ke.getKeyCode() == 39)
{
if (paddleX <= 450)
{
repaint(paddleX,467,50,5);
paddleX+=11;
repaint(paddleX,467,50,5);
}
}
if (ke.getKeyCode() == 37)
{
if (paddleX >= 10)
{
repaint(paddleX,467,50,5);
paddleX-=11;
repaint(paddleX,467,50,5);
}
}
}
public void keyReleased(KeyEvent ke) {}
public void keyTyped(KeyEvent ke) {}
public void actionPerformed(ActionEvent e)
{
if (e.getSource() == startbutton)
{
repaint();
startPlaying();
}
}
public void mouseMoved(MouseEvent me)
{
if (paddleX <= 450)
{
paddleX=me.getX();
repaint();
}
}
public void mouseDragged(MouseEvent me) {}
}
Noah
Who about this, i added the use of mouse:
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
public class CometBallG extends Applet implements Runnable,KeyListener,ActionListener,MouseMotionListener
{
volatile boolean playing = false;
Rectangle ball= new Rectangle(300,100,10,10);
Rectangle paddle = new Rectangle(0,469,50,5);
Rectangle tscore = new Rectangle(530,30,100,25);
Graphics g;
int ballXspeed = 2;
int ballYspeed = -2;
int score= 0;
Thread t;
Button startbutton;
public void init()
{
setBackground(Color.black);
addKeyListener(this);
addMouseMotionListener(this);
startbutton = new Button("Start");
startbutton.addActionListener(this);
add(startbutton);
g = getGraphics();
}
public void startPlaying()
{
ball.x = 300;
ball.y = 100;
playing = true;
t = new Thread(this);
t.start();
requestFocus();
}
public void run()
{
while (playing)
{
clear(g,ball);
ball.x += ballXspeed;
ball.y += ballYspeed;
if (ball.x > 513) ballXspeed *= -1;
if (ball.x < 0)ballXspeed *= -1;
if (ball.y > 480) playing= false;
if (ball.y < 30)ballYspeed *= -1;
paintBall(g);
if (paddle.intersects(ball))
{
ballYspeed*=-1;
clear(g,ball);
ball.y += ballYspeed;
paintBall(g);
paintPaddle(g);
clear(g,tscore);
score+=5;
paintScore(g);
}
try { Thread.sleep(10); }
catch (Exception e) { e.printStackTrace(); }
}
}
public synchronized void clear(Graphics g, Rectangle r)
{
g.setColor(Color.black);
g.fillRect(r.x,r.y,r.width,r.height);
}
public void paintBall(Graphics g)
{
g.setColor(new Color(0,246,255));
g.fillOval(ball.x,ball.y,10,10);
}
public void paintPaddle(Graphics g)
{
g.setColor(Color.white);
g.fillRoundRect(paddle.x,paddle.y,paddle.width,paddle.height,4,4);
}
public void paintScore(Graphics g)
{
g.setColor(Color.red);
g.drawLine(525,20,525,495);
g.drawString(" Score: "+score,tscore.x,tscore.y+14);
g.setColor(Color.red);
g.drawLine(20,495, 525,495);
}
public void update(Graphics g)
{
paint(g);
}
public void paint(Graphics g)
{
super.paint(g);
paintScore(g);
paintPaddle(g);
}
public void keyPressed(KeyEvent ke)
{
if (ke.getKeyCode() == 39)
if (paddle.x <= 450)
{
clear(g,paddle);
paddle.x+=11;
paintPaddle(g);
}
if (ke.getKeyCode() == 37)
if (paddle.x >= 10)
{
clear(g,paddle);
paddle.x-=11;
paintPaddle(g);
}
}
public void keyReleased(KeyEvent ke) {}
public void keyTyped(KeyEvent ke) {}
public void actionPerformed(ActionEvent e)
{
if (e.getSource() == startbutton)
{
clear(g,getBounds());
paintScore(g);
paintPaddle(g);
startPlaying();
}
}
public void mouseMoved(MouseEvent me)
{
if (me.getY() < paddle.y - 20) return;
clear(g,paddle);
paddle.x = me.getX();
if (paddle.x < 0) paddle.x = 0;
if (paddle.x > 525-paddle.width) paddle.x = 525-paddle.width;
paintPaddle(g);
}
public void mouseDragged(MouseEvent me) {}
}
Noah
It's been a couple years since I dabbled in game programming. But basically a double buffer is a virtual screen which you do all your drawing to. Then you just make a single call to copy the buffer to the visible screen.
So yes, the buffer would be a blank image the exact same size as the screen. You draw all your objects to it, just as if you were drawing the actual screen. The only difference is when you're done drawing you just copy the buffer to your visible screen.
Hope that helps in some way. It's a very simple concept, and eliminates the annoying flicker problem. In the old days a double buffer was more of a big deal because RAM was scarce and you couldn't afford to allocate a huge chunk of memory to do your offscreen drawing.
Thank you everyone. I'm at school right now so i'll try it out at home.Let you kno whow it turned out.
myavuzselim: Your code did more or less what I was looking for. noah.w : What does the g.getClipBounds() do?
In my samples there is no need of Images, in your code you are creating and displaying image and this takes resources.
In my first sample i use repaint(x,y,w,h), in the paint i use getClipBounds() to find out the area i need to paint and i draw the black rect only in this area (to erase the ball/paddle) and then i draw the ball and the paddle.
The second sample is a different approach.
Noah
noah.w: the second way is really good. I tried it but it didn't work, so I completely rewrote my code using the concept that you showed me. Now it works really good.
check it out: http://www.geocities.com/jvp02/cometball.html
thanx. now i'll ive got to do is experiment with that code and try out other things like 2d shooters. Than i'll know how to make games.