Game speed
Don Freeman

Ok...I've read the FAQ and some examples of how to do this...but I still can't find anything that shows what I need.

I want to be able to set a 'speed limit', if you will, for fast computers. I am going to design the logic / render loops so they are optimized for the target computer...but I don't want the faster computers to run faster than the target. In the unused time slice, I would like to yeild to the OS.

So, my question is...how do I implement this into my game?
Also...if the interrupt doesn't change the value of a variable, does it still need to be volatile? I wouldn't think so, since the interrupt doesn't change it, but I want to be correct on this.

I would like something like:

1//////////////////////////////////////////////////////////////////////////////////////////////////
2#include <time.h>
3#include <allegro.h>
4//////////////////////////////////////////////////////////////////////////////////////////////////
5volatile int iFPS = 0; // Actual frames per second.
6int iTargetFPS = 20; // the FPS speed limit.
7int iFPSTimerSpeed = 2; // update the FPS counter this fast.
8int iFPSCounter = 0;
9//////////////////////////////////////////////////////////////////////////////////////////////////
10void FPSTimer( void )
11{
12 iFPS = iFPSCounter * iFPSTimerSpeed;
13 iFPSCounter = 0;
14}
15END_OF_FUNCTION(FPSTimer)
16//////////////////////////////////////////////////////////////////////////////////////////////////
17void InitSYS( void )
18{
19 allegro_init();
20 set_color_depth(32);
21 set_gfx_mode(GFX_DIRECTX_WIN,400,400,0,0);
22 set_color_conversion(COLORCONV_TOTAL);
23 install_timer();
24 install_keyboard();
25 install_mouse();
26} // I know...I should check for return values....I don't care right now.
27//////////////////////////////////////////////////////////////////////////////////////////////////
28int main( void )
29{
30 InitSYS();
31 LOCK_VARIABLE(iFPS);
32 LOCK_FUNCTION(FPSCounter);
33 install_int_ex(FPSTimer,BPS_TO_TIMER(iFPSTimerSpeed));
34 while ( true )
35 {
36 if ( keypressed() )
37 {
38 if ( key[KEY_ESC] )
39 {
40 clear_keybuf();
41 break;
42 }
43 }
44 iFPSCounter++;
45 if ( iFPS >= iTargetFPS )
46 {
47 // Do logic / rendering stuff.
48 }
49 else
50 {
51 // yield to system.
52 }
53 textprintf(screen,font,0,0,makecol(255,255,255),"FPS: %i", iFPS);
54 }
55 return 0;
56}
57END_OF_MAIN()
58//////////////////////////////////////////////////////////////////////////////////////////////////

Edit...corrected the code.

Rick

Normally:

1voaltile int iLogic;
2void Timer( void )
3{
4 iLogic++;
5}
6END_OF_FUNCTION(FPSTimer)
7 
8//main loop
9bool bRanLogic=false;
10iLogicCounter = 0;
11while(iLogic > 0 && iLogicCounter < 5)
12{
13 logic();
14 iLogicCounter++;
15 iLogic--;
16 bRanLogic = true;
17}
18if(bRanLogic)
19 draw(); //only need to draw if the logic ran
20else
21 //some sort of yeild, to free up OS, I normally skip this

On very fast computers the logic wouldn't run more than once each time it enters the while loop, which would be the timers time 60 BPS if the norm I think, but on slow computers it could get into an infinit loop. That is why we have iLogicCounter. If we get into a situation where logic() runs for a long time and iLogic keeps getting ++ before it hits iLogic--, then the iLogicCounter will make sure we only do this 5 times before breaking out and showing to the user what we did.

Elverion

Heres a "better" main loop to consider... It uses the timer (speed_counter is the variable for that), and avoids some wasted processing for faster computers. It could still use some minor tweeks, though (Check Rick's post, for example).

1 Init();
2 
3 // other initialization crap
4 
5 while( !quit )
6 {
7 if (key[KEY_ESC]) {
8 quit=true; }
9
10 bool bLogicUpdate = false;
11
12 while(speed_counter <= 0) {
13 rest(1); }
14
15 while(speed_counter > 0)
16 { // logic "step"
17 // accept input
18 // do some calculations
19 
20 bLogicUpdate = true;
21 --speed_counter;
22 }
23
24 // redraw
25 if( bLogicUpdate == true ) {
26 ScreenSystem->draw_flip(); // <-- this blits to the screen...just my way of doing it.
27 fps++; } // <-- this is a variable i use to keep track of FPS. ignore it.
28 yield_timeslice();
29 }
30
31 return 0;

Kitty Cat

Elverion, your bLogicUpdate variable is rather useless there. Since you wait with rest(1) until you have logic to do, bLogicUpdate will always be true before it gets checked. As well, yield_timeslice is unneeded since you rest when you're caught up (and if you yield when you're not caught up, you're just wasting time and causing lost frames).

Tobias Dammers

I don't know about the rest of you huys, but I haven't managed to produce smooth animation using allegro timers yet, unless I lock in on the physical screen refresh rate (or a multiple of that).
QueryPerformanceCounter() or gettimeofday() are the way to go, they're cheap, accurate and easy to code. Only fall back on allegro timers if you have to.

Thread #567996. Printed from Allegro.cc