Java-Gaming.org    
Featured games (81)
games approved by the League of Dukes
Games in Showcase (499)
Games in Android Showcase (118)
games submitted by our members
Games in WIP (568)
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  
  Simple Game Loop  (Read 4271 times)
0 Members and 1 Guest are viewing this topic.
Offline fletchergames

Senior Member





« Posted 2011-02-24 00:22:09 »

I was making a video of gameplay for a game I made previously when I noticed that the level timer was horribly slow.  For a 23-minute video, it said that it took me 6-minutes to play the level!  First, I looked at the code for outputting the time.  That was fine.  Then I traced the method calls that set the time all the way back to the main loop.

I've had various problems with the game running sluggishly since before the game was released.  On newer computers, it wasn't much of a problem, but it was horrible on older computers.  It was odd because the game didn't seem like it should use much in the way of system resources.  I've "fixed" this many times without the problem really being fixed.  I decided to take a long hard look at my game loop.

Here was my old 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  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
protected final void runSafely() {
   //verify that the loop will continue
  shouldContinueLoop = true;
   
   //This variable is used to only skip yielding a certain number of times in a row.
  byte numDelays = 0;
   
   //This variable stores how much time the loop overslept by in the last cycle.
  //Negative values probably won't occur because Thread.sleep shouldn't undersleep.
  long overSleepTime = 0L;
   
   //This variable is used for computing the length of an update phase.
  long lastUpdatePhase = Time.currentTimeMillis();

   while(shouldContinueLoop) {
      //if the application isn't active, sleep for half a second
     if(!isApplicationActive()) {
         try {
            Thread.sleep(500);
         } catch(InterruptedException exception) {}
           
         continue;
      } //end if the frame isn't active
     
      //figure out the start of the current phase
     long currentCycle = Time.currentTimeMillis();
      long updatePhaseLength = currentCycle - lastUpdatePhase;
      if(updatePhaseLength > period)
         updatePhaseLength = period;
     
      //update the current model
     doUpdatePhase(updatePhaseLength);
      lastUpdatePhase = Time.currentTimeMillis();
   
      //render the game
     render();
   
      //figure out how long the cycle took
     long timeAfterCycle = Time.currentTimeMillis();
      long cycleLength = timeAfterCycle - currentCycle;
      long sleepTime = period - cycleLength - overSleepTime;
     
      //if some time is left in this cycle
     if(sleepTime > 0) {
         //sleep
        try {
            Thread.sleep(sleepTime);
         } catch(InterruptedException exception) {}
           
         overSleepTime = Time.currentTimeMillis() - timeAfterCycle - sleepTime;
      } //end if some time is left in this cycle
     //else no time is left in this cycle
     else {
         overSleepTime = 0L;

         //don't sleep, but yield if absolutely necessary
        numDelays++;
         if(numDelays >= maximumDelaysPerYield) {
            //yield to let other threads execute
           Thread.yield();
            numDelays = 0;
         } //end if there have been too many delays
     } //end else no time is left in this cycle
  } //end while forever
} //end runSafely


The code was heavily based upon the game loop code in Killer Game Programming in Java, though it wasn't quite identical.  The point of most of the code was to allow other processes to run at the same time as the game rather than hogging all the system's resources.  I deleted most of that code.

Suddenly, the sluggishness problems were less, though the bug with the time wasn't fixed.  It increased the "period" constant from 40 to 250 because it is effectively the maximum length of the update phase in milliseconds.  The problem was solved.

I also changed one other line of code
1  
lastUpdatePhase = Time.currentTimeMillis();
to
1  
lastUpdatePhase = currentCycle;
.  This didn't change anything for me, but I figure I was losing the time that update phase took, which wouldn't show up on the timer.  Since my computer is way more powerful than what the program needs, the update phase just happened to be almost instantaneous.

This is now my game loop for a game I'm updating:

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  
protected final void runSafely() {
   //verify that the loop will continue
  shouldContinueLoop = true;
   
   //This variable is used for computing the length of an update phase.
  long lastUpdatePhase = Time.currentTimeMillis();

   while(shouldContinueLoop) {
      //if the application isn't active, sleep for half a second
     if(!isApplicationActive()) {
         try {
            Thread.sleep(500);
         } catch(InterruptedException exception) {}
           
         continue;
      } //end if the frame isn't active
     
      //figure out the start of the current phase
     long currentCycle = Time.currentTimeMillis();
      long updatePhaseLength = currentCycle - lastUpdatePhase;
      if(updatePhaseLength > maximumUpdateLength)
         updatePhaseLength = maximumUpdateLength;
     
      //update the current model
     doUpdatePhase(updatePhaseLength);
      lastUpdatePhase = currentCycle;
   
      //render the game
     render();
   } //end while forever
} //end runSafely


I won't go so far as to say that the code from Killer Game Programming in Java was wrong because I didn't copy it exactly.  I'm just saying that you have to be careful what you do with your main loop code.  My program has always been somewhat sluggish because of the main loop code, and I didn't notice the timer but until I made a gameplay video.

The problem is probably caused by how the rest of my code worked.  In KGPiJ, the update phase seems like it does a specific amount of updating in each phase.  My own code sends the number of milliseconds elapsed to the update method, which updates different amounts based upon how much time has passed.  This caused me to change parts of KGPiJ's game loop, which is probably how things got screwed up.

I have some concerns that my program might run at 100% CPU usage now, but that doesn't seem to actually happen for some reason.  In any case, I would rather use 100% of the CPU than have the game run slowly with an incorrect timer.
Offline gimbal

JGO Knight


Medals: 25



« Reply #1 - Posted 2011-02-25 09:13:32 »

Remember that if you have a multicore CPU, your game loop thread will only hog one core. So if you have four cores you will only see 25% usage. But anyone with a single core CPU will see 100% usage.

This IS an issue because that will certainly drive up the fan speed to vacuum cleaner levels.


If you worry about the imprecise sleep / time readouts in Windows environments: there is a documented workaround for that. Start up an endlessly sleeping daemon thread. The JVM keeps the timer precision at 1ms as long as at least one thread is asleep (and you don't sleep for an interval that is a multiple of 10 - Long.maxLong works just fine). Its a bit of a hack, but I use it with good results and so have others I've shared the trick with.
Offline princec

JGO Kernel


Medals: 391
Projects: 3
Exp: 16 years


Eh? Who? What? ... Me?


« Reply #2 - Posted 2011-02-25 13:49:46 »

After many years wrangling and wringing my hands in despair I finally turned my back on sleep() and use a busy yield() loop once more, because I get rock steady framerates and bedamned the CPU gobbling power. I probably caused a flower to wilt somewhere in a rainforest, and then resumed my guilt-free life of lethargy and rampant consumerism.

Cas Smiley

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

Senior Member


Medals: 3
Projects: 1


Java games rock!


« Reply #3 - Posted 2011-02-25 14:48:04 »

I agree with Cas. I turned to yield() a long time ago, and framerates have been constant since Wink
Offline Eli Delventhal

JGO Kernel


Medals: 42
Projects: 11
Exp: 10 years


Game Engineer


« Reply #4 - Posted 2011-02-25 15:54:02 »

After many years wrangling and wringing my hands in despair I finally turned my back on sleep() and use a busy yield() loop once more, because I get rock steady framerates and bedamned the CPU gobbling power. I probably caused a flower to wilt somewhere in a rainforest, and then resumed my guilt-free life of lethargy and rampant consumerism.

Cas Smiley
Haha. I use a happy medium - there is yielding and also after a given interval a sleep for just 1ms. The framerate is great and my CPU consumption remains low.

See my work:
OTC Software
Offline princec

JGO Kernel


Medals: 391
Projects: 3
Exp: 16 years


Eh? Who? What? ... Me?


« Reply #5 - Posted 2011-02-25 17:38:54 »

Hmm, an interesting idea. I might give it a go and see how it fares.

Cas Smiley

Offline JL235

JGO Coder


Medals: 10



« Reply #6 - Posted 2011-02-25 19:08:56 »

After many years wrangling and wringing my hands in despair I finally turned my back on sleep() and use a busy yield() loop once more, because I get rock steady framerates and bedamned the CPU gobbling power. I probably caused a flower to wilt somewhere in a rainforest, and then resumed my guilt-free life of lethargy and rampant consumerism.

Cas Smiley
I once did this myself in a second thread that handled sound. It busy yielded whilst waiting to push the next block of data (or something to that effect). The game was unusable on Windows XP, MacOS and Linux (fine on Vista and Win7). Busy yield loops can easily starve other threads (yes even though they are repeatedly yielding).

Offline princec

JGO Kernel


Medals: 391
Projects: 3
Exp: 16 years


Eh? Who? What? ... Me?


« Reply #7 - Posted 2011-02-25 19:26:35 »

I'd make the sound thread a higher priority than the game thread, but make it so that it sleeps().

Cas Smiley

Offline fletchergames

Senior Member





« Reply #8 - Posted 2011-03-03 17:15:01 »

I didn't notice the replies to this thread before.

I added a Thread.yield() with no apparent difference.  I don't think I want to mess with it any more than that since it actually works now.  I might tamper with it for future games, but this is a game that I released quite two years ago.

Maybe now that it doesn't have these performance issues on certain computers, someone will actually buy it.

It was never that much of an issue on newer computers, but it did mess up the timer.  I had received reports of the game being unplayable on really old computers, but I used to think it was because of the giant background images.  Removing the background images did increase performance, but that wasn't the real issue.  Sadly, it's difficult to pinpoint these things because I don't have any QA staff or even a bunch of old computers to test the game.
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.

Pippogeek (39 views)
2014-09-24 16:13:29

Pippogeek (30 views)
2014-09-24 16:12:22

Pippogeek (20 views)
2014-09-24 16:12:06

Grunnt (46 views)
2014-09-23 14:38:19

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

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

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

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

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

BurntPizza (54 views)
2014-09-19 03:14:18
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!