Java-Gaming.org    
Featured games (81)
games approved by the League of Dukes
Games in Showcase (498)
Games in Android Showcase (117)
games submitted by our members
Games in WIP (563)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  How can I make this simple game loop better?  (Read 10989 times)
0 Members and 1 Guest are viewing this topic.
Offline jasonjohnson

Senior Newbie





« Posted 2009-02-24 21:24:58 »

I've been having a bit of trouble coming up with a decent game loop (or maybe this is decent, I just feel it could be better). Taken directly from the game I'm working on, my loop is below:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
public void loop() {
   Date startTime = new Date();
   Date previousRepaint = startTime;
   Date currentTime = startTime;
   float delta;
   float redrawsPerSecond = 1000/maxFps;
   
   // While the game is not over...
  while(!gameOver) {
      currentTime = new Date();
      delta = currentTime.getTime()-previousRepaint.getTime();
     
      update(delta);
      repaint();
     
      previousRepaint = currentTime;
     
      // Limit repaint to the max FPS.
     if(delta <= redrawsPerSecond) {
         try { Thread.sleep(10); } catch(Exception e) { /* pass */ }
      }
   }
}


I see this as a sort of passive limitation (using Thread.sleep() instead of limiting the redraws conditionally beforehand). One thing to note is when I leverage the delta properly, I can keep CPU usage down to between 15-20% with very smooth animation.

I'm really looking for any sort of advice on this. Is there generally a better way to go about the game loop? Is this code fine, but maybe a few tweaks here and there?

Offline h3ckboy

JGO Coder


Medals: 5



« Reply #1 - Posted 2009-02-24 21:35:23 »

are you using any libraries. cause if so. you can limit the updates in Slick. acn probably do someting similar in LWJGL and JOGL.
Offline jasonjohnson

Senior Newbie





« Reply #2 - Posted 2009-02-24 21:46:09 »

I'm not currently using any libraries. I'm literally just casting the Graphics object that is sent to the paint() methods I'm overriding up to a Graphics2D object and turning on anti-aliasing with the proper RenderingHints.

I will eventually switch to something a bit more powerful (and more appropriate) like Slick, LWJGL or JOGL for more complex games, but I wanted to get a good grasp of the underlying 2D rendering capabilities.

Any thoughts on the game loop itself, though?

Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline Wildern

Junior Member





« Reply #3 - Posted 2009-02-25 00:15:50 »

Your loop will be jerky depending on what is going on between/during frame renders.  If cpu usage stays constant, your updates will be smooth, but get a cpu usage spike and you get a jerk.

I would recommend having a variable that holds your desired frames/second and using that in combination with your delta to calculate how long you need to sleep.

This is pretty much what I use in my game loop.
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
   private static final int DELAYS_PER_YIELD = 1000000000;
   private static final int MAX_FRAME_SKIPS = 5;
   private static final long NANO_IN_MILLI = 1000000L;
   int fps = 60;
   long period = 1000L / (long)fps;
   boolean running = true;

   
   public void start() {
      new Thread() {
         public void run() {
            long overSleepTime = 0L;
            int noDelays = 0;
            long excess = 0L;
            Thread.currentThread().setPriority(10);
            long beforeTime = System.nanoTime();
            long lastRunTime = beforeTime;

            while (running) {
               long delta = System.nanoTime() - lastRunTime;
               lastRunTime = System.nanoTime();
               nextFrame(delta / NANO_IN_MILLI);

               render();

               long afterTime = System.nanoTime();
               long timeDiff = afterTime - beforeTime;
               long sleepTime = period - timeDiff - overSleepTime;
               if (sleepTime > 0L) {
                  try {
                     Thread.sleep(sleepTime / NANO_IN_MILLI, (int)(sleepTime % NANO_IN_MILLI));
                  } catch (InterruptedException interruptedexception) { }
                  overSleepTime = System.nanoTime() - afterTime - sleepTime;
               } else {
                  excess -= sleepTime;
                  overSleepTime = 0L;
                  if (++noDelays >= DELAYS_PER_YIELD) {
                     Thread.yield();
                     noDelays = 0;
                  }
               }
               beforeTime = System.nanoTime();
               int skips;
               for (skips = 0; excess > period && skips < MAX_FRAME_SKIPS; skips++) {
                  excess -= period;
                  nextFrame(period / NANO_IN_MILLI);
               }
               framesSkipped += skips;
            }
            System.exit(0);
         }
      }.start();
   }
Offline jasonjohnson

Senior Newbie





« Reply #4 - Posted 2009-02-26 00:53:30 »

Looks like a decent upgrade from my trivial game loop  Smiley

I'll dissect this and see where I can get in terms of performance. Thanks for sharing your loop. Anyone else have a game loop they'd be willing to share? Preferably not based on Slick or any other game library?

Offline zammbi

JGO Coder


Medals: 4



« Reply #5 - Posted 2009-02-27 12:27:45 »

