As an addition to Journeyman Geek's answer (because my edit got rejected) for the people who are interested in the coding part/developer perspective:
From the programmers perspective, for those who are interested, the DOS times were times where every CPU tick was important so programmers kept the code as fast as possible.
A typical scenario where any program will run at the max CPU speed is this simple (pseudo C):
int main()
{
while(true)
{
}
}
this will run forever, now, let's turn this code snippet into a pseudo-DOS-game:
int main()
{
bool GameRunning = true;
while(GameRunning)
{
ProcessUserMouseAndKeyboardInput();
ProcessGamePhysics();
DrawGameOnScreen();
//close game
if(Pressed(KEY_ESCAPE))
{
GameRunning = false;
}
}
}
unless the DrawGameOnScreen
functions uses double buffering/V-sync (which was kind of expensive in the days when DOS games were made), the game will run at maximum CPU speed.
On a modern day mobile i7 this would run at around 1,000,000 to 5,000,000 times per second (depending on the laptop configuration and current cpu usage).
This would mean that if I could get any DOS game working on my modern CPU in my 64bit windows I could get more than a thousand (1000!) FPS which is too fast for any human to play if the physics processing "assumes" it runs between 50-60 fps.
What current day developers (can) do is:
- Enable V-Sync in the game (*not available for windowed applications** [a.k.a. only available in full-screen apps])
- Measure the time difference between the last update and update the physics according to the time difference which effectively make the game/program run at the same speed regardless of the FPS rate
- Limit the framerate programmatically
*** depending on the graphics card/driver/os configuration it may be possible.
For point 1 there is no example I will show because it's not really any "programming". It's just using the graphics features.
As for point 2 and 3 I will show the corresponding code snippets and explanations:
2:
int main()
{
bool GameRunning = true;
long long LastTick = GetCurrentTime();
long long TimeDifference;
while(GameRunning)
{
TimeDifference = GetCurrentTime()-LastTick;
LastTick = GetCurrentTime();
//process movement based on how many time passed and which keys are pressed
ProcessUserMouseAndKeyboardInput(TimeDifference);
//pass the time difference to the physics engine so it can calculate anything time-based
ProcessGamePhysics(TimeDifference);
DrawGameOnScreen();
//close game if escape is pressed
if(Pressed(KEY_ESCAPE))
{
GameRunning = false;
}
}
}
Here you can see the user input and physics take the time difference into account, yet you could still get 1000+ FPS on screen because the loop is running as fast as possible. Because the physics engine knows how much time passed, it doesn't have to depend on "no assumptions" or "a certain framerate" so the game will work at the same speed on any cpu.
3:
What developers can do to limit the framerate to, for example, 30 FPS is actually nothing harder, just take a look:
int main()
{
bool GameRunning = true;
long long LastTick = GetCurrentTime();
long long TimeDifference;
double FPS_WE_WANT = 30;
//how many milliseconds need to pass before we need to draw again so we get the framerate we want?
double TimeToPassBeforeNextDraw = 1000.0/FPS_WE_WANT;
//For the geek programmers: note, this is pseudo code so I don't care for variable types and return types..
double LastDraw = GetCurrentTime();
while(GameRunning)
{
TimeDifference = GetCurrentTime()-LastTick;
LastTick = GetCurrentTime();
//process movement based on how many time passed and which keys are pressed
ProcessUserMouseAndKeyboardInput(TimeDifference);
//pass the time difference to the physics engine so it can calculate anything time-based
ProcessGamePhysics(TimeDifference);
//if certain amount of milliseconds pass...
if(LastTick-LastDraw >= TimeToPassBeforeNextDraw)
{
//draw our game
DrawGameOnScreen();
//and save when we last drawn the game
LastDraw = LastTick;
}
//close game if escape is pressed
if(Pressed(KEY_ESCAPE))
{
GameRunning = false;
}
}
}
What happens here is that the program counts how many milliseconds have passed, if a certain amount is reached (33 ms) then it redraws the game screen, effectively applying a frame rate near ~30.
Also, depending on the developer he/she may choose to limit ALL processing to 30 fps with the above code slightly modified to this:
int main()
{
bool GameRunning = true;
long long LastTick = GetCurrentTime();
long long TimeDifference;
double FPS_WE_WANT = 30;
//how many miliseconds need to pass before we need to draw again so we get the framerate we want?
double TimeToPassBeforeNextDraw = 1000.0/FPS_WE_WANT;
//For the geek programmers: note, this is pseudo code so I don't care for variable types and return types..
double LastDraw = GetCurrentTime();
while(GameRunning)
{
LastTick = GetCurrentTime();
TimeDifference = LastTick-LastDraw;
//if certain amount of miliseconds pass...
if(TimeDifference >= TimeToPassBeforeNextDraw)
{
//process movement based on how many time passed and which keys are pressed
ProcessUserMouseAndKeyboardInput(TimeDifference);
//pass the time difference to the physics engine so it can calculate anything time-based
ProcessGamePhysics(TimeDifference);
//draw our game
DrawGameOnScreen();
//and save when we last drawn the game
LastDraw = LastTick;
//close game if escape is pressed
if(Pressed(KEY_ESCAPE))
{
GameRunning = false;
}
}
}
}
There are a few other methods, and some of them I really do hate.
For example, using sleep(<amount of milliseconds>)
.
I know this is one method to limit the framerate, but what happens when your game processing takes 3 milliseconds or more? And then you execute the sleep...
this will result in a lower framerate than the one which only sleep()
should be causing.
Let's for example take a sleep time of 16 ms. this would make the program run at 60 hz. now the processing of the data, input, drawing and all the stuff takes 5 miliseconds. we are at 21 miliseconds for one loop now which results in slightly less than 50 hz, while you could easily still be at 60 hz but because of the sleep it's impossible.
One solution would be to make an adaptive sleep in the form of measuring the processing time and deducting the processing time from the wanted sleep resulting in fixing our "bug":
int main()
{
bool GameRunning = true;
long long LastTick = GetCurrentTime();
long long TimeDifference;
long long NeededSleep;
while(GameRunning)
{
TimeDifference = GetCurrentTime()-LastTick;
LastTick = GetCurrentTime();
//process movement based on how many time passed and which keys are pressed
ProcessUserMouseAndKeyboardInput(TimeDifference);
//pass the time difference to the physics engine so it can calculate anything time-based
ProcessGamePhysics(TimeDifference);
//draw our game
DrawGameOnScreen();
//close game if escape is pressed
if(Pressed(KEY_ESCAPE))
{
GameRunning = false;
}
NeededSleep = 33 - (GetCurrentTime()-LastTick);
if(NeededSleep > 0)
{
Sleep(NeededSleep);
}
}
}
FOR F IN 0 TO 1000; NEXT F;
– Macke Aug 12 '13 at 8:18