Ah! I missed the obvious reason for the odd behavior while first looking at your code (I think). Essentially, there are three potential problems here:
First: Missing Updates
Each and every iteration you'll update your game logic only once (or not at all). In a similar way, you draw your screen (or skip drawing).
However, this causes one problem:
- Let's assume you'd like to update the game 100 times per second (makes it easier to explain/compare).
- This means there are 10 ms between updates.
- As long as each and every iteration is faster than 10 ms, you won't notice any problem.
- Now assume there's some stuttering for a moment (don't know, let's say you connected some device or whatever).
- This iteration, 25 ms have passed. This means that you'd have to do two updates before rendering again, i.e.
update(); update(); render();.
- Due to your design, you won't do this. Instead you'll get
update(); render(); update(); render();.
- This might cause visual/noticeable stuttering (especially if we're talking about more than just two updates!) since the game now obviously lags a bit (till you've cleared your update backlog; assuming you're able to do so).
- This also means you're essentially connecting your logic updates to the framerate. If you can't render at the intentional framerate, your game logic will slow down as well.
Second: Race Conditions
This is something you can't really avoid if you'd like to do fixed timesteps:
- Let's assume you're rendering 60 times per second and updating 30 times per second.
- Theoretically, this should trigger one call to
update() for every second call to
- However, this is essentially just an average number.
- It's possible, that between two frames you miss your update since you're like 5 ns too fast.
- Rather than calling
update(); render(); render(); update(); render(); render(); you'd at once call
update(), render(); render(); render(); update(); render();. Overall you're still at 60/30 fps, but your timing is still off a bit (especially if you've got vertical sync enabled).
- This can cause minor inconsistencies that might become noticeable, especially when things on screen are moving quite fast.
- Fix Your Timestep! elaborates a bit more and also provides a solution to this.
Third: Rounding/Precision Issues
While I can't say this for sure, you might be running into precision issues with your variables.
delta is some floating point variable (due to the
/ ns step?), you might run into rounding issues here.
System.nanoTime() doesn't necessarily return a very precise time value - it's implementation dependent. It could be in nanoseconds, but it could as well just return whole milliseconds or even less (could also depend on the system's energy saving settings):
This method provides nanosecond precision, but not necessarily nanosecond resolution (that is, how frequently the value changes) - no guarantees are made except that the resolution is at least as good as that of currentTimeMillis().
Depending on your actual number of frames/updates this might be neglectable though. (And there isn't really anything you can do about this, other than trying to pick sane numbers/scaling that will work for you.)