Quote
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
   private static final int DELAYS_PER_YIELD = 1000000000;
   private static final int MAX_FRAME_SKIPS = 5;
   private static final long NANO_IN_MILLI = 1000000L;
   int fps = 60;
   long period = 1000L / (long)fps;
   boolean running = true;

   
   public void start() {
      new Thread() {
         public void run() {
            long overSleepTime = 0L;
            int noDelays = 0;
            long excess = 0L;
            Thread.currentThread().setPriority(10);
            long beforeTime = System.nanoTime();
            long lastRunTime = beforeTime;

            while (running) {
               long delta = System.nanoTime() - lastRunTime;
               lastRunTime = System.nanoTime();
               nextFrame(delta / NANO_IN_MILLI);

               render();

               long afterTime = System.nanoTime();
               long timeDiff = afterTime - beforeTime;
               long sleepTime = period - timeDiff - overSleepTime;
               if (sleepTime > 0L) {
                  try {
                     Thread.sleep(sleepTime / NANO_IN_MILLI, (int)(sleepTime % NANO_IN_MILLI));
                  } catch (InterruptedException interruptedexception) { }
                  overSleepTime = System.nanoTime() - afterTime - sleepTime;
               } else {
                  excess -= sleepTime;
                  overSleepTime = 0L;
                  if (++noDelays >= DELAYS_PER_YIELD) {
                     Thread.yield();
                     noDelays = 0;
                  }
               }
               beforeTime = System.nanoTime();
               int skips;
               for (skips = 0; excess > period && skips < MAX_FRAME_SKIPS; skips++) {
                  excess -= period;
                  nextFrame(period / NANO_IN_MILLI);
               }
               framesSkipped += skips;
            }
            System.exit(0);
         }
      }.start();
   }


I think the fps is broken, I tried setting it to very low but still keep on sucking up my cpu and rendering fast as possible.

You also in your example "framesSkipped += skips" does nothing.

Current project - Rename and Sort
Offline Mr_Light

Senior Member


Medals: 1


shiny.


« Reply #6 - Posted 2009-02-27 15:12:05 »

boolean running - should be volatile

it also seems like an odd place to put System.exit(0)

You also might want to reset interrupt status - though it shouldn't cause problems in this specific context, as you also rely on the running boolean and the contents of the loop should not require much time to execute.

Thread.currentThread().setPriority(10);
seems random you might want to make it relative to the calling thread or allow one to set it by exposing it with their value.

It's harder to read code than to write it. - it's even harder to write readable code.

The gospel of brother Riven: "The guarantee that all bugs are in *your* code is worth gold." Amen brother a-m-e-n.
Offline Wildern

Junior Member





« Reply #7 - Posted 2009-02-27 15:26:01 »

I think the fps is broken, I tried setting it to very low but still keep on sucking up my cpu and rendering fast as possible.

You also in your example "framesSkipped += skips" does nothing.

60fps uses less than 10% cpu on my machine (Dual Core 2.6GHz with 3GB RAM).
Lowering fps also lowers cpu used.

The frameSkipped is left over from stats collection, I pulled out most of the stats collection code to simplify the example and I just missed that bit.

boolean running - should be volatile

it also seems like an odd place to put System.exit(0)

You also might want to reset interrupt status - though it shouldn't cause problems in this specific context, as you also rely on the running boolean and the contents of the loop should not require much time to execute.

Thread.currentThread().setPriority(10);
seems random you might want to make it relative to the calling thread or allow one to set it by exposing it with their value.

Thanks for the input.
Where would you recommend putting the exit?  When the user ends my game I set running to false which stops the loop.  If I don't call exit, the application stays open.
Offline damaxxed

Senior Newbie




I ♥ Prototyping


« Reply #8 - Posted 2009-03-05 23:09:07 »

1  
2  
3  
   Date startTime = new Date();
   Date previousRepaint = startTime;
   Date currentTime = startTime;

Why are you using Date? Isn't a long for each timestamp enough? Use
1  
   long startTime = System.getCurrentMillis(); //can be System.nanoTime() if you need higher precision




1  
2  
3  
      if(delta <= redrawsPerSecond) {
         try { Thread.sleep(10); } catch(Exception e) { /* pass */ }
      }

I dislike Thread.sleep() because it's very unprecise on my Windows XP machine.. I prefer solutions like this:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
final long LOOPTIME = 1000/60; //for 60 FPS in 1000ms
long nextLoop;

while(true) {
    nextLoop = System.currentTimeMillis() + LOOPTIME;

    //do logic and repaint stuff

    while(System.currentTimeMillis() < nextLoop) {
        Thread.yield();
    }
}
Online pjt33
« Reply #9 - Posted 2009-03-07 18:01:20 »

I dislike Thread.sleep() because it's very unprecise on my Windows XP machine.. I prefer solutions like this:
That maxes the CPU on my Linux box. I think in general nanoSleep is the best solution, assuming availability of Java 1.5.
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline damaxxed

Senior Newbie




I ♥ Prototyping


« Reply #10 - Posted 2009-03-09 07:39:08 »

That maxes the CPU on my Linux box. I think in general nanoSleep is the best solution, assuming availability of Java 1.5.

Yes, it maxes the CPU, but other Threads are able to do their work even with that. Using Thread.sleep(long ms, long ns) isn't accurate, too, afaik.
Online pjt33
« Reply #11 - Posted 2009-03-09 09:00:10 »

