Timers and high speed animation - need advice!
James Bunting

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?

Michael Jensen

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.

Audric
Quote:

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.

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.

Dustin Dettmer
 1 #ifndef TIMER_H 2 #define TIMER_H 3 4 #ifndef WIN32 5 #include 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 6 #else 7 #include 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.

Heres some usage:

```...
Timer::time_t t = timer.usecs();

...

cout << timer.usecs() - t << " Microseconds have passed.\n";
```

Wilson Saunders

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 }

BAF

Just some simple math is needed. Here is my (probably flawed somehow) code from a TINS a few years ago:

anim.h:

1// baf 2005 2 3// anim.h 4// animation stuff 5 6#ifndef ANIM_H 7#define ANIM_H 8 9#include "main.h" 10#include "timers.h" 11 12typedef struct ABITMAP 13{ 14 int num_frames, fps, w, h, masked; 15 BITMAP **frames; 16} ABITMAP; 17 18typedef struct ABITMAP_INSTANCE 19{ 20 ABITMAP *parent; 21 int curframe; 22} ABITMAP_INSTANCE; 23 24ABITMAP *load_abitmap(const char *filename); 25int save_abitmap(const char *filename, ABITMAP *bmp); 26ABITMAP_INSTANCE *grab_abitmap_instance(ABITMAP *bmp); 27 28void destroy_abitmap(ABITMAP *bmp); 29void destroy_abitmap_instance(ABITMAP_INSTANCE *bmp); 30 31void update_animation(ABITMAP_INSTANCE *bmp); 32void ablit(ABITMAP_INSTANCE *source, BITMAP *dest, int dest_x, int dest_y); 33void ablit_r(ABITMAP_INSTANCE *source, BITMAP *dest, int dest_x, int dest_y, fixed angle); 34 35#endif

anim.c:

1// baf 2005 2 3// anim.c 4// animation stuff 5 6#include "anim.h" 7 8int lac = 0; 9 10ABITMAP *load_abitmap(const char *filename) 11{ 12 PACKFILE *abmp; 13 ABITMAP *ret; 14 int i, x, y; 15 abmp = pack_fopen(filename, "r"); 16 if(!abmp) 17 return NULL; 18 ret = (ABITMAP *)malloc(sizeof(ABITMAP)); 19 ret->num_frames = pack_igetl(abmp); 20 ret->fps = pack_igetl(abmp); 21 ret->w = pack_igetl(abmp); 22 ret->h = pack_igetl(abmp); 23 ret->masked = pack_igetl(abmp); 24 ret->frames = (BITMAP **)malloc(sizeof(BITMAP *) * ret->num_frames); 25 for(i = 0; i < ret->num_frames; ++i) 26 { 27 abmp = pack_fopen_chunk(abmp, 1); 28 ret->frames<i> = create_bitmap(ret->w, ret->h); 29 for(y = 0; y < ret->h; ++y) 30 for(x = 0; x < ret->w; ++x) 31 putpixel(ret->frames<i>, x, y, makecol(pack_igetl(abmp), pack_igetl(abmp), pack_igetl(abmp))); 32 abmp = pack_fclose_chunk(abmp); 33 } 34 pack_fclose(abmp); 35 return ret; 36} 37 38int save_abitmap(const char *filename, ABITMAP *bmp) 39{ 40 PACKFILE *abmp; 41 int i, x, y, col; 42 abmp = pack_fopen(filename, "w"); 43 if(!abmp) 44 return -1; 45 pack_iputl(bmp->num_frames, abmp); 46 pack_iputl(bmp->fps, abmp); 47 pack_iputl(bmp->w, abmp); 48 pack_iputl(bmp->h, abmp); 49 pack_iputl(bmp->masked, abmp); 50 for(i = 0; i < bmp->num_frames; ++i) 51 { 52 abmp = pack_fopen_chunk(abmp, 1); 53 for(y = 0; y < bmp->h; ++y) 54 { 55 for(x = 0; x < bmp->w; ++x) 56 { 57 col = getpixel(bmp->frames<i>, x, y); 58 pack_iputl(getr(col), abmp); 59 pack_iputl(getg(col), abmp); 60 pack_iputl(getb(col), abmp); 61 } 62 } 63 abmp = pack_fclose_chunk(abmp); 64 } 65 pack_fclose(abmp); 66} 67 68ABITMAP_INSTANCE *grab_abitmap_instance(ABITMAP *bmp) 69{ 70 ABITMAP_INSTANCE *ret; 71 ret = (ABITMAP_INSTANCE *)malloc(sizeof(ABITMAP_INSTANCE)); 72 ret->parent = bmp; 73 ret->curframe = 0; 74 return ret; 75} 76 77 78void destroy_abitmap(ABITMAP *bmp) 79{ 80 int i; 81 for(i = 0; i < bmp->num_frames; ++i) 82 destroy_bitmap(bmp->frames<i>); 83 free(bmp->frames); 84 free(bmp); 85} 86 87void destroy_abitmap_instance(ABITMAP_INSTANCE *bmp) 88{ 89 free(bmp); 90} 91 92 93void update_animation(ABITMAP_INSTANCE *bmp) 94{ 95 BASSERT(bmp); 96 97 /* if(anim_counter == lac) 98 return; 99 lac = anim_counter;*/ 100 101 // anim_counter goes up by 60 each second... we gotta calculate this with frames per second. 102 // update. the curframe. 103 if(anim_counter % (60 / bmp->parent->fps) == 0) 104 ++bmp->curframe; 105 106 // we dont want segfaults ;) 107 if(bmp->curframe >= bmp->parent->num_frames) 108 bmp->curframe %= bmp->parent->num_frames; 109 110 // keep it from overflowing 111 if(anim_counter >= 60) 112 anim_counter -= 60; 113} 114 115void ablit(ABITMAP_INSTANCE *source, BITMAP *dest, int dest_x, int dest_y) 116{ 117 BASSERT(source); 118 BASSERT(source->parent); 119 BASSERT(source->parent->frames); 120 //printf("%d ", source->curframe); 121 if(source->parent->masked) 122 masked_blit(source->parent->frames[source->curframe], dest, 0, 0, dest_x, dest_y, source->parent->w, source->parent->h); 123 else 124 blit(source->parent->frames[source->curframe], dest, 0, 0, dest_x, dest_y, source->parent->w, source->parent->h); 125} 126 127void ablit_r(ABITMAP_INSTANCE *source, BITMAP *dest, int dest_x, int dest_y, fixed angle) 128{ 129 rotate_sprite(dest, source->parent->frames[source->curframe], dest_x, dest_y, angle); 130}

And here is the abitmap utility: