I am trying to understand the Allegro timer/interrupt code in a little more detail as I need a high precision timer system for high speed animations. I need to animate loads of things at all kinds of speeds but using just one interrupt.
In a nutshell to get the full range of intervals I need (in this case to animate stuff with a range from 1 to 60 times per sec) I need to install a timer that fires every 120 hunds/sec. I figure this because it allows me to move things at >30 intervals (say 55 or 49 etc). To not double the timer range to 120 (as in run it at 60 ticks) means that there is no difference between moving 31 and 59 times per sec as the move interval will not divide into the ticks.
Anyway putting all that stuff aside (which can clearly be a real head bender) my questions are...
1. Am I missing the point totally and looking at things the wrong way? Is there another way of getting stuff to animate at anything from 1 to 59 or even say 47 times per sec?
2. If my game slows down to < 120 FPS (very likely at times) would the timer interrupt not fire on time. So it it seperate from the game code like a "proper hardware" interrupt under DOS?
3. Is 120 ticks per sec considered too fast for an Allegro interrupt? Not the norm surely?
I think the highest you can really depend on is about 100 ticks a second -- the timers arn't much more accurate than 10ms... (I don't think even the Windows OS timers are for that matter)...
At any rate, if you're going to max out at 60 animations per second, then the most you'll ever need to call your timer is 60 times a second...
Basically, in each of your objects have a constant variable that says how many ticks to skip per frame of animation, and another variable to count. (For instance if you want to animate an object at 60 fps, you would never skip any frames and the constant would be 1, if you wanted 30 fps for another object, you would set it to 2, 45 fps would be 1.33, 15 fps would be 4 -- I think the formula is something like constant_skip_var = (1/(fps/60) )
every tick you would add one to the counter, and every time the counter was equal to or greater than the constant you would call your animate (usually just incrase the frame counter) and subtract the constant value from the counter value.
--- that method has some obvious flaws though.
1. Am I missing the point totally and looking at things the wrong way? Is there another way of getting stuff to animate at anything from 1 to 59 or even say 47 times per sec?
You should be able to do everything with a single-speed timer, for example 100Hz.
Animate means either or all of the following:
a) take a "logical step" in the unit's AI: examine the situation and make decisions accordingly.
b) modify the coordinates : this is the movement.
c) modifiy the appearance. sprite, color, blinking effect on/off, angle (heading)
For b), you can use coordinates and speed with a precision higher than pixel "integers". So you can precisely map any speed and position: A constant +0.35 speed will result in a movement of 35 pixels per second.
a) and c) usually can be taken as discrete steps, so they don't need sub-step precision.
1 | #ifndef TIMER_H |
2 | #define TIMER_H |
3 | |
4 | #ifndef WIN32 |
5 | #include <sys/time.h> |
6 | #endif |
7 | |
8 | extern class Timer { |
9 | public: |
10 | |
11 | typedef unsigned int time_t; |
12 | |
13 | private: |
14 | |
15 | #ifdef WIN32 |
16 | unsigned long long startTime; |
17 | unsigned long long currentTime; |
18 | #else |
19 | timeval startTime; |
20 | timeval currentTime; |
21 | #endif |
22 | |
23 | public: |
24 | |
25 | Timer(); |
26 | |
27 | time_t usecs(); |
28 | time_t msecs(); |
29 | time_t secs(); |
30 | |
31 | }timer; |
32 | |
33 | #endif |
1 | #include "timer.h" |
2 | #include "log.h" |
3 | |
4 | #ifdef WIN32 |
5 | #include <windows.h> |
6 | #else |
7 | #include <sys/time.h> |
8 | #endif |
9 | |
10 | Timer timer; |
11 | |
12 | Timer::Timer() |
13 | { |
14 | #ifdef WIN32 |
15 | startTime = GetTickCount(); |
16 | #else |
17 | gettimeofday(&startTime, 0); |
18 | #endif |
19 | } |
20 | |
21 | Timer::time_t Timer::usecs() |
22 | { |
23 | #ifdef WIN32 |
24 | static bool onetime = 0; |
25 | static unsigned long long freq = 0; |
26 | |
27 | if(!onetime) { |
28 | |
29 | onetime = true; |
30 | QueryPerformanceFrequency((LARGE_INTEGER*)&freq); |
31 | } |
32 | |
33 | QueryPerformanceCounter((LARGE_INTEGER*)¤tTime); |
34 | |
35 | currentTime *= 1000000; |
36 | currentTime /= freq; |
37 | |
38 | return currentTime - startTime * 1000; |
39 | #else |
40 | gettimeofday(¤tTime, 0); |
41 | |
42 | return (unsigned long)((currentTime.tv_sec - startTime.tv_sec) / 1000) |
43 | + (unsigned long)((currentTime.tv_usec - startTime.tv_usec)); |
44 | #endif |
45 | } |
46 | |
47 | Timer::time_t Timer::msecs() |
48 | { |
49 | #ifdef WIN32 |
50 | currentTime = GetTickCount(); |
51 | |
52 | return currentTime - startTime; |
53 | #else |
54 | gettimeofday(¤tTime, 0); |
55 | |
56 | return (unsigned long)((currentTime.tv_sec - startTime.tv_sec) * 1000) |
57 | + (unsigned long)((currentTime.tv_usec - startTime.tv_usec) / 1000); |
58 | #endif |
59 | } |
60 | |
61 | Timer::time_t Timer::secs() |
62 | { |
63 | #ifdef WIN32 |
64 | currentTime = GetTickCount(); |
65 | |
66 | return (currentTime - startTime) / 1000; |
67 | #else |
68 | gettimeofday(¤tTime, 0); |
69 | |
70 | return (unsigned long)(currentTime.tv_sec - startTime.tv_sec); |
71 | #endif |
72 | } |
73 | #endif |
There are 1000 milliseconds(msecs) in a second, and 1000 usecs in a millisecond.
Tested on windows and linux and usec precision appears to be pretty consistent.
[edit]
Heres some usage:
... Timer::time_t t = timer.usecs(); ... cout << timer.usecs() - t << " Microseconds have passed.\n";
If some of your animations are running too fast you should have a frame display for multiple updates.
So if your code looks like this:
1 | void Update(){ |
2 | anim ++; |
3 | if( anim >= 3) anim = 0; |
4 | } |
5 | |
6 | void draw(){ |
7 | switch(anim){ |
8 | case 0: |
9 | draw_frame0(); |
10 | break; |
11 | case 1: |
12 | draw_frame1(); |
13 | break; |
14 | case 2: |
15 | draw_frame2(); |
16 | break; |
17 | } |
You can make it run at half speed by changing it to look like this:
1 | void Update(){ |
2 | anim ++; |
3 | if( anim >= 6) anim = 0; |
4 | } |
5 | |
6 | void draw(){ |
7 | switch(anim){ |
8 | case 0: |
9 | case 1: |
10 | draw_frame0(); |
11 | break; |
12 | case 2: |
13 | case 3: |
14 | draw_frame1(); |
15 | break; |
16 | case 4: |
17 | case 5: |
18 | draw_frame2(); |
19 | break; |
20 | } |
Just some simple math is needed. Here is my (probably flawed somehow) code from a TINS a few years ago:
anim.h:
anim.c:
And here is the abitmap utility: