Game loop with separate timer for rendering and game logic

I want to separate the game logic and rendering into two different cycles, because I do not want fps to control the speed of the game. I tried to achieve this by creating CADisplayLink for rendering and NSTimer for game logic. But a strange thing happened:

Sometimes (1 out of 15 application launches) the game runs at very low fps (about 5-10), but the rest of the time it is completely smooth. If I remove the NSTimer logic and combine the two loops, the fps will be constantly high, but this is clearly not an acceptable solution. Therefore, it seems that sometimes two timers “delay each other” or something like that, but I do not quite understand the inner workings of runloops.

This is how I create a timer and 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 problem and how to fix it?

Or can you recommend any other solutions to separate the rendering cycle and game logic?

+6
source share
3 answers

You can do this with a single loop, using either your gameTimer or CADisplayLink, measuring the time elapsed since the last loop, and using it to increase 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]; } 

As usual, I handle these things. Usually I like to create update functions right in my game objects. Therefore, the ship update will look like this:

 [ship update:timePassed_mc]; 
+3
source

So, I ended up proposing Andrew Zimmer with some minor changes as I update my game objects together between equal intervals.

So, I use only one loop that runs CADisplayLink. Here's the last 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 (...) // } 
+1
source

Remember that NSTimer will not give you any reliable deadlines. Its closing, but not guaranteed, and if you start the game cycle, you will get a random shot.

You can make your game logic in the stream and sleep mode until the next frame (this will not be a constant amount of time).

0
source

Source: https://habr.com/ru/post/900231/


All Articles