An efficient discrete step timing loop.

[quote]Do you yourself find this necessary to do in order to prevent image stutter?
[/quote]
Nope. I think your stuttering is caused by something else.

[quote]Perhaps your particular app would be the deciding factor.
[/quote]
Yes, definitly.
One thing this system handles poorly is something simple as a ball bouncing against a surface. If the actual “bounce” is between two game ticks, the linear interpolation will cause the ball to magically hover above the ground for a full gametick instead of bouncing on the surface.
This could be solved by adding interpolation keyframes, but then we’re deep in gc hell.

Hi,

First post. Learned a heck of a lot from you guys- thanks.

Question- while testing this game loop, I notice that my fps seem to be limited to the refresh rate of the screen mode I switch into. Is this a Window’s “feature”?

Edit: Hmm- I just found an answer: http://www.xp-refresh.net/

Also- I understand what vertical synch is all about, but I don’t yet see how to “do it” with pure Java. (I do see how to do it via LWJGL- very nice btw). I currently get big time flashing- which is what I’d expect if my buffer.show() isn’t synched with monitor’s vsynch and their respective rates aren’t perfectly in synch. But I’m a big time newb- maybe I’m missing something.

Could someone give me some pointers, advice, links?
Thanks a lot!

Well, I got rid of the flicker by adding a timer.sleep(22000).
I’m using the GAGE AdvancedTimer.
I now get 66 fps while @ 1024x768, 85Hz.

I’m assuming that I don’t see the flicker anymore because the 85Hz sufficiently covers the 66fps and the beat frequency becomes unnoticeable. (yes/no/sorta?)

Still feel like I’m at the mercy of some mysterious forces.
BTW: WinXP home edition, 1.8GHz, NVidia TNT2 Model 64- 32Mb.

Welcome.
Your question seems a bit off topic to this thread and you might want to post some code to make yourself a bit more clear.

Anyway, it seems like the flickering has something to do with buffering (you are double or triple buffering are you?) rather than vertical sync (not syncing to the monitor will not result in flicker, but in ‘tearing’).
I think using BufferStrategy will sync to the monitor’s refresh rate, unless this has been disabled in your video driver.

Hope this helps,
Erik

Its easy to check your render code.

Remove all rendering, do only buffer flips ( and maybe background clearing )

If the FPS u get is NOT the Refresh Rate of the current mode, your code is buggy.

This is only true for fullscreen mode, in windowed mode you need some special tweaks.

Your app performance is only 77% thats a really bad performance, maybe later it drops below 60fps and below 60Hz its unusable for RT games

In Markus_Persson’s original code, there was a timer.getTimeInMillis(). I accidentally used the AdvancedTimer’s getClockTicks(), which is NOT in milliseconds. Things behave as expected, no more sleeping either, when I slipped in System.currentTimeMillis() temporarily.
I need to do some math in order to correctly use the AdvancedTimer. Thanks.

What do you mean with “Unit”? Where is the unit defined? is it the “xPos+=1” or the "xPlayerPos.getValue(1) ??

I’like to speed up the movement of my object. If I try e.g. “xPos+=10” the object moves faster, but is hopping between several pixels (according to the interpolation-value) when there is no further key event.

Well “unit” isn’t an actual variable or anything. It’s just the distance you want to move in a given amount of time. That could be 200 pixels per second or 1 inch per minute, etc. So, in the example, clientTick is the actual framerate you get that may or may not fluctuate wildly from one second to the next, while gameTick is sort of the theoretical, constant framerate that you want to achieve a.k.a the “unit” value. The interpolation mechanism, then, sort of works as an intermediary between the actual and theoretical framerate to keep the sprite moving at constant “unit” increments. Thus, if you want ALL of your movement to appear to move faster, you would increase your “unit” value, which, in the case of the example given would be the value of GAMETICKS_PER_SECOND. Anyway, that’s my current understanding of it…

Hmmm its the sun is shining outside and its brightly, is this a real life feature ?

the fps is directly connected to your monitor refresh rate, a 60Hz refresh means f = 60[s**-1] so a T cycle is T = 1/f = 16.66667 ms

At the beginning of every T cycle your monitor will show a new picture from VRAM, if your FPS exceeds the monitor refresh rate, guess what happens, if u update VRAM in the middle of the T cycle you will not see any new picture on the monitor, simple isnt it !

The VSync marks the beginning of a new T cycle, the buffer show() method waits until a new T cycle is started

Go to your local bookstore and buy some books about game physics, or go to a company where u can learn this profession, professional game developer, its not easy but after 5 or 6 years you can program your own game

