对于对编码部分/开发人员的观点感兴趣的人来说,作为Journeyman Geek的答案(因为我的编辑遭到拒绝)的补充:
从程序员的角度来看,对于那些感兴趣的人来说,DOS时间是每个CPU滴答很重要的时间,所以程序员尽可能快地保存代码。
任何程序将以最大CPU速度运行的典型情况是这么简单(伪C):
int main()
{
while(true)
{
}
}
这将永远运行,现在,让我们把这段代码片段变成一个伪DOS游戏:
int main()
{
bool GameRunning = true;
while(GameRunning)
{
ProcessUserMouseAndKeyboardInput();
ProcessGamePhysics();
DrawGameOnScreen();
//close game
if(Pressed(KEY_ESCAPE))
{
GameRunning = false;
}
}
}
除非 DrawGameOnScreen
函数使用双缓冲/ V-sync(在DOS游戏制作的时候有点贵),游戏将以最高CPU速度运行。
在现代的移动i7上,每秒运行大约1,000,000到5,000,000次(取决于笔记本电脑的配置和当前的CPU使用情况)。
这意味着,如果我可以在我的64位窗口中使用现代CPU进行任何DOS游戏,我可以获得超过一千(1000!)FPS,如果物理处理“假设”它运行,则对于任何人来说都太快了在50-60 fps之间。
今天开发人员(可以)做的是:
- 在游戏中启用V-Sync(*不适用于窗口应用程序** [仅限全屏应用程序中提供的a.k.a.])
- 测量上次更新之间的时间差,并根据时间差更新物理,有效地使游戏/程序以相同的速度运行,而不管FPS速率如何
- 以编程方式限制帧速率
***取决于显卡/驱动程序/操作系统配置 可以 有可能。
对于第1点,我没有示例,因为它不是真正的“编程”。它只是使用图形功能。
至于第2点和第3点,我将显示相应的代码片段和解释:
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;
}
}
}
在这里你可以看到用户输入和物理考虑时差,但你仍然可以在屏幕上获得1000+ FPS,因为循环运行得尽可能快。因为物理引擎知道经过了多少时间,所以它不必依赖于“无假设”或“某个帧速率”,因此游戏将在任何cpu上以相同的速度工作。
3:
开发人员可以做的将帧速率限制为30 FPS实际上并不困难,只需看看:
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;
}
}
}
这里发生的是程序计算已经过了多少毫秒,如果达到一定量(33毫秒),那么它重绘游戏屏幕,有效地应用接近~30的帧速率。
此外,根据开发人员的不同,他/她可能会选择将所有处理限制为30 fps,并将上述代码略微修改为:
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;
}
}
}
}
还有一些其他的方法,其中一些我真的很讨厌。
例如,使用 sleep(<amount of milliseconds>)
。
我知道这是限制帧速率的一种方法,但是当游戏处理需要3毫秒或更长时间会发生什么?然后你执行睡眠......
这将导致比仅有的帧速率更低的帧率 sleep()
应该是造成的。
例如,我们假设睡眠时间为16毫秒。这将使程序运行在60赫兹。现在处理数据,输入,绘图和所有东西需要5毫秒。现在我们在一个循环中的时间为21毫秒,这导致略低于50赫兹,而你可能很容易仍然在60赫兹,但由于睡眠,这是不可能的。
一个解决方案是制作一个 适应性睡眠 以测量处理时间和从想要的睡眠中减去处理时间的形式,从而修复我们的“错误”:
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);
}
}
}