Yes, it maxes the CPU, but other Threads are able to do their work even with that.
Maybe. I found that one of the games in this year's Java4k competition was almost unplayable until its author changed from yield to sleep, at which point it started actually detecting and handling all of my key presses.
Offline Gudradain
« Reply #12 - Posted 2009-03-11 16:37:28 »

Everyone seems to use a game loop with thread.sleep(). Except me  Sad

What I did is basically create a repeating timer that call repaint() when it ends and I use paint() as my game loop... If the fps is faster than the Timer it will wait for the timer, if it's slower then the next repaint() will just have to wait. Anything is wrong with that Huh
Offline Eli Delventhal

JGO Kernel


Medals: 42
Projects: 11
Exp: 10 years


Game Engineer


« Reply #13 - Posted 2009-03-12 17:29:11 »

I don't even understand exactly what you're saying, but using the Timer class is acceptable. The main problem with it is that you can't directly control it like you can with the sleeping. So, if you're getting slower or faster FPS you can't really adjust it accordingly. For simple games I think it works fine, but I wouldn't use it for anything that's complex.

See my work:
OTC Software
Offline Gudradain
« Reply #14 - Posted 2009-03-13 01:48:50 »

I don't even understand exactly what you're saying, but using the Timer class is acceptable. The main problem with it is that you can't directly control it like you can with the sleeping. So, if you're getting slower or faster FPS you can't really adjust it accordingly. For simple games I think it works fine, but I wouldn't use it for anything that's complex.

After reading it I got scare that I took a wrong way so I did some test to see how I could control the game speed so it match with the FPS. I'm still using a timer and I was able to change the game speed accordingly to the FPS. Here is a sample of my code.
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
public class GameFrame extends JFrame{
 
  public static int TIME_DELAY = 10;
  GamePanel gPanel;
  Timer timer = new Timer(TIME_DELAY, TimerListener);
 
  class TimerListener implements ActionListener{
   
    public void actionPerformed(ActionEvent e){
      gPanel.repaint();
    }
  }
}

public class GamePanel extens JPanel{
 
  GameFrame gameFrame;
 
  protected void paintComponent(Graphics g) {
    long time0 = System.nanotime();
    //The code of the game loop
   //....
   //end of the game loop
   long time1 = System.nanotime();
    int t10 = (int)(time1-time0)/1000000;
    int timeDelay = setTimeDelay(t10);
    //+2 because it's the average time difference between 2 repaint and
   //the time in the game loop
   gameFrame.TIME_DELAY = timeDelay + 2;
    gameFrame.timer.setDelay(timeDelay);    
  }
 
  private int setTimeDelay(int time){
    listTimeDelay.addFirst(time);
    if(listTimeDelay.size() > 10){
      listTimeDelay.removeLast();
    }
    int retour = 0;
    for(int i=0; i<listTimeDelay.size(); i++){
      retour += listTimeDelay.get(i);
    }
    if(listTimeDelay.size() > 0){
      retour = retour/listTimeDelay.size();
    }
    //13+2 is my maximum repaint() speed
   if(retour < 13){
      retour = 13;
      //Too big value mess up with the physics
   }else if(retour > 30){
      retour = 30;
    }
    return retour;
  }
}


Anyone think it would make a problem later? Because if I have to change it I guess it's better now than later.
Offline Eli Delventhal

JGO Kernel


Medals: 42
Projects: 11
Exp: 10 years


Game Engineer


« Reply #15 - Posted 2009-03-13 16:44:02 »

That will always be a frame behind, because you're setting the delay for the next frame based on the current frame's fps, and you don't appear to be using a delta variable of any kind. It won't really be very noticeable, but I still don't recommend using Timer.

See my work:
OTC Software
Offline Gudradain
« Reply #16 - Posted 2009-04-04 19:19:18 »

Yay I finally made a good game loop (at least I think I do). Any places for improvement?

Check it out :
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
public class GameLoop implements Runnable{
   
    long desiredFPS = 60;
    long desiredDeltaLoop = (1000*1000*1000)/desiredFPS;
   
    long beginLoopTime;
    long endLoopTime;
    long currentPhysicTime;
    long lastPhysicTime;
   
    boolean running = true;
   
    public void run(){
       
        loopInitialization();
       
        while(running){
            beginLoopTime = System.nanoTime();
            executeDrawing();
           
            lastPhysicTime = currentPhysicTime;
            currentPhysicTime = System.nanoTime();
            executePhysic(currentPhysicTime - lastPhysicTime);
           
            executeLogic();
           
            endLoopTime = System.nanoTime();
            adjustSpeed();
        }
       
    }
   
    private void adjustSpeed(){
        long deltaLoop = endLoopTime - beginLoopTime;
       
        if(deltaLoop > desiredDeltaLoop){
            //Do nothing. We are alreadyLate
       }else{
            try{
                Thread.sleep((desiredDeltaLoop - deltaLoop)/(1000*1000));
            }catch(InterruptedException e){
                //Do nothing
           }
        }
    }
   
    private void loopInitialization(){
        currentPhysicTime = System.nanoTime();
    }
   
