Fast engine.tick() design

Hello all.

I am making a Java game. It’s intended target platform is NOT Windows, but I’m still going to write it in Java. So, I have a pretty basic question, but there’s a few different rather complicated answers.

My question is this: How can I make the most efficient engine, or main loop, which ticks and does gameplay action (moves character, enemies, etc.), while also dealing with repainting? There are a few different ways to do so.

Method A:
Put delay in the tick() method. Here, the game loop logic, and the repainting, is done on the same thread. If the game processing and drawing takes longer than the delay, then the game will simply slow down. I’ll have to code efficiently!

class GameThread extends Thread {
	public void run() {
		running = true;
		do {
			try {
				game.tick();
				display.repaint();
			} catch (Exception e) {
				e.printStackTrace();
			}
		} while (running);
	}
}
// ... Engine tick ...
	public void tick() {
		long start = System.nanoTime();

		// Do game logic
		doActions();

		// Wait until a given amount of time has passed
		while (now - lastFrameNs < DELAY_NS) {
			try {
				Thread.sleep(1);
			} catch (Exception e) {}
			now = System.nanoTime();
		}
	}

Method B:
Calculate the time delta in the tick() method, and move enemies and other game logic according to how much time has passed. The game will always appear to run at the same speed, although the FPS will go higher or lower depending on the processing load of the game actions and drawing.


	long delta = System.currentTimeMillis() - lastLoopTime;
	lastLoopTime = System.currentTimeMillis();

	// Do game logic
	entity.moveBy(delta);

In the above logic, you see that enemies will move depending on the time taken. So, if it took 50ms, they’ll move by 10 pixels, or if it took 25ms, they’ll move by 5 pixels.

The problem with this method, is that in-game logic timing becomes hard. What if a gun is supposed to fire 3 times a second? If the processing theoretically took a total of 1 second, I would have to fire the gun 3 times in the same tick(), which is bad because what if you’d want a back and forth between two entities (A interacts with B, B interacts with A, etc., instead of A interacts with B x 3, and THEN B interacts with A x 3). Or, in some cases where the logic just fails, the gun would only fire once when it was supposed to fire three times.

Method C:

Hmmm, I can’t think of a method C. :slight_smile:

Well, what are you guys’ take on this?

Thanks!

You can decouple the engine.tick() from the engine.render() so that you keep deterministic gameplay, while the framerate might vary.


   public static final long[] HZ_60         = new long[] { 16, 17, 17 }; // 16.66
   public static final long[] HZ_80         = new long[] { 12, 13 };    // 12.50
   public static final long[] HZ_100        = new long[] { 10 };

   private long[]             updateHzArray = HZ_80;
   private int                updateHzIndex = 0;
   private long[]             renderHzArray = HZ_80;
   private int                renderHzIndex = 0;

   public GameLoop(long[] updateHz, long[] renderHz)
   {
      this.renderHzArray = renderHz;
      this.updateHzArray = updateHz;
   }

   //

   public long renderInterval()
   {
      return renderHzArray[(renderHzIndex++) % renderHzArray.length];
   }

   public long updateInterval()
   {
      return updateHzArray[(updateHzIndex++) % updateHzArray.length];
   }

   public void loop()
   {
      long nextUpdate = now();
      long nextRender = now();

      int renderCount = 0;
      int updateCount = 0;
      long renderTime = 0L;
      long updateTime = 0L;
      Interval second = new Interval(1000);

      while (!this.isStopRequested())
      {
         long now = now();

         if (now < nextUpdate && now < nextRender)
         {
            try
            {
               Thread.sleep(1);
            }
            catch (Exception exc)
            {
               exc.printStackTrace();
            }

            continue;
         }

         if (now >= nextUpdate)
         {
            long t0 = now();
            this.update(now);
            long t1 = now();
            updateTime += t1 - t0;
            updateCount += 1;

            nextUpdate += this.updateInterval();
         }

         if (now >= nextRender)
         {
            long t0 = now();
            this.render(now);
            Display.update();
            long t1 = now();
            renderTime += t1 - t0;
            renderCount += 1;

            // allow to skip frames
            while (now >= nextRender)
            {
               nextRender += this.renderInterval();
            }
         }
      }
   }

I have most of my games run at a game logic of about 30-40 hz, while rendering the game as fast as possible, interpolating the state between the last two ticks.

Interesting, my code does the inverse. But then, I never wrote a serious game.

Riven, I do it the same as you. Rendering gets second priority, whereas logic tries to be smooth. Probably depends on the game. The reason why rendering gets left out is because I need fidelity of input (thus I need to capture it frequently) whereas FPS are not so important, as long as the user can’t really observe any graphical hiccups. Also, rendering in my games usually ends up taking much longer than the logic, so it makes sense to let the logic run free and reign in the rendering instead. I imagine Markus’s games don’t often suffer from these problems because he so often has very complicated game algorithms but very low res textures (like Minecraft as a good example).

Same here.

