I have written a simple 2d platform game for Android and am wondering how one deals with frame-skipping? Are there any alternatives? Let me explain further.
So, my game loop allows for the rendering to be skipped if game updates and rendering do not fit into my fixed time-slice (16.667ms). This allows my game to run at identically perceived speeds on different devices. And this works great, things do run at the same speed.
However, when the gameloop skips a render call for even one frame, the sprite glitches. And thinking about it, why wouldn't it? You're seeing a sprite move say, an average of 10 pixels every 1.6 seconds, then suddenly, there is a pause of 3.2ms, and the sprite then appears to jump 20 pixels. When this happens 3 or 4 times in close succession, the result is very ugly and not something I want in my game.
Therfore, my question is how does one deal with these 'pauses' and 'jumps' - I've read every article on game loops I can find (see below) and my loops are even based off of code from these articles. The articles specifically mention frame skipping but they don't make any reference to how to deal with visual glitches that result from it.
I've attempted various game-loops. My loop must have a mechanism in-place to allow rendering to be skipped to keep game-speed constant across multiple devices (or alternative, if one exists)
I've tried interpolation but this doesn't eliminate this specific problem (although it looks like it may mitigate the issue slightly as when it eventually draws the sprite it 'moves it back' between the old and current positions so the 'jump' isn't so big.
I've also tried a form of extrapolation which does seem to keep things smooth considerably, but I find it to be next to completely useless because it plays havoc with my collision detection (even when drawing with a 'display only' coordinate - see extrapolation-breaks-collision-detection)
I've tried a loop that uses Thread.sleep when drawing / updating completes with time left over, no frame skipping in this one, again fairly smooth, but runs differently on different devices so no good. And I've tried spawning my own, third thread for logic updates, but this, was extremely messy to deal with and the performance really wasn't good. (upon reading tons of forums, most people seem to agree a 2 thread loops ( so UI and GL threads) is safer / easier).
Now if I remove frame skipping, then all seems to run nice and smooth, with or without inter/extrapolation. However, this isn't an option because the game then runs at different speeds on different devices as it falls behind from not being able to render fast enough.
I'm running logic at 60 Ticks per second and rendering as fast as I can.
I've read, as far as I can see every article out there, I've tried the loops from My Secret Garden and Fix your timestep.
I've also read:
Against the grain
deWITTERS Game Loop
Plus various other articles on Game-loops. A lot of the others are derived from the above articles or just copied word for word.
These are all great, but they don't touch on the issues I'm experiencing. I really have tried everything I can think of over the course of a year to eliminate these glitches to no avail, so any and all help would be appreciated.
A couple of examples of my game loops (Code follows):
From My Secret Room
public void onDrawFrame(GL10 gl) {
//Rre-set loop back to 0 to start counting again
loops=0;
while(System.currentTimeMillis() > nextGameTick && loops < maxFrameskip) {
SceneManager.getInstance().getCurrentScene().updateLogic();
nextGameTick += skipTicks;
timeCorrection += (1000d / ticksPerSecond) % 1;
nextGameTick += timeCorrection;
timeCorrection %= 1;
loops++;
}
extrapolation = (float)(System.currentTimeMillis() + skipTicks - nextGameTick) / (float)skipTicks;
render(extrapolation);
}
And from Fix your timestep
double t = 0.0;
double dt2 = 0.01;
double currentTime = System.currentTimeMillis()*0.001;
double accumulator = 0.0;
double newTime;
double frameTime;
@Override
public void onDrawFrame(GL10 gl) {
newTime = System.currentTimeMillis()*0.001;
frameTime = newTime - currentTime;
if ( frameTime > (dt*5)) //Allow 5 'skips'
frameTime = (dt*5);
currentTime = newTime;
accumulator += frameTime;
while ( accumulator >= dt )
{
SceneManager.getInstance().getCurrentScene().updateLogic();
previousState = currentState;
accumulator -= dt;
}
interpolation = (float) (accumulator / dt);
render(interpolation);
}