    private void executeDrawing(){
        //Do the drawing here
   }
   
    private void executePhysic(long deltaPhysic){
        //Do the physic here
   }
   
    private void executeLogic(){
        //Do the logic here
   }


Offline appel

JGO Wizard


Medals: 51
Projects: 4


I always win!


« Reply #17 - Posted 2009-04-06 13:50:12 »

You'll be putting the garbage collector to overdrive, and hence reducing the framerate, if you try to do something like:

while(true) {
...
   MyObject myObj = new MyObject();
...
}

Check out the 4K competition @ www.java4k.com
Check out GAMADU (my own site) @ http://gamadu.com/
Offline Gudradain
« Reply #18 - Posted 2009-04-06 17:40:28 »

Hmmm. I don't really get what you are telling me. What object did I create in the loop? I can see that I create 2 or 3 long variable is it what you are talking about?
Offline appel

JGO Wizard


Medals: 51
Projects: 4


I always win!


« Reply #19 - Posted 2009-04-06 19:02:31 »

Hmmm. I don't really get what you are telling me. What object did I create in the loop? I can see that I create 2 or 3 long variable is it what you are talking about?

I was referring to the original post's "currentTime = new Date();"

Check out the 4K competition @ www.java4k.com
Check out GAMADU (my own site) @ http://gamadu.com/
Offline whome

Junior Member




Carte Noir Java


« Reply #20 - Posted 2009-05-06 21:26:44 »

Yay I finally made a good game loop (at least I think I do). Any places for improvement?

I took your mainloop and made a testrun, its very simple left-to-right animation. I have tried everything(?) to make it smooth as silk in a windowed Java application still not eating CPU100%. I think its close to impossible or I just can't.

I can cleary see occasional jumps and small tearing.

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
117  
118  
119  
120  
121  
122  
123  
124  
125  
126  
127  
128  
129  
130  
131  
132  
133  
134  
135  
136  
137  
138  
139  
140  
141  
142  
143  
144  
145  
146  
147  
148  
149  
150  
151  
152  
153  
154  
155  
156  
157  
158  
159  
160  
161  
162  
163  
164  
165  
166  
167  
168  
169  
170  
171  
172  
173  
174  
175  
176  
177  
178  
179  
180  
181  
182  
183  
184  
185  
186  
187  
188  
189  
190  
191  
//http://www.java-gaming.org/topics/how-can-i-make-this-simple-game-loop-better/19971/view.html

import java.util.*;

import java.awt.Color;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferStrategy;

import java.awt.DisplayMode; // for full-screen mode

public class GameLoop1 implements KeyListener {
    Frame mainFrame;

    private static final long NANO_IN_MILLI = 1000000L;  
    long desiredFPS = 60;
    long desiredDeltaLoop = (1000*1000*1000)/desiredFPS;    

    long beginLoopTime;
    long endLoopTime;
    long currentPhysicTime;
    long lastPhysicTime;

    long fps;
    long frameCounter;
    long lastFpsTime;
   
    Rectangle2D rect;
   
