Game Timer

Nibbie
Posts: 4
Joined: 2012.12
Post: #1
Hey, Quick question. I have C and Object C experience with a app in the app store. but I started on my first game to have fun learning about gaming. I started to write a little game where a ship shoots rocks falling from the top of the iphone screen, very simple. left and right UIButtons to move left and right and a UIButton to fire.

But I started with a default NSTimer and this caused slow downs in the game and jitter. So I spent the afternoon reading up on timers and what a lot there is to read.

So testing tonight I dropped everything in a while loop just to start learning. Now that I have it in a while loop it might run pretty fast, which is fine but while in the loop I have no access to my UIButtons? My guess is of course that the while loop won't release control while it is TRUE.

So how do I gain access to the UIButtons while the loop is running?
••••••••••••••••
Code tags don't seem to work? sorry

[Moderator AnotherJake says: I edited them into your post for you. In the post editor, click on the "#" to use the tags.]

Code:
-(void)viewDidAppear:(BOOL)animated{
       [self runLoop];
}

-(void)runLoop{
    while( game_is_running ) {
        [self shipMove]; // Uses UIButtons to move left and right.
        [self moveShotPosition]; //Moves the shot int eh y direction
        [self moveRockPostion]; //moves the rock in the y direction down
        [self checkCollision]; // checks for collision of rock and ship or shot and rock
        
        if (!music.playing && lives > 0) { // If music stops, restart it.
            [music play];
        }
    }
}
••••••••••••••••••••
Quote this message in a reply
Moderator
Posts: 3,579
Joined: 2003.06
Post: #2
It doesn't work because the Cocoa UI operates using events. When you loop on your own like that, it prevents the normal event system from working. If you want to use Cocoa, you have to use a timer, not a while() loop.
Quote this message in a reply
⌘-R in Chief
Posts: 1,261
Joined: 2002.05
Post: #3
(Dec 1, 2012 06:55 PM)larswik Wrote:  But I started with a default NSTimer and this caused slow downs in the game and jitter.

The timer itself isn't going to cause anything to slow down and jitter. Describe your approach and what you saw in more detail.


The reason the while loop doesn't let you use any of your buttons is because the main run loop isn't being allowed to run to service any events. You could throw some time into your loop to let the run loop do its things, but I don't recommend going down this road at all.
Quote this message in a reply
Nibbie
Posts: 4
Joined: 2012.12
Post: #4
Thanks guy. This is the first weekend I have tried to dabble in games and it is a whole different world then the programming that I am use to. Doing research I came across a nice little IOS Class called 'CADisplayLink'

This solved all of the problems that I was having. But if I ever decided to port a game over to an android it might be better to set up some kind of a universal run loop.

Searching the web I come across things called "Delta Time" but most all loops, with delta time are in a while loop. Are there any good tutorials that breakdown and explain game timers? I had no idea how important a good game timer is.
Quote this message in a reply
Moderator
Posts: 3,579
Joined: 2003.06
Post: #5
Getting your delta time correct isn't as easy to figure out at first as one might think. Many (most?) examples use delta time as the time between frames and then base the animation directly on that, which we often call variable rate timing. It turns out to be a no-no because it is variable (i.e. it changes from frame to frame), although it's the easiest to start with and you get fantastic results if your frame rate is steady. I mistakenly used this for many years. Unfortunately, if the frame rate hiccups, your game is susceptible to all sorts of math failures which are hard, if not impossible, to work around. I have seen some pretty complex examples, including a physics engine, which used delta time like this, and it was easy to break the simulation by simply resizing the window which disturbed the frame rate enough to break collision detection and explode springs, among other bizarre side-effects. This just isn't the right way to do it.

Much better is to use a fixed rate timing step, which is hard to grasp at first because it is a bit of a brain twister to implement, although the code is very simple. Read this. Unfortunately, you'll get some intermittent chop with this because your fixed rate update will never be exact with the actual frame rate, but your integration is far more reliable (integration meaning calculus integration for things like physics and collision detection). It is a necessary trade-off for anything more complex than pong or asteroids. Fortunately, when the game scene is busy, you never notice the intermittent chop. I recommend starting with this, if you can grasp it.

