Friday, July 15, 2011

Predefined FPS (Frames Per Second) value for your game loop

In the last decades tons of inks have been spent in articles about how to achieve a constant value for FPS in a game loop.

But why add another headache in the game development process? Why to time it? Why not just leave things run as quick as the CPU can and forget about on-purpose delays and timing?

The main reason is that you have to make sure that the game has the same tempo independent of the power of the CPU and if possible to synchronize the game loop with the refresh rate of the display. Imagine that your hero is at the leftmost bound of the scene and as you hit the right arrow key then he is at the rightmost edge in no time.

Another reason it that once we know the FPS then this is the key for defining the step for x, y direction (and even z in a 3D environment). You know the width of your scene, you know in how much time your hero has to walk all this distance(completely up to you) as if his way was free of obstacles, and if you also know how many frames he walks in a second, then you are a simple division away from setting a correct step (…not exactly …but I will discuss it in another article)

Particularly nowadays where mobile gaming rules the galaxy it is most important to find a way to stabilize on-the-fly the FPS value because you don’t know in the creation time of the game in what hardware configuration the game is going to run. Of course if you write games for an a priori known hardware (as example, if you are an iPhone-developer) then you can experiment in the development time and find the correct values so to put them hardcoded in your source code.

Of course if the game logic is implemented in a terrible way then you have to take other medicine and return later in this tutorial.

Please read first the wrong way for achieving a constant FPS value: It says that...
...if you want to have in your game, let’s say, 25 frames per second (FPS) then you have to add a delay of 1000/25 = 40 milliseconds at the end of every game cycle.

This approach is absolutely wrong because it doesn’t take into consideration:
1) the execution time of one cycle of your game loop.
2) the time that the delay() or sleep() function needs to execute itself.

And, you may ask, what if alone the execution time covers completely the optimal value for the delay? Can you in this case ignore the delay(X milliseconds) function? No! A delay() or sleep() function must be there even with a minimum value in its argument so that the CPU can give time and resources to other threads as well(Input, Display, System threads, etc).

So, a first conclusion for what will follow is that the delay time in unknown in the initialization process and thus we have to keep a variable for this:


int iGameThreadDelay = 1;


and change this value on-the-fly according to the everytime calculated FPS value. You will decide for a new value every 1 second.
So, we introduce a variable called lTimer, initialized before going into the while(gameRunning) loop and in every cycle we check if the difference with the current time is greater or equal(the best) than 1000 milliseconds (= 1 second).
And this “1-second-has-passed” moment is the best opportunity to get the cycles performed in this interval (then, FPS=cycles). The variable lGameCycles is increase by 1 in every iteration of the game loop and we can compare this value with the desired one to decide if the delay time has to be increased or decreased. Of course, there must a check for this delay time not to take values less than 1.

Here is a game loop in pseudo language. You can easily find the libraries of the language you use to get the current time in milliseconds and for letting a thread go to sleep mode for some milliseconds.

final static int FPS = 30;
Timer = GetCurrentTimeInMillis();
// better, if you initialize it from a precalculated value
iGameThreadDelay = 1;

// will be set to zero every (almost) 1 sec
lGameCycles = 0;
while ( gameRunning == true )
{
lGameCycles++;
// ....
// Game logic here
// ....
if ( GetCurrentTimeInMillis() - lTimer >= 1000 )
{
if ( lGameCycles > FPS )
{
// if too many FPS, then increase the delay
iGameThreadDelay++;
}
else // ...decrease the delay ...
{
// ... making sure that the minumum value is 1
if ( iGameThreadDelay > 1 )
{
iGameThreadDelay--;
}
}
lGameCycles = 0;
lTimer = GetCurrentTimeInMillis();
}
delay ( iGameThreadDelay );
}


Inaccurate can occur when the difference of the current time and the saved one (in the previous ‘second’) is > 1000 milliseconds. This can be happened while in the k iteration of the loop the difference is let’s say 998 milliseconds and of course in this case the flow doesn’t enter the if (CurrentTimeMillis() - lTimer >= 1000 ) section and right after this in the k+1 iteration(the next) have already been spent 80ms for the execution of the game logic then the calculated difference will be 998 + 80 = 1078 milliseconds.


This value can be stored somewhere and be used in the next game session because – and I have to admit it – using this approach and starting the iGameThreadDelay from 1, it may take a few seconds for the game mechanism to calculate the best value and some players may experience bizarre movements in these few seconds. Thus, by storing it somewhere, it will happened only for the very first time the game is start.

Exactly the same logic can be applied on platforms where a time scheduler executes periodically a callback function (like iPhone & Android).

No comments:

Post a Comment