OMG it is still flickering.

Hi folks!

I was trying to create a small space-shooter (pong style) to get some experience with java 2D and realtime rendering.

a jar containing a runnable version can be found at: http://www.hdm-stuttgart.de/~ml18/jumat/jumat.zip

My problem is, that the movement of the ships is still flickering although I am using double-buffering.

Further implementations should be socket or RMI based multiplayer so I decided to run the game in a seperate thread (perhaps this could be useful later, but don’t know exactly)

But even if I remove the Thread and run the game in the main Thread (it gets up to 30000fps) the flickering still exists :frowning:

Here’s the main part of the code:

The Frame containg a JPanel in the center:


public class GameWindow extends JFrame implements Runnable {

  private ScenePanel pScene = new ScenePanel(this);
  BorderLayout borderLayout1 = new BorderLayout();
  boolean running = true;
  boolean left = false;
  boolean right = false;
  boolean fire = false;
  boolean lock = false;

  int screenwidth = 800;
  int screenheight = 600;
  int fps;
  int dir = 1; // TEST: Player2 automove direction
  float frameduration;
  Ship player1 = new Ship("gfx/bat.gif", 368, 480, 300, 800, 100);
  Ship player2 = new Ship("gfx/cow.gif", 368, 40, 250, 850, 100);

  public GameWindow() {
    this.setResizable(false);
    this.setTitle("Spacefight");
    this.addKeyListener(new java.awt.event.KeyAdapter() {
      public void keyPressed(KeyEvent e) {
        this_keyPressed(e);
      }
    });
    this.addKeyListener(new java.awt.event.KeyAdapter() {
      public void keyReleased(KeyEvent e) {
        this_keyReleased(e);
      }
    });
    this.getContentPane().setLayout(borderLayout1);
    this.getContentPane().add(pScene,  BorderLayout.CENTER);
    setSize(screenwidth, screenheight);
      
      Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
      Dimension size = getSize();
      screenSize.height = screenSize.height/2;
      screenSize.width = screenSize.width/2;
      size.height = size.height/2;
      size.width = size.width/2;
      int y = screenSize.height - size.height;
      int x = screenSize.width - size.width;
      setLocation(x, y);

      
    setVisible(true);
    addWindowListener(new WindowAdapter() {
      public void windowClosing(WindowEvent e) {
        System.exit(0);
      }
    });
  }
  void this_keyPressed(KeyEvent e) {
    if (e.getKeyCode() == KeyEvent.VK_SPACE) {
      if(!lock) {
        lock = true;
        fire = true;
        player1.shot.x = player1.posX+player1.w/2 - player1.shot.w/2;
      }
    }
    if (e.getKeyCode() == KeyEvent.VK_LEFT) {
      right = false;
      left = true;
    }
    if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
      right = true;
      left = false;
    }
  }
  void this_keyReleased(KeyEvent e) {
    if (e.getKeyCode() == KeyEvent.VK_LEFT) {
      left = false;
    }
    if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
      right = false;
    }
  }

  public void run() {
    int count = 0;
    long fpsStartTime = System.currentTimeMillis();
    long  startTime;

    synchronized (this) {
      while (running) {
        startTime = System.currentTimeMillis();
        {try {this.wait(1);}  catch (Exception e) {}}
        pScene.repaint();
        count++;
        if (System.currentTimeMillis() > (fpsStartTime + 999)) {
          fps = count;
          count = 0;
          fpsStartTime = System.currentTimeMillis();
        }
        frameduration = (System.currentTimeMillis() - startTime) / 1000.000f;
        // Begin of Player2 automove
        if (dir==0) {
          if (player2.posX > 8) {
            player2.posX -= player2.movespeed * frameduration;
          }
          else {
            player2.posX = 8;
            dir = 1;
          }
        }
        if (dir==1) {
          if (player2.posX < screenwidth-player2.w-12) {
            player2.posX += player2.movespeed * frameduration;
          }
          else {
            player2.posX = screenwidth-player2.w-12;
            dir = 0;
          }
        }
        // End of Player2 automove
        if (left) {
          if (player1.posX > 8) {
            player1.posX -= player1.movespeed * frameduration;
          }
          else {
            player1.posX = 8;
          }
        }
        if (right) {
          if (player1.posX < screenwidth-player1.w-12) {
            player1.posX += player1.movespeed * frameduration;
          }
          else {
            player1.posX = screenwidth-player1.w-12;
          }
        }
        if (fire) {
          player1.shot.y -= player1.firespeed * frameduration;
          if (player2.checkHit(player1.shot)) {
            player2.hitpoints -= player1.shot.dmg;
            player1.shot.y = player1.shot.initY;
            fire = false;
            lock = false;
            if (player2.hitpoints <= 0) {
              player2.hitpoints = 100; // TEST: explosion will follow. GAME OVER
            }
          }
          if (player1.shot.y <= 0) {
            player1.shot.y = player1.shot.initY;
            fire = false;
            lock = false;
          }
        }
      }
    }
  }

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

}