The very best is to use a fixed rate step with the above technique and then further interpolate that to match the frame delta. Read this. I call this sub-tick interpolation, and it's much harder to implement, depending on how complex your game is. It opens up the door to do more complicated things like client side prediction, game time manipulation (i.e. faster/slower playback). I don't recommend this to start with.
Quote this message in a reply
⌘-R in Chief
Posts: 1,261
Joined: 2002.05
Post: #6
The first article is also here on iDG. http://www.idevgames.com/articles/timebasedanimation
Quote this message in a reply
Nibbie
Posts: 4
Joined: 2012.12
Post: #7
Thanks for the detailed info and the links. There is so much to know when it comes to game timers. I guess for simplicity sake while I take on game programming I will stick to the CALinkDisplay. I did some research on this today and it seemed very robust for this kind of work.

I am going to read the links you posted. But today, Sunday was a full day on learning about timers.

Thanks a lot!
Quote this message in a reply
Member
Posts: 241
Joined: 2008.07
Post: #8
I recommend using mach. Extremely accurate time deltas.

Here's my implementation of its use:

.h:

Code:
//
//  CPhysicsTimer.h
//
//  Created by Brandon Mantzey on 10/22/08.
//

#import <mach/mach.h>
#import <mach/mach_time.h>
#import <unistd.h>

#define NANO_TO_DOUBLE(X) (X*10e-10)


@interface CPhysicsTimer : NSObject
{
    unsigned short FPS;
    uint64_t TimeStart;
    uint64_t TimeDelta;
    uint64_t TimeLastFrame;
    uint64_t TimeElapsed;
    uint64_t ElapsedNano;
    uint64_t TimePrev;    
    
    double systemSpecificRatio;
}
@property unsigned short FPS;

// This function should also be called to initialize and restart/start the timer.
// In most cases, this function should only ever be called once.
-(void) Reset;

// !!!CALL ONLY ONCE PER FRAME!!!  If you call this more than once per frame, severe timing
// issues will occur.
// This function should be called once, only once, and every frame once only.
-(void) Update;

// Returns the time calculated since the Reset function was called.
-(double) GetTime;
// Returns the time difference between Update calls.
-(double) GetDeltaTime;


@end

.m:

Code:
//
//  CPhysicsTimer.m
//  TrainLoaded
//
//  Created by Brandon Mantzey on 10/22/08.
//  Copyright 2008 Stratogon. All rights reserved.
//

#import "CPhysicsTimer.h"


@implementation CPhysicsTimer

-(id)init
{
    self = [super init];
    return self;
}