    public GameLoop1(Map<String,String> args, GraphicsDevice device) {
        try {
            // Setup the frame
           GraphicsConfiguration gc = device.getDefaultConfiguration();
           
            mainFrame = new Frame(gc);
            mainFrame.setUndecorated(true);
            mainFrame.setIgnoreRepaint(true);
            mainFrame.setVisible(true);
            mainFrame.setSize(640, 480);
            //mainFrame.setLocationRelativeTo();
           mainFrame.setLocation(100,100);
            mainFrame.createBufferStrategy(2);
            mainFrame.addKeyListener(this);

            if ("true".equalsIgnoreCase(args.get("fullscreen"))) {
              device.setFullScreenWindow(mainFrame);
              device.setDisplayMode(new DisplayMode(640, 480, 8, DisplayMode.REFRESH_RATE_UNKNOWN));
            }
         
            // Cache the buffer strategy and create a rectangle to move
           BufferStrategy bufferStrategy = mainFrame.getBufferStrategy();
            rect = new Rectangle2D.Float(0,100,64,64);
           
            // loop initialization
           currentPhysicTime = System.nanoTime();
            lastFpsTime = currentPhysicTime;

            // Main loop
           while(true) {
                beginLoopTime = System.nanoTime();

                // Synchronise with the display hardware. Note that on
               // Windows Vista this method may cause your screen to flash.
               // If that bothers you, just comment it out.
               //Toolkit.getDefaultToolkit().sync();
           
                // **1) execute drawing
               Graphics g = bufferStrategy.getDrawGraphics();
                drawScreen(g);
                g.dispose();

                // Flip the buffer
               if( !bufferStrategy.contentsLost() )
                    bufferStrategy.show();
               
                // **2) execute physics
               lastPhysicTime = currentPhysicTime;
                currentPhysicTime = System.nanoTime();
                updateWorld(currentPhysicTime - lastPhysicTime);              
           
                calculateFramesPerSecond();
           
                // **3) execute logic
               // executeLogic(); // handle keyboard and other inputs
               endLoopTime = System.nanoTime();
                adjustSpeed();            
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        } finally {
            device.setFullScreenWindow(null);
        }
    }
   
    private void adjustSpeed() {
        long deltaLoop = endLoopTime - beginLoopTime;
       
        if(deltaLoop > desiredDeltaLoop) {
            // do nothing. We are alreadyLate
           System.out.println("Late, do not sleep");
        } else {
            try {
                Thread.sleep((desiredDeltaLoop - deltaLoop) / NANO_IN_MILLI);
            } catch (InterruptedException ex) { }
        }
    }  

    private void updateWorld(long elapsedTime) {
        // speed: 150 pixels per second
       double xMov = (140f/(NANO_IN_MILLI*1000)) * elapsedTime;
        //double xMov = 2.0;
       rect.setRect(rect.getX() + xMov, 100, 64, 64);
       
        if( rect.getX() > mainFrame.getWidth() )
            rect.setRect(-rect.getWidth(), 100, 64, 64);
    }
   
    private void drawScreen(Graphics g) {
        g.setColor(Color.BLACK);
        g.fillRect(0, 0, mainFrame.getWidth(), mainFrame.getHeight());
        g.setColor(Color.WHITE);
        g.drawString("FPS: " + fps, 0, 17);
       
        g.setColor(Color.RED);
        g.fillRect((int)rect.getX(), (int)rect.getY(), (int)rect.getWidth(), (int)rect.getHeight());
    }
   
    private void calculateFramesPerSecond() {
        if( currentPhysicTime - lastFpsTime >= NANO_IN_MILLI*1000 ) {
            fps = frameCounter;
            frameCounter = 0;
            lastFpsTime = currentPhysicTime;
        }
        frameCounter++;
    }
   
    public void keyPressed(KeyEvent e) {
        if( e.getKeyCode() == KeyEvent.VK_ESCAPE ) {
            System.exit(0);
        }
    }
   
    public void keyReleased(KeyEvent e) { }
    public void keyTyped(KeyEvent e) { }

    public static void main(String[] args) {
        try {
       Map<String,String> mapArgs = parseArguments(args);

            GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
            GraphicsDevice device = env.getDefaultScreenDevice();
            new GameLoop1(mapArgs, device);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }


   /**
    * Parse commandline arguments, each parameter is a name-value pair.
    * Example: java.exe MyApp "key1=value1" "key2=value2"
    */

   private static Map<String,String> parseArguments(String[] args) {
      Map<String,String> mapArgs = new HashMap<String,String>();

      for(int idx=0; idx < args.length; idx++) {
         String val = args[idx];
         int delimIdx = val.indexOf('=');
         if (delimIdx < 0) {
            mapArgs.put(val, null);
         } else if (delimIdx == 0) {
            mapArgs.put("", val.substring(1));
         } else {
            mapArgs.put(
               val.substring(0, delimIdx).trim(),
               val.substring(delimIdx+1)
            );
         }
      }
     
      return mapArgs;
   }

}


edit: another issue, my FPS counter clearly does not work. target fps is 60 but my debug prints +90fps all the time.
Offline Gudradain
« Reply #21 - Posted 2009-05-10 04:25:16 »

Hello Whome.

Thx for making this testrun. To tell the truth when I post it I was hoping someone find a flaw in my design so I could improve it. (It is nearly my first gameloop).

I tried you testrun but I couldn't reproduce the jump unless I go to like 2000 fps (that eating nearly all my CPU). But, I see some tearing. I not sure to fully understand what cause tearing in a game but I find this with some research. http://www.gamedev.net/community/forums/topic.asp?topic_id=372033. Looks like you can't remove tearing unless you go fullscreen mode and enable vsync.

btw, what is you debug? Your fps counter print around 61 fps for me but you said it doesn't work.
Offline whome

Junior Member




Carte Noir Java


« Reply #22 - Posted 2009-05-12 19:24:29 »

Upper-left corner FPS counter was my debug value. When I posted my example code it printed run +90 fps but now I run it again. Its a steady 60-63 fps value. Go figure what was wrong last time, strange. So we come to a conclusion fps calc works after all :-)

I know and have done few native D3D apps they run fine windowed vsync lock. Its so nice a windowed non-tearing rendering loop, triple buffered flips and low cpu% usage.

Here is an another game loop borrowed from a forum post. Loop animation does not rely on a delta time but each updateWorld() call is one animation frame to be simulated. Slower machines drop renderFPS but loop tries to keep up to a updateFPS. I think it is a bit more consistent fps, but tearing is still visible. About "Toolkit.getDefaultToolkit().sync()" method I think we can forget it.

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
117  
118  
119  
120  
121  
122  
123  
124  
125  
126  
127  
128  
129  
130  
131  
132  
133  
134  
135  
136  
137  
138  
139  
140  
141  
142  
143  
144  
145  
146  
147  
148  
149  
150  
151  
152  
153  
154  
155  
156  
157  
158  
159  
160  
161  
162  
163  
164  
165  
166  
167  
168  
169  
170  
171  
172  
173  
174  
175  
176  
177  
178  
179  
180  
181  
182  
183  
184  
185  
186  
187  
188  
189  
190  
191  
192  
193  
194  
195  
196  
197  
198  
199  
200  
201  
202  
203  
204  
205  
206  
207  
208  
209  
210  
211  
212  
213  
214  
215  
216  
217  
218  
219  
220  
221  
222  
223  
224  
225  
226  
227  
228  
229  
230  
231  
232  
233  
// java GameLoop2 "fullscreen=true" "fps=60" "vsync=false"
//http://www.java-gaming.org/topics/how-can-i-make-this-simple-game-loop-better/19971/view.html

import java.util.*;

import java.awt.Color;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferStrategy;

import java.awt.DisplayMode; // for full-screen mode

// java GameLoop2 "fullscreen=true" "fps=60" "vsync=false"
public class GameLoop2 implements KeyListener {
    Frame mainFrame;

