Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » Timers

Credits go to BrknPhoenix, CGamesPlay, and Kitty Cat for helping out!
This thread is locked; no one can reply to it. rss feed Print
Timers
Brian Simpson
Member #6,645
December 2005
avatar

Is there a possible way I can wait a certain number of seconds(Even down to the fractions if possible) so that it's the same speed on all computers? I think that if you used rest it would work, but I have to be precise with the timing with this program. I'm not very well versed in delta time or any of that stuff, so an explanation on how I can wait "real" seconds would be great. Thanks.

BrknPhoenix
Member #7,304
June 2006

volatile unsigned int    timer;

somewhere else...

void tick()
{
    timer++;
}
END_OF_FUNCTION (tick)

elsewhere...

    LOCK_VARIABLE(timer);
    LOCK_FUNCTION(tick);
    install_int(tick, 1);

timer increases 1 every millisecond. that's about it :p 1,000 ms to a second

        if (timer % 10 == 0)
        {
            Logic->DoLogic(scene);
        }

Do logic 100 times per second. Note that yours won't look exactly the same... I just ripped this right out of something I'm working on right now, hehe

Brian Simpson
Member #6,645
December 2005
avatar

Ok so now that I have the timer code, how can I like use "rest()" with this function? I want the gamae to use a for loop and at the end of the loop, wait 2 seconds(example) and then repeat the for loop. Is this possible?

CGamesPlay
Member #2,559
July 2002
avatar

Warning: You probably need to rethink your design! You should do something where an object has a counter counting up to 2 seconds, then goes into the loop state afterwards, where it sotres its iterator. Basically, instead of using rest, look into state-based programming.

--
Tomasu: Every time you read this: hugging!

Ryan Patterson - <http://cgamesplay.com/>

BrknPhoenix
Member #7,304
June 2006

Quote:

Ok so now that I have the timer code, how can I like use "rest()" with this function? I want the gamae to use a for loop and at the end of the loop, wait 2 seconds(example) and then repeat the for loop. Is this possible?

You don't want to use rest with it :) See the line that says:

if (timer % 10 == 0)

Since your timer is incrementing by 1 every millisecond, that event will occur every 10 milliseconds, which amounts to 100 times per second consistently through the duration of the program. If you are looking to just keep the program running the same speed on all systems as you said in your original post, just put your logic in that if block and you have done it :)

If you want to time in another way, then you need to store the end time and do...something... until you reach the end time.

So if you just wanted to pause two seconds, you could do something like:

eTime = time + 2000; // 2 seconds from now
while(time < eTime);

edit: whoops made a mistake

Brian Simpson
Member #6,645
December 2005
avatar

Damnit, I'm getting confused. So would something like this work?

//This is the main loop
while(!key[KEY_ESC])
{
 if(timer == 230)//.23 Seconds I think according to brknphoenix
 {
  doaction();
  timer = 0;
 }
}

Is that what you mean cgamesplay?

BrknPhoenix
Member #7,304
June 2006

That won't work because it'll reset your timer every .23 seconds... Just do timer % 10... Every tenth millisecond it will run your logic, so it will end up doing it 100 times per second. Let the number count up without resetting it, it can go up to like over 4 billion :)

edit: And note what the actual number is, is not important... the value of it, I mean. we're just using it to time with...

if (timer % 10 == 0)
{
    //do stuff
}

CGamesPlay
Member #2,559
July 2002
avatar

Here is an example, done in C because C++ requires too much indenation:

1// BallState variables
2 
3#define BS_FROZEN 1
4#define BS_BOUCING 2
5 
6struct ball
7{
8 int state;
9 int time;
10 int x, y;
11 int dx, dy;
12};
13 
14void upd_ball(ball* b)
15{
16 // This function adds to the ball's timer.
17 ++b->time;
18 // If the ball is frozen and 2 secodns has elapsed, start bouncing again.
19 if(b->state == BS_FROZEN && b->time >= 2000)
20 goto_ball_state(b, BS_BOUNCING);
21 else
22 { // Otherwise if the ball has been bouncing for 3 seconds, freeze it.
23 if(b->time >= 3000)
24 goto_ball_state(b, BS_FROZEN);
25 // Bounce the ball around the screen.
26 b->y += b->dy;
27 b->x += b->dx;
28 if(b->x > SCREEN_W || b->x < 0)
29 b->dx = -b->dx;
30 if(b->y > SCREEN_H || b->y < 0)
31 b->dy = -b->dy;
32 }
33}
34 
35void goto_ball_state(ball* b, int ns)
36{
37 // Set time in the new state to 0
38 b->time = 0;
39 // Set new state
40 b->state = ns;
41}

