views:

3024

answers:

5

Hi. In order to cleanly port my game to the iPhone, I'm trying to make a game loop that doesn't use NSTimer.

I noticed in some sample code that, if using NSTimer, you'd set it up at the beginning with something like

    self.animationTimer = [NSTimer scheduledTimerWithTimeInterval:animationInterval target:self selector:@selector(drawView) userInfo:nil repeats:YES];

where drawView would look something like:


- (void)drawView 
{
    glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
    mFooModel->render();
    glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
    [context presentRenderbuffer:GL_RENDERBUFFER_OES];
}

When using this technique mFooModel renders fine, but I instead want to make my own game loop that calls drawView instead of having NSTimer call drawView 60 times a second. I would like something like:


while(gGameState != kShutDown)
{
    [self drawView]
}

Unfortunately when I do this, all I get is a black screen. Why does this happen? Is there anyway I can implement what I'm describing here?

The reason I want to avoid NSTimer is because I want to do physics and AI updates in the game loop. I use my own clock/timer to keep track of the amount of time that has elapsed so that I can do this accurately. Rendering happens as fast as possible. I try to use some of the techniques as described in this article

This is somewhat of an impulsive question (the one you do after you've been coding all day, get stuck, and hope the answer is there by morning)

Cheers guys.

+2  A: 

If you don't wish to use NSTimer you can try running the NSRunLoop manually:

static BOOL shouldContinueGameLoop;
static void RunGameLoop() {
    NSRunLoop *currentRunLoop = [NSRunLoop currentRunLoop];
    NSDate *destDate = [[NSDate alloc] init];
    do {
        // Create an autorelease pool
        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
        // Run the runloop to process OS events
        [currentRunLoop runUntilDate:destDate];
        // Your logic/draw code goes here
        // Drain the pool
        [pool drain];
        // Calculate the new date
        NSDate *newDate = [[NSDate alloc] initWithTimeInterval:1.0f/45 sinceDate:destDate];
        [destDate release];
        destDate = newDate;
    } while(shouldContinueGameLoop);
    [destDate release];
}
rpetrich
Thanks. This is very useful code.
I forgot to warn that if you have more work than 1/45th of a second (or the runloop stalls for more than that) you will have stuttering, delayed touch events and other odd issues. Be sure to test thoroughly. Updating the code to account for that is possible, but application specific
rpetrich
Doesn't all the alloc / dealloc in the run loop affect performance? Is there a way to move that all outside the loop?
Andrew
Hardly. The pool is there so that if the runloop creates temporary objects, they will be disposed instead of building up. The two `NSDate`s are a tiny tiny drop in the bucket.
rpetrich
+6  A: 

Another option with iPhoneOS 3.1 is to use the new CADisplayLink api. This will call the selector you specify when the screen contents needs to be updated.

displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(renderAndUpdate)];
[displayLink setFrameInterval:2];
[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];

The new OpenGL project template in XCode also use the CADisplayLink if you need some more example code.

Jens Utbult
+1  A: 

While using CADisplayLink is a really good alternative for 3.1 based games,
anything using "Timer" is a really bad idea.

The best approach is the good old "tripple buffering" to decouple the GPU work.

Fabien has a very good explanation in his Doom Iphone review:
http://fabiensanglard.net/doomIphone/

Andreas
A: 

Regarding CADisplayLink and the Doom for iPhone article by Fabien, I emailed Fabien and I think the best option is subjective. Performance-wise DisplayLink and triple buffering should be the same, but DisplayLink is only available on > OS 3.1. So that should be your determining factor.

CJ Hanson
A: 

Take care using self as a target for displayLinkWithtarget, the manual says "The newly constructed display link retains the target". Martin

Martin Lockett