[英]SDL_Delay messing with SDL_GetTicks
我正在使用 SDL 开发一个简单的节奏游戏,但我一直在跟踪游戏中的时间(滴答声)。 为了让自己的生活更简单,我想将 FPS 限制在 60,但是使用 SDL_Delay() 会影响 SDL_GetTicks(),这很奇怪。 SDL_GetTicks() function 不应该返回初始化的时间(包括在 SDL_Delay 中花费的时间)吗? 我试图用 std::chrono 做一些事情,但我无法理解计算时间。 我有一个想法用线程做一些事情,但我觉得有一种更简单的方法。
不确定这是否是您要寻找的,但我认为值得一试。
由于实际时间和游戏时间之间通常不存在 1:1 的对应关系,因此有必要实现一个考虑游戏经过时间的“机制”。
在处理帧率时我更喜欢实现的方法是以下之一:
假设您希望将帧速率限制为 60FPS。 这意味着您的游戏必须每 1000 毫秒(1 秒)的 60 次更新一次。
为了实现这一点(并尝试将 FPS 锁定在一个近似固定的值),您可以测试渲染前一帧需要多长时间并相应地调整传递给SDL_Delay()
的值。 此外,您可以使用累加器来获得更接近 60 FPS(而不是 62)的速度。
下面是一个用 C 编写的示例,可以很容易地移植到 C++ 中。
remainder
是累加器
static void capFrameRate(long *then, float *remainder)
{
long wait, frameTime;
wait = 16 + *remainder; // 16 is the integer part of 1000/60
*remainder -= (int)*remainder;
frameTime = SDL_GetTicks() - *then; // time it took the previous frame to render
wait -= frameTime;
if (wait < 1)
{
wait = 1;
}
SDL_Delay(wait);
*remainder += 0.667; // 0.667 is the fractional part of 1000/60
*then = SDL_GetTicks();
}
主要的:
int main(int argc, char** argv)
{
long then;
float remainder;
while (1)
{
// clear renderer
// get input
// update game logic
// render frame
capFramerate(&then, &remainder);
}
return 0;
}
您可以跟踪自上一帧以来经过的游戏时间(即delta time )。
例如,如果您使用object.x += 5;
,object 的运动将取决于当前的帧率(由于许多通常不可预测的因素,可能会有所不同); 但是,如果我们使用增量时间,我们可以每秒更新 object position 一些像素: object.x += 5 * deltaTime;
,而 object 将始终每秒移动 5 个像素,即使帧速率上升到一个非常大的值。
帧限制:使用增量时间,您无需再延迟游戏。 但是,允许游戏以系统允许的任何帧速率运行可能会导致几个问题。 最简单的解决方案是实现帧限制机制,强制游戏循环等待目标增量时间过去。
例如,如果目标是 60 FPS,我们希望目标时间为每帧 16.667 毫秒(1000/60)。 然后我们可以使用SDL_TICKS_PASSED()
宏来实现: while (,SDL_TICKS_PASSED(SDL_GetTicks(); mTicksCount + 16));
.
最后一件事,注意增量时间太长可能很有用,因为游戏可能会暂停(例如,在调试和单步执行断点时)。 要解决此问题,您可以将增量时间限制为最大值(例如 0.05f),因此游戏模拟在任何一帧都不会向前跳得太远。
这是一个实现它的例子。
游戏 class:
class Game
{
public:
Game();
void RunLoop();
private:
// Stuff (window, renderer, ...)
float deltaTime; // Delta time
Uint32 mTicksCount; // Number of ticks since start of game
// get input
UpdateDeltaTime(); // Update delta time
UpdateGameLogicExample(); // Update game objects as function of delta time
// render frame
};
更新时间差的方法:
void Game::UpdateDeltaTime()
{
// Frame limiting: wait until 16ms has elapsed since last frame
while (!SDL_TICKS_PASSED(SDL_GetTicks(), mTicksCount + 16));
// Delta time is the difference in ticks from last frame
// (converted to seconds)
deltaTime = (SDL_GetTicks() - mTicksCount) / 1000.0f;
// Clamp maximum delta time value
if (deltaTime > 0.05f)
{
deltaTime = 0.05f;
}
// Update tick counts (for next frame)
mTicksCount = SDL_GetTicks();
}
void Game::UpdateGameLogicExample()
{
gameObj.x += 5 * deltaTime; // Each second the game object will move by 5 pixel, independantly from the processor frequency or other external factors
}
游戏循环:
void Game::RunLoop()
{
while (true)
{
// clear renderer
// get input
UpdateDeltaTime();
UpdateGameLogicExample();
// render frame
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.