@Demonpants
The same approach Markus_Persson described can be used to run the logic at any fixed speed. This means you can place the input checks in the logic. Letting your simulation run at whatever speed it can means if that speed is very slow, everything explodes. Not prioritizing rendering means that objects may not move smoothly. Markus_Persson’s games don’t have problems because his time step approach solves them all.

More on a fixed time step:

It’s not about prioritizing framerate over game feel at all. It’s actually about making sure the game plays exactly the same on all computers (see the link Nate posted). The fact that the framerate gets as smooth as possible on any given computer is just a nice side effect.

There are a couple of downsides to this approach:

  • If game logic takes a long time, every n’th frame will render slower than the rest, causing stuttering graphics
  • The game visuals will always lag behind by 1/game_logic_hz seconds because of the interpolation. (Of course, this has the upside of putting the graphics more in synch with the audio if you tweak it right)
  • If a game logic step takes more than 1/game_logic_hz seconds on a computer, the game will never catch up and the game won’t be playable on that computer.

I feel all dumb and stupid for sticking to my fixed frame-rate logic now :confused:

But hey, it works. 1 tick = 1 frame, no interpolation, and if it doesn’t run at 60Hz… the computer’s too slow.

Cas :slight_smile:

Hey, don’t knock it! A lot of my “smaller” games (ludum dare, 4k) use fixed 60 hz logic.
A major advantage of that system is that each frame takes about as long to process and render, so there’s not as much uneven framerate.
A downside is playing a 60hz game on a monitor running anything other than 60 hz (except perhaps 30 or 120 hz) WILL get choppy.

Fortunately modern monitors almost all run at 60Hz! (Being typically TFT screens)

Cas :slight_smile:

Hm, interesting. I’ve always just used a delta to make up for stutters - if it has been a long time since the last step then everything will move a little bit further. But I suppose it makes sense in most settings to try to fix things around the graphics, if I really think about it. Having the logic stutter is not nearly as observable as having the graphics stutter.

Actually, no. My engine ticks at a fixed rate. I thought that was obvious from the sourcecode.

It is the rendering thread that makes a ‘best effort’ at ticking at a fixed rate.

Oh. Yeah I didn’t read your source.

I do it something like this:

while(running)
{
float delta = calculateDeltaFromLastTime();
update(delta);
if (timeSinceLastRender > renderThreshold)
{
render();
}
Thread.yield();
}

Usually I make it so that updates happen about 3 times per render. And obviously it goes as fast as it possibly can. Problem with this obviously is that it uses up your whole processor no matter what. I’ve been using the iPhone lately so this doesn’t matter, but usually when deving in Java I just use the sync() method in LWJGL so I don’t have to bother with it. Also, the reason I render less often on the iPhone is because long renders will lock receiving touch input which can cause very annoying control. So I update much more often. In Java I guess this isn’t a problem typically because input is usually handled in another Thread anyway. Although I’d imagine with LWJGL you might want to do this so you can poll the keyboard more often.

It’s very important to have a fixed tickrate, to keep your game deterministic, which is a requirement for any (serious) networked game.

update(delta) is the root of all evil, unless delta is a constant :wink:

Yeah I guess I never spent enough time thinking about it. The first time I put a custom loop in the game (I had been using java.util.Timer previously, which is even worse), I was using an example that Kev made (Space Invaders) that attempts to achieve a constant FPS but allows fluctuation to happen. Or so I thought when I originally looked it. Since then I’ve been doing my own bastardized method.

So say you’re trying to get 60 fps but the game is only able to get 40 fps. Doesn’t that mean that you should be passing in a delta of 60 / 40 - moving everything a bit extra to make it all appear like it’s working the same? Or should you just do like Markus suggested and go for a low easy to achieve logic frame rate (around 30 hz) and let the rendering take as much time as it needs? I guess really what should happen is you force the logic to maintain its 60 hz as best you can, sucking away from rendering frequency if you’re having trouble meeting your desired hz. Yes/no?

I think I’m honestly going to go and adjust some of my game loops now, because this makes a lot of sense to me now that I’m actually thinking about it.

My posted sourcecode answers your questions.

(If the game can’t manage to tick at the frequency you have set, the game is not suited for the hardware it runs on)

Okay, thanks, Riven. :slight_smile:

Variable rate logic updates are not always evil (IMHO). If one is performing physical simulations and/or distributed system based network logic, then yes you’ll make your life much much easier.

I’d say this point falls into the common game programming catch-phrase of: “Well, it depends”.

there i also a way to achieve same logic everywhere even with a low timer accuracy and a free framerate.

starting with the following assumption :

  • no need to update physic if nothing is rendered to screen
  • need to perform logic at exact fixed time step

basically it run like that :

while(true)
{
 while(logicTime<currentTime)
 {
  performLogic();
  logicTime+=logicRate; //eg : 10ms
 }
 renderScreen();
}

in this one framerate is free and logic is always exacly the same between computer even with a very inaccurate (high granularity) timer, last but not least it is damnly simple to implement

EDIT: something is missing in the above code : no need to render scene if logicTime>=currentTime => sleep(1) this will avoid 100% cpu usage and will adapt framerate to logic

That’s pretty much exactly what I do, but with frame interpolation.