views:

620

answers:

2

Hi all,

I'm new to iPhone dev, but trying to build a 2D game. I was following a book, but the game loop it created basically said:

function gameLoop
    update()
    render()
    sleep(1/30th second)
    gameLoop

The reasoning was that this would run at 30fps. However, this seemed a little mental, because if my frame took 1/30th second, then it would run at 15fps (since it'll spend as much time sleeping as updating).

So, I did some digging and found the CADisplayLink class which would sync calls to my gameLoop function to the refresh rate (or a fraction of it). I can't find many samples of it, so I'm posting here for a code review :-) It seems to work as expected, and it includes passing the elapsed (frame) time into the Update method so my logic can be framerate-independant (however I can't actually find in the docs what CADisplayLink would do if my frame took more than its allowed time to run - I'm hoping it just does its best to catch up, and doesn't crash!).

//
//  GameAppDelegate.m
//
//  Created by Danny Tuppeny on 10/03/2010.
//  Copyright Danny Tuppeny 2010. All rights reserved.
//

#import "GameAppDelegate.h"
#import "GameViewController.h"
#import "GameStates/gsSplash.h"

@implementation GameAppDelegate

@synthesize window;
@synthesize viewController;

- (void) applicationDidFinishLaunching:(UIApplication *)application
{
 // Create an instance of the first GameState (Splash Screen)
 [self doStateChange:[gsSplash class]];

 // Set up the game loop
 displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(gameLoop)];
 [displayLink setFrameInterval:2];
 [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
}

- (void) gameLoop
{
 // Calculate how long has passed since the previous frame
 CFTimeInterval currentFrameTime = [displayLink timestamp];
 CFTimeInterval elapsed = 0;

 // For the first frame, we want to pass 0 (since we haven't elapsed any time), so only
 // calculate this in the case where we're not the first frame
 if (lastFrameTime != 0)
 {
  elapsed = currentFrameTime - lastFrameTime;
 }

 // Keep track of this frames time (so we can calculate this next time)
 lastFrameTime = currentFrameTime;

 NSLog([NSString stringWithFormat:@"%f", elapsed]);

 // Call update, passing the elapsed time in
 [((GameState*)viewController.view) Update:elapsed];
}

- (void) doStateChange:(Class)state
{
 // Remove the previous GameState
 if (viewController.view != nil)
 {
  [viewController.view removeFromSuperview];
  [viewController.view release];
 }

 // Create the new GameState
 viewController.view = [[state alloc] initWithFrame:CGRectMake(0, 0, IPHONE_WIDTH, IPHONE_HEIGHT) andManager:self];

 // Now set as visible
 [window addSubview:viewController.view];
 [window makeKeyAndVisible];
}

- (void) dealloc
{
 [viewController release];
    [window release];
    [super dealloc];
}

@end

Any feedback would be appreciated :-)

PS. Bonus points if you can tell me why all the books use "viewController.view" but for everything else seem to use "[object name]" format. Why not [viewController view]?

A: 

You have Cocos2D listed as a tag in your question but you're not actually using any Cocos2D code. Have you considered doing a Cocos2D implementation for your games? It will save you some unneeded hassle.

As for your syntax question [myObject view] is used for calling methods on myObject while myObject.view is used for setting/getting instance variables exposed as properties. I don't recall if you can retrieve instance variables using [myObject view] as well but if that works then I guess the only difference between the two is the syntax and you could use both methods to retrieve instance variables.

Hope some of that rambling is useful to you.

Rob Segal
Whoops! Told you I didn't know what I was doing! Removed the tag now.Did some reading on the syntax thing, understand it better now. Seems that [myclass property] and myclass.property can do different things. The first is a property (which can execute code, though in most cases I suspect it just returns the variable) whereas the dot is accessing the variable directly (and won't have custom code).Seems like the difference between exposing a private field and a property in C#, only that here they can both appear to have the same name!
Danny Tuppeny
Actually, now I'm confused, because I'm getting errors:accessing unknown 'rect' getter methodWhich makes me think that using dot is exactly the same as using get/set methods!
Danny Tuppeny
If you use the dot you need to define the instance variable you want to retrieve as a property and then synthesize it as well. Lookup syntax for "@property" and "@synthesize" calls.Getter/setter methods get implicitly created for you but only after making use of @synthesize. This is why you are getting an unknown 'rect' getter method.
Rob Segal
I've done that now, though my understanding was that it just creates the get/set methods, so now:object.myval = somethingis just the same as doing:[object setMyval:something];?
Danny Tuppeny
From what I know yes object.myval = something and [object setMyval:something] should be equivalent. Get/set methods for properties don't get created automatically for you to my knowledge until you synthesize them.
Rob Segal
A: 

From many GL example by Apple, I think you should use a timer.

animationTimer = [NSTimer scheduledTimerWithTimeInterval:(1.0/60.0)
                                                  target:self
                                                selector:@selector(updateAndRender)
                                                userInfo:nil
                                                 repeats:TRUE];
iwat
I believe CADisplayLink was added as a better way, but because it was only recently added, there seems to be little documentation/samples :(
Danny Tuppeny