I want to separate the game logic and the rendering into two different loops, because I don't want the fps to control the game speed. I tried to achieve this by creating a CADisplayLink for the rendering, and an NSTimer for the game logic. But then a strange thing happened:
Sometimes (1 out of 15 application runs) the game runs on a very low fps (about 5-10), but the rest of the times it's completely smooth. If I remove the game logic's NSTimer and combine the two loops the fps is consistently high, but it's obviously not an acceptable solution. So it looks like that sometimes the two timers 'delaying each other' or something like that, but I don't completely understand the inner working of runloops.
Here's how I create the timer and the displaylink:
NSTimer *gameTimer = [[NSTimer alloc] initWithFireDate:[NSDate date] interval:1.0 / 60.0 target:self selector:@selector(gameTimerFired:) userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:gameTimer forMode:NSDefaultRunLoopMode];
[gameTimer release];
CADisplayLink *aDisplayLink = [[UIScreen mainScreen] displayLinkWithTarget:self selector:@selector(drawFrame)];
[aDisplayLink setFrameInterval:animationFrameInterval];
[aDisplayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
self.displayLink = aDisplayLink;
Can you tell me what causes the fps issue and how to fix it?
Or can you recommend any other solutions to separate the rendering and the game logic loop?
You can do this with one loop using either your gameTimer or the CADisplayLink by measuring the time passed since the last loop and using it to augment your game logic.
So..
NSDate *oldTime = [[NSDate date] retain];
-(void)updateGame {
NSDate *curTime = [NSDate date];
NSTimeInterval timePassed_ms = [curTime timeIntervalSinceDate:oldTime] * 1000.0;
[oldTime release];
oldTime = curTime;
[oldTime retain];
//use the timePassed_ms to augment your game logic. IE: Moving a ship
[ship moveLength:ship.velocity * timePassed_ms/1000.0];
}
That's usually the way I handle this sort of stuff. I usually like to build update functions right into my game objects. So updating the ship would actually look like this:
[ship update:timePassed_mc];
So I ended up using what Andrew Zimmer suggested, with some tiny modifications, since I update my game objects together between equal intervals.
So I'm using only one loop, the one that launched by the CADisplayLink. Here's the final code:
- (void)drawFrame
{
if (!isGameTimerPaused)
{
NSDate *newDate = [NSDate date];
timeSinceLastUpdate += [newDate timeIntervalSinceDate:lastUpdateDate];
while (timeSinceLastUpdate > 1.0 / 60.0)
{
[self updateGame]; // UPDATE GAME OBJECTS
timeSinceLastUpdate -= 1.0 / 60.0;
}
[lastUpdateDate release];
lastUpdateDate = [newDate retain];
}
// DRAWING CODE HERE
(...)
//
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With