Now, 1000 times each second, call upd_ball on your ball and it will bounce around the screen for 3 seconds, then stop for 2, and repeat.

--
Tomasu: Every time you read this: hugging!

Ryan Patterson - <http://cgamesplay.com/>

Brian Simpson
Member #6,645
December 2005
avatar

Ahh I see, so I could do something like:

//This is the main loop
while(!key[KEY_ESC])
{
 if(timer % 230 == 0)//.23 Seconds I think according to brknphoenix
 {
  doaction();
 }
}

Possibly?

EDIT: Sorry CGamesPlay, I posted after you posted and didn't see that. Would my idea here still work?

BrknPhoenix
Member #7,304
June 2006

Nope, this:

//This is the main loop
while(!key[KEY_ESC])
{
 if(timer % 10 == 0)
 {
  //logic here
 }
 //drawing here
}

1) You can't reset the timer, else you'll never be able to time things up to even one second. You just have to let it count up for the duration of your program.

2) 230 milliseconds is too long. That means your logic is only updating 4 times per second, which is EXTREMELY slow. Your computer and most others should be able to do 100 times per second easy, so if you use the number 10 there, you will accomplish that :)

If you do it like that, it should run the same speed for everyone. Note that you shouldn't draw in there, you should draw after it. You want to draw as fast as possible :) Only the logic that comes before drawing anything goes in there

CGamesPlay
Member #2,559
July 2002
avatar

Well, for the main loop, the ideal solution is:

while(!key[KEY_ESC])
{
    while(timer > 0)
    {
        --timer;
        upd_ball(b);
    }
    // Draw everything outside of the timed loop.
    circlefill(screen, b->x, b->y, 32, -1);
}

[append]

Now what BrokenPhoenix is saying is also fine, but I prefer my OO approach instead.

--
Tomasu: Every time you read this: hugging!

Ryan Patterson - <http://cgamesplay.com/>

Brian Simpson
Member #6,645
December 2005
avatar

I don't think you guys are seeing what I'm trying to accomplish. This isn't to update the screen and such, this is to just simply do one action. I want to have an action occur every so-and-so seconds. I'm not trying to update the game logic every .230 seconds.

BrknPhoenix
Member #7,304
June 2006

That would be what I posted at the end of my second post in this topic.

If you want the value of 2 seconds from now, store the current value of the timer + 2000. If you wanted 10 seconds from now, store the current value of the timer + 10,000. Then do whatever you want to occur or pause whatever you want to pause using that time.

//wait for 2 seconds
eTime = timer + 2000;
while (timer <= eTime); //it will end the loop once the current time
                       //exceeds the target time of 2 seconds from when this started

CGamesPlay
Member #2,559
July 2002
avatar

You don't see what we just said. I will only speak for my method, but look:

Quote:

I want the gamae to use a for loop and at the end of the loop, wait 2 seconds(example) and then repeat the for loop.

In my example, inside the "for" loop you are bouncing the ball. And that for loop lasts for 3 seconds. Then the program pauses for 2 seconds, and continues. If you wanted to have something happen instantly, try the same thing, just differently:

1void upd_ball(ball* b)
2{
3 // This function adds to the ball's timer.
4 ++b->time;
5 // If the ball is frozen and 2 seconds has elapsed, start bouncing again.
6 if(b->state == BS_FROZEN && b->time >= 2000)
7 goto_ball_state(b, BS_BOUNCING);
8 else
9 { // Otherwise bounce the ball for 300 pixels
10 for(int i = 0; i < 300; i++)
11 {
12 // Bounce the ball around the screen.
13 b->y += b->dy;
14 b->x += b->dx;
15 if(b->x > SCREEN_W || b->x < 0)
16 b->dx = -b->dx;
17 if(b->y > SCREEN_H || b->y < 0)
18 b->dy = -b->dy;
19 }
20 // Pause again
21 goto_ball_state(b, BS_FROZEN);
22 }
23}

