mainGameLoop changes
I’ve made some changes to my game loop again which I’ve posted below. This game loop can be cut and pasted straight into the EAGLView.m file.
The first change was to add an autorelease pool inside the while loop. This was necessary to resolve a memory leak when using NSString stringWithFormat inside the renderScene method. Creating an autorelease pool and then draining it each cycle removes that memory leak.
The second issue was lost touch events. To resolve this I needed to increase the amount of time the CFRunLoopRunInMode command had to process other queue events, so moved from 0 to 0.02.
The new run loop is shown below. Let me know if you have any questions or issues.
- (void)mainGameLoop {
// Create variables to hold the current time and calculated delta
CFTimeInterval time;
float delta;
// This is the heart of the game loop and will keep on looping until it is told otherwise
while(true) {
// Create an autorelease pool which can be used within this tight loop. This is a memory
// leak when using NSString stringWithFormat in the renderScene method. Adding a specific
// autorelease pool stops the memory leak
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// I found this trick on iDevGames.com. The command below pumps events which take place
// such as screen touches etc so they are handled and then runs our code. This means
// that we are always in sync with VBL rather than an NSTimer and VBL being out of sync
while(CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.02, TRUE) == kCFRunLoopRunHandledSource);
// Get the current time and calculate the delta between the lasttime and now
// We multiply the delta by 1000 to give us milliseconds
time = CFAbsoluteTimeGetCurrent();
delta = (time - lastTime) * 1000;
// Go and update the game logic and then render the scene
[self updateScene:delta];
[self renderScene];
// Set the lasttime to the current time ready for the next pass
lastTime = time;
// Release the autorelease pool so that it is drained
[pool release];
}
}
Mike
20 Comments
mike on April 18th, 2009
Hi Tom
You have indeed spotted an error in the code. The delay should not be set to 0.02, but should be set to 0.002. The while should look like this
while(CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.002, TRUE) == kCFRunLoopRunHandledSource);
0.02 runs ok on the Sim but is giving the CFRunLoopRunInMode WAY more time than is necessary to process events which is causing it to be slow and also to cause slow downs when a tap event is processed. There should be no reason why you need to have multiple whiles in your game loop. This could add significant delay to the game loop and cause the game to run slowly and jitter.
I am not sure what the ghosting/jitter is when rendering fast moving objects. Is there any code you can either share in a comment or sent to me at mike@domain_of_blog.co.uk and I’ll take a look. In testing I’ve had over 100 objects moving at speed and not seen what you have described so I am now intrigued :D
Mike
Tom on April 20th, 2009
Hi Mike
I’ve altered my code to reflect the changes and found my app to be running much better, thanks! I did think having multiple whiles in my game loop was a little silly, so I was glad to remove them.
The jittering I was experiencing can be replicated quite easily, just increase the time on CFRunLoopRunInMode to 0.1
I can post some code if you like.
Tom
Mike Weller on April 23rd, 2009
By the way, you might want to investigate creating your own thread for the game loop. This will completely eliminate the events-getting-lost issue because you will be running outside the event queue. You can use some sleep methods to control how often your loop iterates.
bob on April 24th, 2009
Hi Everyone
I replaced the main gameloop with this and increased the speed 10 fold and touch events still work fine.
- (void)mainGameLoop {
//Get the resolution of the iPhone timer.
mach_timebase_info_data_t timerInfo;
mach_timebase_info(&timerInfo);
//Store the ratio between the numberator and the denominator.
const float TIMER_RATIO = ((float)timerInfo.numer / (float)timerInfo.denom);
//The resolution of the iPhone is in nanoseconds.
const uint64_t RESOLUTION = 1000000000;
//Create a target time variable based upon the iPhone timer resolution and our FPS.
const float TARGET_TIME = (float)RESOLUTION / 60;
//Start the frame average at the target time for a frame.
float frameAverage = TARGET_TIME;
//Create an artificial last update time that matches our target delta.
float lastUpdateTime = mach_absolute_time() * TIMER_RATIO - TARGET_TIME;
//It will act this many times before it draws.
const unsigned int DRAW_FREQUENCY = 3;
unsigned int timesUpdated = 0;
//Start the game loop.
while (TRUE){
//Create the autorelease pool.
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
//Get the current time and convert it to a useable standard value (ns).
uint64_t now = mach_absolute_time() * TIMER_RATIO;
//Adjust the frame average based upon how long this last update took us.
frameAverage = (frameAverage * 10 + (now - lastUpdateTime)) / 11;
//Create a delta out of the value for the current frame average.
float delta = frameAverage / TARGET_TIME;
//Refresh the last update time.
lastUpdateTime = now;
//Yield to system calls (touches, etc.) for one ms 0.002.
while( CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.009, FALSE) == kCFRunLoopRunHandledSource);
// Go and update the game logic and then render the scene
[self updateScene:delta];
//Draw the game.
timesUpdated++;
if (timesUpdated >= DRAW_FREQUENCY) {
[self renderScene];
timesUpdated = 0;
}
// Release the autorelease pool so that it is drained
[pool release];
}
}
bob on April 24th, 2009
PS:
change the .009 to .002 I have it at .009 because it runs way to fast to watch when using the iphone simulator.
kristapsz on April 24th, 2009
I have two issues with this loop. Basically when I try to exit the loop, then it crashes after a moment. Sometimes it happens right after first time I try to exit, sometimes after second time.
You can replicate this, when using Tutorial 5 project. For example, just make one boolean variable and add touches begin method, which switches boolean to false… (I must say, I tried it in simulator)
Second issues it that when I use timers, it consumes 12MB of memory, but with this loop, it takes 18MB of memory.
Anyone has some ideas or comments on these things?
bob on April 24th, 2009
Hi Kristapsz
I’m running approx 50 sprites all animated, touches, explosions and what not. I haven’t had a crash yet ( knock on wood ).
exiting loop, do you mean when it does the updateScene and returns to render.
kristapsz on April 24th, 2009
Hi Bob. With exiting loop I mean to stop the while(true){} loop, so that the animations would stop and I could pause game or to go to the menus, etc…
Otherwise, if you start the game loop and never stop it, everything works fine.
bob on April 24th, 2009
@kristapsz
Here what to do, change the while loop to..
//Start the game loop.
while (gameState == kGameStateRunning){
then somewhere in your code you just control that variable like so..
gameState = kGameStateRunning;
or
gameState = kGameStatePaused;
this will stop the timing loop and you can do other things then when you are ready just set gameState to lets say ‘kGameStateRunning’ and the timer will resume.
I tried this and it seems to work. Don’t forget to set those variables up.
Hope this helps.
mike on April 24th, 2009
Hi bob, thanks for posting the new game loop. I found the same post and have been playing with it to see the benefits :)
The speed increase you can see is because it is performing 3 game logic updates for every one render update. DRAW_FREQUENCY is what is used to define how many logic updates there should be before a render is performed.
The real benefit of this is that if you have a busy render stage it can make the kind of loop I have at the moment skip, i.e. as we are using delta as part of the calculation to move something, if the render takes a while, the delta would be large. If you have fast moving objects, this jump in movement could mean that the object passes through other objects i.e. the jump is so big, the collision detection code does not get to the the two objects intersecting.
The loop you have posted helps prevent that as there are more updates than renders, so you don’t get the large step increases and collisions should be spotted :) Also, this loop will not generate large deltas, but slow the game down and even stop it if the CPU gets busy etc which can be a good thing. That’s what I’ve seen so far. I am planning to include this as the game loop eventually once I’ve finished working out how it works fully
@kristapsz what kind of error are you seeing when it crashes, do you get any error messages? I’ll have a go at exiting the loop and seeing if I can replicate the problem and track it down.
Thanks for the posts. These discussions certainly make me think and readers will get a lot from them as well.
Thanks
Mike
bob on April 24th, 2009
@Mike
Thanks for the info on how it all works, it appears apple has put out some new code which even has a particle system object and some other stuff. Here’s a link to it just in case you haven’t seen it. Its called TOUCH FIGHTER 2
https://connect.apple.com/cgi-bin/WebObjects/MemberSite.woa/wa/getSoftware?fileID=24200
PS: thanks again for all of your help :)
mike on April 24th, 2009
@bob no problems and thanks for the link. I’d not seen the updated touch fighter.
I’ve had a look through the code and I seem to find some Apple code confusing, and this is one of those projects. I need to spend some more time reviewing it, but it looks like there are some interesting items in there to learn from :)
I’m going to stick with my particle system at the moment. I’m just trying to debug the emission rate of particles and also add gravity etc, but apart from that its looking good.
I’m going to do more on my long flight over to the Middle East to try and pass the time more quickly.
Regards
Mike
bob on April 24th, 2009
@Mike
I feel the same way too. I’ve looked at some of apples examples and some are really bad. They may work but trying to understand what they are doing is very confusing. And a lot of it is just bits and pieces from other code which makes it even worse.
Well, I have to admit your code and tutorials are better than anything I’ve ever seen anywhere.
I hope you write a game and make a ton of $$$$ because you very well deserve it.
If you get a chance look at a free app download called iFighter I think its a good example of Tile Map scrolling. It is definitely one of the best I’ve seen for the iphone.
Have a good day and a good flight.. :)
kristapsz on April 25th, 2009
The error I get is:
-[NSCFSet getObjects:andKeys:]: unrecognized selector sent to instance 0x5219b0
….
Just to test here is what I did:
In eaglview.h I added
BOOL animationIsRunning;
In eaglview.m I added:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ animationIsRunning=!animationIsRunning;
[self mainGameLoop];
}
In mainGameLoop I changed while(true) with while(animationIsRunning) and in initGame I added animationIsRunning=TRUE;
Maybe it is crashing because I put touchesBegan method in eaglview class?
bob on April 25th, 2009
@kristapsz
Here’s what I have
in EAGLView.m at the top of the file
#define kGameStateRunning 1
#define kGameStatePaused 2
int gameState;
Then in initGame I have
gameState = kGameStateRunning;
This is the Touch stuff
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
if(gameState == kGameStatePaused){
//tapToBegin.hidden = YES;
gameState = kGameStateRunning;
[ self init ];
} else if ( gameState == kGameStateRunning){
[ self touchesMoved:touches withEvent: event];
}
}
Then it just passes the event to touches moved
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
//UITouch *touch = [[event allTouches] anyObject];
switch (bulletState) {
case kBulletFired:
[ self updateBullet ];
break;
case kBulletMoving:
[ self updateBullet ];
break;
case kBulletFinished:
bulletState = kBulletFired;
[ self updateBullet ];
break;
default:
break;
}
}
I hope this helps…
mike on April 25th, 2009
@kristapsx, thanks for the extra info. I’ll check this out later today. It sounds like its maybe trying to access something which has been released or is out of your current scope.
If you can send/post your mainGameLoop I’ll take a look.
Mike
Senor on May 31st, 2009
How to set a specific frame/second rate and calling the updateScene every frame/second (e.g 60 f/s)
Thank you.
Denver on September 21st, 2011
Call me wind because I am absluoetly blown away.
kyiohnex on September 22nd, 2011
a8CA3W vhsagzwrszha




Anonymous on April 18th, 2009
Hi
First of all, I’d like to say a massive thanks for your game programming articles, they have been a really big help!!
I’ve just altered my game code to reflect your changes and found the following..
1. If I have fast moving objects on the screen they have a ghosting juddery effect.
2. When I touch the screen, the game pauses momentarily to process the touch.
I’ve found this to be caused by the 0.02 parameter in the CFRunLoopRunInMode function.
What seems to improve things for me is changing the 0.02 back to 0 and performing the while loop multiple times. For Example,
while(CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, TRUE) == kCFRunLoopRunHandledSource);
[self updateScene:delta];
while(CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, TRUE) == kCFRunLoopRunHandledSource);
[self renderScene];
while(CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, TRUE) == kCFRunLoopRunHandledSource);
Is this a bad way of doing things?
Tom