[quote]Well “unit” isn’t an actual variable or anything. It’s just the distance you want to move in a given amount of time. That could be 200 pixels per second or 1 inch per minute, etc. So, in the example, clientTick is the actual framerate you get that may or may not fluctuate wildly from one second to the next, while gameTick is sort of the theoretical, constant framerate that you want to achieve a.k.a the “unit” value. The interpolation mechanism, then, sort of works as an intermediary between the actual and theoretical framerate to keep the sprite moving at constant “unit” increments. Thus, if you want ALL of your movement to appear to move faster, you would increase your “unit” value, which, in the case of the example given would be the value of GAMETICKS_PER_SECOND. Anyway, that’s my current understanding of it…
[/quote]
I still got a problem. I have a simple application where a spaceship can be moved along the x-axis of the screen.
the movement (pixels/second) is very slow with the abve example (but smooth). I can’t figure out how to speed up the movement of them ship. If I set the increment to a higher value, the movement becomes inconsistent.

here’s my implementation of the loop:



public class GameWindow extends JFrame implements Runnable {

  private static final int GAMETICKS_PER_SECOND = 25; 
  private static final int MILLISECONDS_PER_GAMETICK = 1000 / GAMETICKS_PER_SECOND;   
  private static final int screenwidth = 800;
  private static final int screenheight = 600;
  private ScenePanel pScene = new ScenePanel(this);  
  boolean running = true;
  boolean left = false;
  boolean right = false;
  boolean fire = false;
  boolean lock = false;
  int fps;
  int dir = 1; // TEST: enemy automove direction

  Ship player = new Ship("gfx/bat.gif", 480, 4, 8, 100,500);
  Ship enemy = new Ship("gfx/cow.gif", 40, 4, 8, 100, 500);

  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.setContentPane(pScene); 
    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 ) {
        fire = true;
        player.shoot(player.posX+player.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() {
      long previousTime = System.currentTimeMillis(); 
      long passedTime = 0;
      long frametime = previousTime;
      int framecount = 0;  
      while (running)      {  
              float interpolation = passedTime/(float)MILLISECONDS_PER_GAMETICK; 
              clientTick(interpolation);   
              long time = System.currentTimeMillis();  
              passedTime += (time-previousTime);  
              previousTime = time;  
              while (passedTime>MILLISECONDS_PER_GAMETICK)  
              {  
                  framecount++;
                  if (time > (frametime + 999)) {
                    fps = framecount;
                    framecount = 0;
                    frametime = time;
                  }
                  gameTick();  
                  passedTime-=MILLISECONDS_PER_GAMETICK;  
              }  
      } 
  }
      
  public void clientTick(float interpolation) {
      player.posX =(int) player.xIFloat.getValue(interpolation); 
      enemy.posX =(int) enemy.xIFloat.getValue(interpolation);
      repaint();        
  }
  
  public void gameTick() {
      if (left) {
        if (player.posX > 8) {      
            player.xFloatPos = player.xIFloat.getValue(1); 
            player.xFloatPos-=1; 
            player.xIFloat.setValue(player.xFloatPos);
        }
        else {
            player.posX = 8;
        }
      }
      if (right) {
        if (player.posX < screenwidth-player.w-12) {
            player.xFloatPos = player.xIFloat.getValue(1); 
            player.xFloatPos+=1;
            player.xIFloat.setValue(player.xFloatPos);
        }
        else {
            player.posX = screenwidth-player.w-12;
        }
      }
  }

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

} // End Class GameWindow

I think the problem is that you base the timing on System.currentTimeMillis(). It has an extremely poor resolution on Windows.

In your example you had a “timer” object that picked the current time. But how can I create such an object?

I heard of the GAGE-Engine but I don’t want to use external Libraries in my project.

Are there some other useful timers in the JDK?

I personally use the timer included in Gage.
There is one included somewhere in java 1.4.2, but it’s fairly hidden and unsupported (I’ve never used it personally) that has been confirmed to be officially included in 1.5 iirc.

Here is my implementation of the hidden high performance timer in 1.4.2. There has been alot of discussion about it already in the various forums if you want more information about it.


public class PerfTimer
{
      private sun.misc.Perf hiResTimer;
      private long freq;
      
      public PerfTimer()
      {
            hiResTimer = sun.misc.Perf.getPerf();
            freq = hiResTimer.highResFrequency();
      }

      //return the number of nanoseconds per clock tick
      public long getResolution() 
      {
            return (long)((1.0/freq) * 1000000000L);
      }

      //return the current time in milliseconds
      public long getCurrentTimeMillis()
      {
            return hiResTimer.highResCounter() / ((freq + 500L) / 1000L);
      }
}

[quote]I personally use the timer included in Gage.
There is one included somewhere in java 1.4.2, but it’s fairly hidden and unsupported (I’ve never used it personally) that has been confirmed to be officially included in 1.5 iirc.
[/quote]
The sun.misc.Perf timer is NOT the same timer as the one that will be in 1.5. The 1.5 timer is System.nanoTime()

I knew that! I was just testing you!

runs off to hide in a corner