///////////////////////////////////////////////////////////////////////////////
// Use to initialize & to start timer.
// All time calculations are calculated from game time at this point.
///////////////////////////////////////////////////////////////////////////////
-(void) Reset
{
    TimeStart = mach_absolute_time();
    
    getpid();
    
    m_nFPS = 0;
}
///////////////////////////////////////////////////////////////////////////////
// Use to update timer.
// ONLY CALL ONCE PER FRAME!!!
///////////////////////////////////////////////////////////////////////////////
-(void) Update
{
    static int frames = 0;
    static mach_timebase_info_data_t sTimebaseInfo;
    
    if(TimeLastFrame == 0)
        TimeLastFrame = [self GetTime];
    uint64_t timeNow = [self GetTime];
    if(TimePrev == 0 || TimePrev == timeNow)
        TimePrev = [self GetTime];

    getpid();
    
    frames++;
        
    uint64_t diff = timeNow - TimePrev;
//    double testMacro = NANO_TO_DOUBLE(diff);
    
    if(sTimebaseInfo.denom == 0)
    {
        mach_timebase_info(&sTimebaseInfo);
    }
    
    ElapsedNano = diff * sTimebaseInfo.numer / sTimebaseInfo.denom;    
    
    // log how many frames since 1 second
    if(NANO_TO_DOUBLE(diff) > 1.0)
    {
        m_nFPS = frames / (int)(NANO_TO_DOUBLE(diff));
        frames = 0;
        TimePrev = timeNow;
    }
    
    // calculate delta time
    TimeDelta = (timeNow - TimeLastFrame);
    TimeLastFrame = timeNow;
}
///////////////////////////////////////////////////////////////////////////////
// Returns ( in seconds ) the amount of time from last Reset call.
///////////////////////////////////////////////////////////////////////////////
-(uint64_t) GetTime
{
    uint64_t time = mach_absolute_time();
    return (time - TimeStart);
}
-(double) GetTimed
{
    uint64_t time = mach_absolute_time();
    
    uint64_t diff = time - TimeStart;
    
    return NANO_TO_DOUBLE(diff);
}
///////////////////////////////////////////////////////////////////////////////
// Returns ( in seconds ) the amount of time from last frame.
///////////////////////////////////////////////////////////////////////////////
-(uint64_t) GetDeltaTime
{
    return TimeDelta;
}
-(double) GetDeltaTimed
{
    return NANO_TO_DOUBLE(TimeDelta);
}

///////////////////////////////////////////////////////////////////////////////
// Returns the current number of FRAMES PER SECOND.
///////////////////////////////////////////////////////////////////////////////
-(unsigned short) GetFPS
{
    return m_nFPS;
}


-(void) dealloc
{
    [super dealloc];
}



@end

I haven't looked at that in years, so if you see something silly, take it with a grain of salt and don't beat me up too bad. lol Hope this helps.
@Jake - I just read your post, of course after posting mine. Are you saying that use of the class I just posted is a no-no for the reasons you described?
Quote this message in a reply
Moderator
Posts: 3,579
Joined: 2003.06
Post: #9
(Dec 4, 2012 03:09 PM)bmantzey Wrote:  @Jake - I just read your post, of course after posting mine. Are you saying that use of the class I just posted is a no-no for the reasons you described?

Yes that is what I am saying -- by using a variable delta time, your integration is susceptible to instability and can break if the frame rate varies too much. Those two articles I linked to offer good insight on this issue.

Also, yes, mach time is the best on Apple platforms. Here's the most common way I've seen it used by several folks around here at iDG, which I, and probably everyone else, got from some Apple code sample:

Code:
#include <mach/mach.h>
#include <mach/mach_time.h>
#include <mach/mach_host.h>

double CurrentTimeInSecondsGet(void)
{
    static uint64_t        timebase = 0;
    uint64_t            time, nanos;
    double                seconds;
    
    if (timebase == 0)
    {
        // calculate the time base for this platform only on the first time through
        mach_timebase_info_data_t    timebaseInfo;
        mach_timebase_info(&timebaseInfo);
        timebase = timebaseInfo.numer / timebaseInfo.denom;
    }
    
    time = mach_absolute_time();
    nanos = time * timebase;
    seconds = (double)nanos * 1.0e-9;
    return seconds;
}
Quote this message in a reply
Member
Posts: 241
Joined: 2008.07
Post: #10
Yucky division.

Quote:timebase = timebaseInfo.numer / timebaseInfo.denom;

Thanks for the reply. I'll take a look at that.
Quote this message in a reply
Moderator
Posts: 3,579
Joined: 2003.06
Post: #11
The division is only done once per application launch, since timebase will only == 0 immediately after initialization (note that timebase is a static variable, which means it persists like a global variable would). So it's no problem in terms of performance, if that's what you mean.
Quote this message in a reply
Post Reply 

Possibly Related Threads...
Thread: Author Replies: Views: Last Post
  Back to timer/update issue Talyn 2 2,733 Oct 21, 2008 11:50 PM
Last Post: Talyn
  I need a timer Caveman 2 2,881 Aug 17, 2008 07:38 AM
Last Post: Caveman