The JPanel where the drawing takes place


class ScenePanel extends JPanel {

   GameWindow gw;
   Image back = loadImage("gfx/stars0001.jpg");
   private int bufferWidth;
   private int bufferHeight;
   private Image bufferImage;
   private Graphics bufferGraphics;

   private MediaTracker mt;

   ScenePanel(GameWindow gw) {
     this.gw = gw;
   }

   public Image loadImage(String file) {
     Image img = Toolkit.getDefaultToolkit().getImage(file);
     mt = new MediaTracker(this);
     mt.addImage(img, 0);
     try {
       mt.waitForAll();
     }
     catch (InterruptedException e) { e.printStackTrace(); }
     return img;
   }

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

   public void paint(Graphics g){
     if(bufferWidth!=getSize().width || bufferHeight!=getSize().height || bufferImage==null || bufferGraphics==null)
       resetBuffer();
     if(bufferGraphics!=null){
       bufferGraphics.clearRect(0,0,bufferWidth,bufferHeight);
       paintBuffer(bufferGraphics);
       g.drawImage(bufferImage,0,0,this);
     }
   }

   private void resetBuffer(){
     bufferWidth=getSize().width;
     bufferHeight=getSize().height;
     if(bufferGraphics!=null){
       bufferGraphics.dispose();
       bufferGraphics=null;
     }
     if(bufferImage!=null){
       bufferImage.flush();
       bufferImage=null;
     }
     System.gc();
     bufferImage=createImage(bufferWidth,bufferHeight);
     bufferGraphics=bufferImage.getGraphics();
   }


   public void paintBuffer(Graphics g) {
     g.drawImage(back, 0, 0, this);
     g.drawImage(gw.player1.img, gw.player1.posX, gw.player1.posY, this);
     g.drawImage(gw.player2.img, gw.player2.posX, gw.player2.posY, this);
     g.setColor(Color.green);
     g.drawRect(gw.player2.posX, gw.player2.posY, gw.player2.w, gw.player2.h);
     g.drawRect(gw.player1.posX, gw.player1.posY, gw.player1.w, gw.player1.h);
     if (gw.fire) {
       g.drawImage(gw.player1.shot.img, gw.player1.shot.x, gw.player1.shot.y, this);
     }
     g.setColor(Color.red);
     g.fillRect(636,10,(int)(gw.player2.hitpoints*1.5),10);
     g.fillRect(636,553,(int)(gw.player1.hitpoints*1.5),10);
     g.setColor(Color.orange);
     g.drawString("FPS: " + gw.fps, 4, 16);

   }
 }

Would be really cool if someone could help me figure out where the flicker comes from :slight_smile:

It might be something to do with this:

Since the content pane that exists might be doing the clear for you… try doing this instead:


this.setContentPane(pScene);

Kev

Having looked again, it might also be your


pScene.repaint()

