I'm in the process of creating a 2D game in OpenGL ES for the iPhone. I'm calling my game loop using NSTimer at an interval of 0.002 (60 fps) with repeats set to 'NO' (I'm running the NSTimer again from within the function as recommended):
-(void)gameLoop:(id)sender {
double currTime=(double)CACurrentMediaTime();
m_FPS_framesThisSecond++;
float timeThisSecond=currTime-m_FPS_lastSecondStart;
if (timeThisSecond>1.0f) {
m_FPS=m_FPS_framesThisSecond;
m_FPS_framesThisSecond=0;
m_FPS_lastSecondStart=currTime;
}
[game update];
[game render];
//Continue the loop
[NSTimer scheduledTimerWithTimeInterval:0.002 target:self selector:@selector(gameLoop:) userInfo:nil repeats:NO];
}
This runs smoothly on my 3GS, however when I test this on a 2G the game runs much slower and sometimes I get the occasional blank frames. When I lower the interval to 0.033 (30 fps). It's too slow on the 3G.
I know there must be some way to get constistent playback on both devices. Doodle Jump seems to run smoothly and at the same framerate on both phones.
The essential issue is this:
your game needs to update based on the amount of time that has passed since the last time it rendered, not by how many times it has rendered. Suppose you are computing the position of a sprite based on some velocity. The new position should be calculated based on the time between renders, not with the assumption 0.002 seconds has passed since the last render. This is called FPS independent animation.
So, here is a simple example:
// FPS dependent animation
int x = sprite.x + sprite.velocity.x
The proper way to do this would be to use the time since last render, and update proportionally. The following code assumes 0.002 is the time base.
// FPS independent animation
int x = sprite.x + sprite.velocity.x * time_since_last_render/0.002
On a device where the rendering takes twice as long, the next update will move the object twice as far, so it ends up in the same place as it would on a faster device.
A side issue, you shouldn't always render the next frame 0.002 seconds into the future. You should see how long the current frame took to render, subtract that from 0.002 and use that to schedule the next render. Naturally this number would have a minimum value of zero so on slow devices you don't start scheduling into the past. For example, if your rendering function takes exactly 0.002 seconds to render, this will lower your frame rate by half unnecessarily.