    private static final long NANO_IN_MILLI = 1000000L;  

    // num of iterations with a sleep delay of 0ms before
   // game loop yields to other threads.
   private static final int NO_DELAYS_PER_YIELD = 16;

    // max num of renderings that can be skipped in one game loop,
   // game's internal state is updated but not rendered on screen.
   private static int MAX_RENDER_SKIPS = 5;

    private static int TARGET_FPS = 60;
   
    //private long prevStatsTime;
   private long gameStartTime;
    private long curRenderTime;
    private long rendersSkipped = 0L;
    private long period; // period between rendering in nanosecs

    long fps;
    long frameCounter;
    long lastFpsTime;
   
    Rectangle2D rect;
   
    public GameLoop2(Map<String,String> args, GraphicsDevice device) {
        try {
            if (args.containsKey("fps"))
              TARGET_FPS = Integer.parseInt(args.get("fps"));


            // Setup the frame
           GraphicsConfiguration gc = device.getDefaultConfiguration();
           
            mainFrame = new Frame(gc);
            mainFrame.setUndecorated(true);
            mainFrame.setIgnoreRepaint(true);
            mainFrame.setVisible(true);
            mainFrame.setSize(640, 480);
            //mainFrame.setLocationRelativeTo();
           mainFrame.setLocation(100,100);
            mainFrame.createBufferStrategy(2);
            mainFrame.addKeyListener(this);

            if ("true".equalsIgnoreCase(args.get("fullscreen"))) {
              device.setFullScreenWindow(mainFrame);
              device.setDisplayMode(new DisplayMode(640, 480, 8, DisplayMode.REFRESH_RATE_UNKNOWN));
            }
         
            final boolean VSYNC = "true".equalsIgnoreCase(args.get("vsync"));

            // Cache the buffer strategy and create a rectangle to move
           BufferStrategy bufferStrategy = mainFrame.getBufferStrategy();
            rect = new Rectangle2D.Float(0,100,64,64);

            // loop initialization
           long beforeTime, afterTime, timeDiff, sleepTime;
            long overSleepTime = 0L;
            int noDelays = 0;
            long excess = 0L;
            gameStartTime = System.nanoTime();
            //prevStatsTime = gameStartTime;
           beforeTime = gameStartTime;
     
            period = (1000L*NANO_IN_MILLI)/TARGET_FPS;  // rendering FPS (nanosecs/targetFPS)
           System.out.println("FPS: " + TARGET_FPS + ", vsync=" + VSYNC);
            System.out.println("FPS period: " + period);
           

            // Main loop
           while(true) {
               // **2) execute physics
              updateWorld(0);              

               // **1) execute drawing
              Graphics g = bufferStrategy.getDrawGraphics();
               drawScreen(g);
               g.dispose();

               // Synchronise with the display hardware. Note that on
              // Windows Vista this method may cause your screen to flash.
              // If that bothers you, just comment it out.
              if (VSYNC) Toolkit.getDefaultToolkit().sync();

               // Flip the buffer
              if( !bufferStrategy.contentsLost() )
                   bufferStrategy.show();

               afterTime = System.nanoTime();
               curRenderTime = afterTime;
               calculateFramesPerSecond();

               timeDiff = afterTime - beforeTime;
               sleepTime = (period-timeDiff) - overSleepTime;
               if (sleepTime > 0) { // time left in cycle
                 //System.out.println("sleepTime: " + (sleepTime/NANO_IN_MILLI));
                 try {
                     Thread.sleep(sleepTime/NANO_IN_MILLI);//nano->ms
                 } catch(InterruptedException ex){}
                  overSleepTime = (System.nanoTime()-afterTime) - sleepTime;
               } else { // sleepTime <= 0;
                 System.out.println("Rendering too slow");
                  // this cycle took longer than period
                 excess -= sleepTime;
                  // store excess time value
                 overSleepTime = 0L;
                  if (++noDelays >= NO_DELAYS_PER_YIELD) {
                     Thread.yield();
                     // give another thread a chance to run
                    noDelays = 0;
                  }
               }
           
               beforeTime = System.nanoTime();
         
               //If the rendering is taking too long, then
              //update the game state without rendering
              //it, to get the UPS nearer to the required frame rate.
              int skips = 0;
               while((excess > period) && (skips < MAX_RENDER_SKIPS)) {
                  // update state but don’t render
                 System.out.println("Skip renderFPS, run updateFPS");
                  excess -= period;
                  updateWorld(0);
                  skips++;
               }
               rendersSkipped += skips;
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        } finally {
            device.setFullScreenWindow(null);
        }
    }

    private void updateWorld(long elapsedTime) {
        // speed: 150 pixels per second
       //double xMov = (140f/(NANO_IN_MILLI*1000)) * elapsedTime;
       double xMov = 140f / (TARGET_FPS);
        rect.setRect(rect.getX()+xMov, 100, 64, 64);        
        if( rect.getX() > mainFrame.getWidth() )
            rect.setRect(-rect.getWidth(), 100, 64, 64);
    }
   
    private void drawScreen(Graphics g) {
        g.setColor(Color.BLACK);
        g.fillRect(0, 0, mainFrame.getWidth(), mainFrame.getHeight());
        g.setColor(Color.WHITE);
        g.drawString("FPS: " + fps, 0, 17);
       
        g.setColor(Color.RED);
        g.fillRect((int)rect.getX(), (int)rect.getY(), (int)rect.getWidth(), (int)rect.getHeight());
    }
   
    private void calculateFramesPerSecond() {
        if( curRenderTime - lastFpsTime >= NANO_IN_MILLI*1000 ) {
            fps = frameCounter;
            frameCounter = 0;
            lastFpsTime = curRenderTime;
        }
        frameCounter++;
    }
   
    public void keyPressed(KeyEvent e) {
        if( e.getKeyCode() == KeyEvent.VK_ESCAPE ) {
            System.exit(0);
        }
    }
   
    public void keyReleased(KeyEvent e) { }
    public void keyTyped(KeyEvent e) { }

    public static void main(String[] args) {
        try {
       Map<String,String> mapArgs = parseArguments(args);

            GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
            GraphicsDevice device = env.getDefaultScreenDevice();
            new GameLoop2(mapArgs, device);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }


   /**
    * Parse commandline arguments, each parameter is a name-value pair.
    * Example: java.exe MyApp "key1=value1" "key2=value2"
    */

   private static Map<String,String> parseArguments(String[] args) {
      Map<String,String> mapArgs = new HashMap<String,String>();

      for(int idx=0; idx < args.length; idx++) {
         String val = args[idx];
         int delimIdx = val.indexOf('=');
         if (delimIdx < 0) {
            mapArgs.put(val, null);
         } else if (delimIdx == 0) {
            mapArgs.put("", val.substring(1));
         } else {
            mapArgs.put(
               val.substring(0, delimIdx).trim(),
               val.substring(delimIdx+1)
            );
         }
      }
     
      return mapArgs;
   }

}
Offline Darrin

Junior Member


Projects: 1



« Reply #23 - Posted 2009-05-12 21:04:04 »

Hiya,

I highly recommend looking at Kevin Glass's tutorial.    I too use Java2D.  Here my loop, set up to run in an applet.


1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
public void run()    {

        // Timer and FPS
       long loopTime = System.currentTimeMillis();
        FPS fps = new FPS();

       
        //Main game loop
       while(bRunning)
        {
            // timer for smoothing graphics
           long timeLapse = System.currentTimeMillis() - loopTime;
            loopTime = System.currentTimeMillis();
           
            // fps counter
           fps.update();


             Graphics2D g2d = (Graphics2D) bufferStrategy.getDrawGraphics();

            // Wipe screen to black
           g2d.setColor(Color.black);
            g2d.fillRect(0, 0, CANVAS_X, CANVAS_Y);

            // Splash screen
           if (state == GameState.SPLASH){
                splash.draw(g2d, highScore);
                lives = NUMBER_OF_LIVES;
            }
           

                g2d.drawString("fps: "+fps.getFPS(), 5, 30);
                drawAndSleep(g2d, loopTime);
            }
           }

 private void drawAndSleep(Graphics2D g2d, long loopTime){


        // clear and flip graphics
       g2d.dispose();

        //Show bufferStrategy
       if(!bufferStrategy.contentsLost())
            bufferStrategy.show();

        long sleep = loopTime+31-System.currentTimeMillis();
        // System.out.println(sleep);   // check to make sure never negative
       try { Thread.sleep(sleep); } catch (Exception e) {}

    }

Offline Gudradain
« Reply #24 - Posted 2009-05-13 00:43:25 »

Ok I took the 2 examples that you gave me and I remove all the things not related to timing so we could see the differences easily.

The one of Whome
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
private static final long NANO_IN_MILLI = 1000000L;
   private static int MAX_RENDER_SKIPS = 5;
   private static int TARGET_FPS = 60;
   
   boolean running = true;
   long period = (1000L*NANO_IN_MILLI)/TARGET_FPS;
   
   public void run(){

      long beforeTime = System.nanoTime();
      long afterTime, timeDiff, sleepTime;
      long excess = 0L;

      while(running){
         update();
         
         render();
         
         afterTime = System.nanoTime();
         timeDiff = afterTime - beforeTime;
         sleepTime = (period-timeDiff) - excess;
         if (sleepTime > 0) { // time left in cycle
           //System.out.println("sleepTime: " + (sleepTime/NANO_IN_MILLI));
           try {
               Thread.sleep(sleepTime/NANO_IN_MILLI);//nano->ms
           } catch(InterruptedException ex){}
            excess = (System.nanoTime()-afterTime) - sleepTime;
         } else { // sleepTime <= 0;
           System.out.println("Rendering too slow");
            // this cycle took longer than period
           excess -= sleepTime;
         }

         beforeTime = System.nanoTime();

         /* If the rendering is taking too long, then
                update the game state without rendering
                it, to get the UPS nearer to the
                required frame rate. */

         int skips = 0;
         while((excess > period) && (skips < MAX_RENDER_SKIPS)) {
            // update state but don’t render
           System.out.println("Skip renderFPS, run updateFPS");
            excess -= period;
            update();
            skips++;
         }
      }
   }
   
   private void render(){
      //Not implemented
  }
   
   private void update(){
      //Not implemented
  }


And the one of Darrin
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
public class ExampleLoopNew {
   
   boolean running = true;
   
   public void run(){
     
      long loopBeginTime = System.currentTimeMillis();
      long loopTimeLapse;
     
      while(running){
         
         loopTimeLapse = System.currentTimeMillis() - loopBeginTime;
         loopBeginTime = System.currentTimeMillis();
         
         render();
         
         long sleep = (loopBeginTime+31) - System.currentTimeMillis();
         // System.out.println(sleep);   // check to make sure never negative
        try { Thread.sleep(sleep); } catch (Exception e) {}
      }
   }
   
   private void render(){
      //Not implement
  }

}


and now mine (with somes modification)
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
public class ExampleLoopMine {
   
   long desiredFPS = 60;
    long desiredDeltaLoop = (1000*1000*1000)/desiredFPS;
   
   boolean running = true;
   
   public void run(){
     
      long beginLoopTime;
      long endLoopTime;
      long currentUpdateTime = System.nanoTime();
      long lastUpdateTime;
      long deltaLoop;
     
      while(running){
         beginLoopTime = System.nanoTime();
         
         render();
         
         lastUpdateTime = currentUpdateTime;
         currentUpdateTime = System.nanoTime();
         update(currentUpdateTime - lastUpdateTime);
         
         endLoopTime = System.nanoTime();
         deltaLoop = endLoopTime - beginLoopTime;
           
           if(deltaLoop > desiredDeltaLoop){
               //Do nothing. We are already late
          }else{
               try{
                   Thread.sleep((desiredDeltaLoop - deltaLoop)/(1000*1000));
               }catch(InterruptedException e){
                   //Do nothing
              }
           }
      }
   }
   
   private void render(){
      //Not implemented
  }
   
   private void update(long deltaTime){
      //Not implemented
  }

}


Anyone can tell what make one better than the other?

Offline Gudradain
« Reply #25 - Posted 2009-05-13 00:46:58 »

I can't understand what is the use of this code in the loop Whome show
1  
2  
3  
4  
5  
if (++noDelays >= NO_DELAYS_PER_YIELD) {
                   Thread.yield();
                   // give another thread a chance to run
                  noDelays = 0;
                }


In the comment it said "give another thread a chance to run" but other thread still run no matter this thread is running right? And if I'm right Thread.yield() doesn't free up CPU time.

EDIT : I answered myself http://www.java-forums.org/new-java/12990-difference-between-thread-yield-thread-sleep-methods.html
Offline skinny boy

Junior Member





« Reply #26 - Posted 2009-09-15 20:13:20 »

a lot of familiar code,,
finally the studying started to pay off

(though i should stop and start writing some code, otherwise what is the point??)

(hint, Andrew Davison, Killer Game Programming in Java, Chapter 2 )

(not really sure about the chapter)
Pages: [1]
  ignore  |  Print  
 
 
You cannot reply to this message, because it is very, very old.

 

Add your game by posting it in the WIP section,
or publish it in Showcase.

The first screenshot will be displayed as a thumbnail.

radar3301 (12 views)
2014-09-21 23:33:17

BurntPizza (31 views)
2014-09-21 02:42:18

BurntPizza (22 views)
2014-09-21 01:30:30

moogie (20 views)
2014-09-21 00:26:15

UprightPath (28 views)
2014-09-20 20:14:06

BurntPizza (33 views)
2014-09-19 03:14:18

Dwinin (48 views)
2014-09-12 09:08:26

Norakomi (74 views)
2014-09-10 13:57:51

TehJavaDev (103 views)
2014-09-10 06:39:09

Tekkerue (50 views)
2014-09-09 02:24:56
List of Learning Resources
by Longor1996
2014-08-16 10:40:00

List of Learning Resources
by SilverTiger
2014-08-05 19:33:27

Resources for WIP games
by CogWheelz
2014-08-01 16:20:17

Resources for WIP games
by CogWheelz
2014-08-01 16:19:50

List of Learning Resources
by SilverTiger
2014-07-31 16:29:50

List of Learning Resources
by SilverTiger
2014-07-31 16:26:06

List of Learning Resources
by SilverTiger
2014-07-31 11:54:12

HotSpot Options
by dleskov
2014-07-08 01:59:08
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!