--
Tomasu: Every time you read this: hugging!

Ryan Patterson - <http://cgamesplay.com/>

Brian Simpson
Member #6,645
December 2005
avatar

So are you saying to put that in the main loop if I want it to occur every 2 seconds? I just see a problem with that because I thought that it would wait 2 seconds and NOTHING would occur. The thing is I want everything in the background to continue as normal but just have the action occur every 2 seconds. Will that while loop do what I described? I just thought that the if statement wouldn't do that.

BrknPhoenix
Member #7,304
June 2006

Quote:

So are you saying to put that in the main loop if I want it to occur every 2 seconds? I just see a problem with that because I thought that it would wait 2 seconds and NOTHING would occur. The thing is I want everything in the background to continue as normal but just have the action occur every 2 seconds. Will that while loop do what I described? I just thought that the if statement wouldn't do that.

//stuff happening all the time
if (timer % 2000 == 0)
{
  // stuff that happens every 2 seconds
}
//stuff happening all the time

Kitty Cat
Member #2,815
October 2002
avatar

if(timer % 10 == 0)
won't work properly because it relies on you being able to catch that exact millisecond. In general, timers are only accurate to 10ms or so (about .01 seconds, about 100 times a second). Allegro's timers are a bit worse than this due to overhead from all installed timers.

But, even if they really were accurate, say one time you check, timer is 9 (which means you won't do anything yet). The OS interrupts your game to run other threads. You get back and timer is now 22 (~13ms later). You just missed two executions, and won't make them up since timer%10 still doesn't == 0.

It's best to run the timer as fast as you want the game to update, and just do an update whenever the timer is > 0 (while decrementing timer every time you do an update).

--
"Do not meddle in the affairs of cats, for they are subtle and will pee on your computer." -- Bruce Graham

CGamesPlay
Member #2,559
July 2002
avatar

If, Brian, you were talking to me, I gave a main loop that calls that function in my post. My main loops works the same way as KittyCat's.

--
Tomasu: Every time you read this: hugging!

Ryan Patterson - <http://cgamesplay.com/>

Brian Simpson
Member #6,645
December 2005
avatar

Sorry CGamesPlay, I consistently miss what you're saying. If I used your method, would I have to install a timer for that variable and such? I'm really sorry if I seem stupid, I just don't know very much about this topic.

void function()
{
 timer++;
 if(timer >= 2000)
 {
  play_sample(...);
 }
}

Ok, now assuming I check that every millisecond, would that work to fit my needs? I'm trying to incorporate what you're all saying into what I make, but it's difficult because of my lack of experience.

CGamesPlay
Member #2,559
July 2002
avatar

No, you would use the same timer variable that BrknPhoenix said to use, except the main loop would look like mine. What I posted is pretty much the standard way of timing. What I would do in your case is this: (since all you need is a single event)

int sound_last_played = 0;
int game_time = 0; // This is the variable updated 1000 times each second by your timer interrupt.
while(!key[KEY_ESC])
{
    if(game_time - sound_last_played > 2000)
    {
        play_sample(...);
        sound_last_played = game_time;
    }
}

But do keep that state-based programing in mind for when you want to have multiple unrelated objects acting at the same time.

--
Tomasu: Every time you read this: hugging!

Ryan Patterson - <http://cgamesplay.com/>

Brian Simpson
Member #6,645
December 2005
avatar

Ok that makes sense. I'm going to have to use the state-based programming though because I have to update a pixel's location every so and so seconds as well. I guess I have enough knowledge to get it done and if I have any problems I should be able to figure it out. Thanks a lot everyone.

BrknPhoenix
Member #7,304
June 2006

haha, switching to the other timing method fixed the keyboard problem i was having in the other topic I made :p

Go to: