[SOLVED] Issue with smooth game loop

EDIT: Problem solved! Thanks so much to theagentd for helping me!

It’s been a while!

I’ve been dealing with a minor issue for a few days. I’d been having issues with consistency in screen scrolling and small screen tears, so I rewrote my game loop based on examples from ra4king and Eli. I’m still seeing small issues, though, and was wondering if I could have a few more pairs of eyes check it out.

Here’s a jar edit: updated to current version
Here’s the full source on BitBucket

Here’s the most relevant source

Game loop



@Override
	public void run() {
		createBufferStrategy(2);
		BufferStrategy bs = getBufferStrategy();

		init();

		while (running) {
			final double GAME_HERTZ = 60.0;
			final double TIME_BETWEEN_UPDATES = 1000000000 / GAME_HERTZ;
			final int MAX_UPDATES_BEFORE_RENDER = 1;
			double lastUpdateTime = System.nanoTime();
			double lastRenderTime = System.nanoTime();

			final double TARGET_FPS = 60;
			final double TARGET_TIME_BETWEEN_RENDERS = 1000000000 / TARGET_FPS;

			while (running) {
				double now = System.nanoTime();
				int updateCount = 0;

				if (!paused) {
					while (now - lastUpdateTime > TIME_BETWEEN_UPDATES && updateCount < MAX_UPDATES_BEFORE_RENDER) {
						tick();
						lastUpdateTime += TIME_BETWEEN_UPDATES;
						updateCount++;
					}

					render(bs);
					lastRenderTime = now;
					
					while (now - lastRenderTime < TARGET_TIME_BETWEEN_RENDERS && now - lastUpdateTime < TIME_BETWEEN_UPDATES) {
						Thread.yield();
						now = System.nanoTime();
					}
				}
			}
		}

		System.exit(0);
	}


Render function



private void render(BufferStrategy bs) {
		do {
			do {
				Graphics g = image.getGraphics();
				g.clearRect(0, 0, WIDTH, HEIGHT);

				game.draw(screen);

				g = bs.getDrawGraphics();
				g.drawImage(image, 0, 0, getWidth(), getHeight(), null);
				g.dispose();

			} while (bs.contentsRestored());

			bs.show();

		} while (bs.contentsLost());
	}

edit: added code tags


Thanks for taking the time to help me!

Nathan

I’m not seeing any issues, but the screen isn’t scrolling at all, so I have no idea where you would see tears! That, and your little guy doesn’t actually move smoothly, so I don’t know where you would see any graphical issues. Maybe you can state specifically what the issue is? Sorry!

Sorry! I just updated the download to the most current version - that one was a few days old.

Oh, that’s alright!

Well, I tested out the new version and again saw nothing wrong with it, maybe try running it outside of the IDE if you haven’t yet? The only think I noticed is that the player sort of floats to a stop after I release the keys, I think it might be intentional though because it happened consistently. But other than that, nope no screen tearing! :slight_smile:

Yeah, it’s on a tile based movement system.

Hm. Maybe it’ll happen if someone else tries? It very well could be a hardware related occurrence.

Thanks for your time though!

No problem! What CPU do you have? Seeing as you’re using Java2D, which isn’t hardware accelerated, I would guess that maybe you don’t have an amazing CPU? But that’s just a guess :stuck_out_tongue:

Couldn’t be the cpu - I have a 1.3ghz i5 Haswell; IE the new Macbook air, and I’m running dual-boot with Windows 7, which is what I’m programming in.

I’m not emulating Windows so I doubt it’s related to the Macbook hardware.

No offense, but they make a 1.3 GHz i5? Wow, I never knew, I always thought the i- series was high end strictly. To be honest with you, that’s not the best CPU out there, but I doubt it couldn’t handle a simple Java2D program. So I guess that’s not the issue!

You’re mistaken, but no worries.

This processor can actually run League at 60fps on medium settings, WoW at 100fps on medium settings, Minecraft on 60 on normal render distance, etc. The low clock speed is to conserve battery life (it’s unreal. 12 hours). In short, I’m positive it isn’t my processor. :slight_smile:

Ah, I see. Its just strange, I’ve never heard of a processor with that low of a clock speed in a multi-thousand dollar laptop, its just different to me!

Well, I’m just spamming the thread now with my naivness, so I’ll stop and hopefully someone else could have a look! Because I quite honestly have no idea as it runs fine on my desktop!

Tearing is solved by V-sync, which you have no direct control over when using Java2D. Make sure you haven’t forced it off in your drivers and it should be on by default.

I would not recommend a busy loop around Thread.yeild(). Try removing at and see if it’s any smoother.

Are you doing anything that depends on a delta time value for example? If you’re not moving your objects evenly, they won’t appear smooth.

Off-topic:

Running League maxed at 5120x2880 (a bit over 4K) at 200-300 FPS. Desktops are OP…

I agree, desktops are the best :smiley: I’ve got a pretty decent rig myself, but I got this for the portability/battery life almost exclusively.

WOW. This fixed it immediately! See here!

Thanks so much friend!

It was just a wild guess… Could’ve been that System.nanoTime() didn’t have very good accuracy or something.

The best way to sleep for a smooth game loop is this:


long sleepTime = .... // in nanoseconds

long t = System.nanoTime(), diff;
while((diff = System.nanoTime() - t) < sleepTime) {
    if(diff < sleepTime * 0.8)
        Thread.sleep(1);
    else
        Thread.yield();
}

This came from my earlier post about game loops, which contains more in-depth explanation and code.

I’ve always been told using Thread.sleep is bad for games because different computers have slightly different clock speeds, and therefore some computers will sleep for longer while others will sleep for less. I don’t think its a huge issue because it shouldn’t be that big of a difference, but is it an issue?

Not for Linux and OS X, but it is a problem on Windows, since by default a lower-resolution timer is used for Thread.sleep. The fix for this is shown in the link to the earlier post of mine, by putting this code at the first line in your main method:


if(System.getProperty("os.name").startsWith("Win")) {
   new Thread() {
      {
         setDaemon(true);
         start();
      }
      
      public void run() {
         while(true) {
            try {
               Thread.sleep(Long.MAX_VALUE);
            }
            catch(Exception exc) {}
         }
      }
   };
}

This forces the use of the high resolution timer under Windows.

i ran your fixed jar and it looks like you did away with the syncing all together.

this is the sync method i use and it works out really good :slight_smile: you can fit it into your code by simply calling sync(60); each loop cycle


	public void sync(int sync) {
	      if (sync!=-1) {
	         long diff = 1000000000L / sync + lastFrame;
	         long now = System.nanoTime();
	         
	         try {
	            while (diff > now) {
	               Thread.sleep((diff-now) / 2000000L);
	               now = System.nanoTime();
	            }
	         } catch (Exception e) {}

	         lastFrame = now;
	      } 
	 }

edit: also lastFrame is a long outside of the method initialized when you start your loop as lastFrame = System.nanoTime();