in the running thread. I believe repaint() schedules a repain of the surface as soon as possible, so you don’t actually guarantee that a rendering has taken place by the time its returned (this would explain the extraordinary frame rates).

Calling repaint(0) where the 0 is the time allowed before the repaint actually happens has worked for me in the past, but there may be a more correct way of doing it. repaint(0) will indicate that the repaint must occur immediately.

Kev

A big thanks so far, Kev :slight_smile:

I’ll try your proposals as soon as I’m getting eclipse running on this PC where I’m currently sitting :wink:

with “replay(0);” there’s a slight improvement (couldn’t say exactly because there is still flickering)

the setContent did nothing to improve the performance.

:frowning:

any other ideas? ??? ::slight_smile:

Finally on a machine where I can download and try,

Aww, no source! :slight_smile:

Interesting, I don’t get any global flickering… is it just the ships you’re worried about? I get an effect around the ships, but I think its more to do with the screen updating out of sync with the game giving that shimmering around the ships…

Kev

Hmmm, I ran it on my box and to me it actually doesn’t look like it’s flicker per se…more like it’s skipping a few pixels every so often as it slides back and forth. It seems to me you need a way to interpolate the position of your spaceship so that when your framerate drops, your ship doesn’t “hop” so much. Maybe this thread might help? http://www.java-gaming.org/cgi-bin/JGNetForums/YaBB.cgi?board=Tuning;action=display;num=1058296748

And maybe a couple of other ideas if that’s not it. Did you double check that your JFrame’s double buffering didn’t get turned of by checking isDoubleBuffered()? Also is pScene.repaint() trying to paint the entire window? You might already know this but you would get much better performance just repainting the square surrounding the spaceship if this is not already the case.

Unless this is an applet game I’d strongly recommend dropping the repaint() approach in favor of an active rendering loop. As i think has already been pointed out 3000 calls to repaint doesn’t necc equal 3000 frames as the system is free to coalesce repaint attempts.

I also note that you are calling repaint on the entire panel.

It doesnt look like you’ve done anything to disable background clearing. If the background is getting cleared each frame that could give you a flicker. Disable the clear and just do dirty rectangle update.

OR as I said before go to active rendering and just flip frames.
Thats the best solution as long as you dont need to run under Java 1.1

Trying to remember from the “old days” but doesn’t overriding update(Graphics g) prevent background clearing?

Kev

[quote]Unless this is an applet game I’d strongly recommend dropping the repaint() approach in favor of an active rendering loop. As i think has already been pointed out 3000 calls to repaint doesn’t necc equal 3000 frames as the system is free to coalesce repaint attempts.
[/quote]
Please forgive me my lack of knowledge but what exactly is an active rendering loop??

I´’ll try the timing-loop algo posted in the other topic. It looks very suitable for me :slight_smile: Hope it works.

thx so far… :slight_smile:

I’ll post my results here if sth important happens :wink:

kev: yes. Overriding update will remove the flicker associated with background clearing.

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

}

just learned that looking at javazoid’s interpolation bug jar :wink:

See my Scroller example in the code area OR this article:

http://java.sun.com/docs/books/tutorial/extra/fullscreen/

Where does your scroller code live now Jeff?

Kev

PS. Jeff, a God? Fear thee all.

Is this the latest and greatest scroller code by chance?
https://misc-demos.dev.java.net/source/browse/misc-demos/

I was just looking at it recently which is why I had it handly. By the way, thanks for the great example Jeff, it’s helped me out quite a bit in learning some of this stuff.

Yep, thats my demo. Cleaned up some by GhergisKahn so thank him too :slight_smile:

JK

ok, now :slight_smile:

with the gage-timer it runs a lot smoother.

thx a lot :slight_smile:

btw. can I download the scoller example as a zip file somewhere?

The link is at the bottom of the previous page.

30000 fps? wow!

he wasn’t getting 30000 fps. He was getting 30000 calls to repaint per sec, which as already explained has no direct correlation to frame rate.