views:

85

answers:

2

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.

+1  A: 

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.

freespace
Thanks! Would I calculate the time_since_last_render by simply getting the difference between the current time minus the time from the previous frame?
Scott
+1  A: 

There are actually two types of frame rates:

  • Display frame rate: rate at which the screen is redrawn
  • Logical frame rate: rate at which the game logic is updated (game speed)

I think you are looking for a constant logical frame rate that is decoupled from a display frame rate that adjusts to the phone hardware's capability. Compare to your game loop that must complete one display frame for each logical frame.

Check out the "Constant Game Speed with Maximum FPS" algorithm described in deWiTTERS Game Loop The game logic will appear to run at the same speed on both the 3G and 2GS, but the display frame rate will adjust to the slower 2G CPU. Although not covered in the article above, you can also cap the display frame rate to save battery.

Leftium