Overview
Having a lot of problem with choppy animations in my game loop. I'm running the loop from onDrawFrame and as you can see, my loop allows for frame skipping if it takes too long (I'm using a fixed time delta and my time 'slice' for 60fps is 16.666666ms).
I've confirmed that the stuttering happens when frames are skipped.
- When there are frame skips, how can I stop the animation from going choppy?
If it absolutely has to skip frames (I concede there will be times on certain devices when this will happen), how can I mitigate the choppiness?
- onDrawFrame(GL10 gl) seems to be called up to 24ms since the last time it was called.
On my Google Nexus 10 Tablet, my actual rendering can take anywhere between 5 and 14ms and my game updates only take between 0 and 4ms so most of the time it should fit nicely into the 16.6666 time slice. However, when I remove everything from onDrawFrame, it seems to be called anywhere from 16 to 25ms apart. (Most times, it is above 16ms), therefore, it's skipping a lot of frames - even without rendering or updates being called!!
This seems to go against the whole point of a device running at 60FPS - is this VSync doing this? What's the point of it taking more than 16ms?
Things I've attempted
OK, I tried spawning a separate thread on which I run my updates (again, limiting them to 60 per second). I still get the problem.
I've also tried interpolation but this again, hasn't made any difference. (I am going to be posting a separate question to see if I've got that right).
I'm really struggling with this so I hope someone can help!
Code
float interpolation;
long nextGameTick = System.currentTimeMillis();
//loop counter
int loops;
//This is the amount of frames that we allow our app to skip before logic updating is affected (generally shouldn't happen)
final int maxFrameskip = 5;
//Game updates per second
final int ticksPerSecond = 60;
//Amount of time each update should take
final int skipTicks = (1000 / ticksPerSecond);
public void onDrawFrame(GL10 gl) {
//Set/Re-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++;
tics++;
}
interpolation = (float)(System.currentTimeMillis() + skipTicks - nextGameTick)
/ (float)skipTicks;
render(interpolation);
}
Applying interpolation
render(float interpolation){
//This example shows interpolation for X axis only. Y position (spriteScreenY is assumed to be valid)
interpolatedPosX = spriteGridX+(SpriteXVelocity*dt)*interpolation;
spriteScreenPosX = interpolationPosX * screenWidth;
drawSprite(spriteScreenX, spriteScreenY);
}