C++ Jittery 游戏循环——如何让它尽可能流畅?
C++ Jittery game loop - how to make it as smooth as possible?
这是我的游戏循环代码:
while (shouldUpdate)
{
timeSinceLastUpdate = 0;
startTime = clock();
while (timeAccumulator >= timeDelta)
{
listener.handle();
em.update();
timeAccumulator -= timeDelta;
timeSinceLastUpdate += timeDelta;
}
rm.beginRender();
_x->draw();
rm.endRender();
timeAccumulator += clock() - startTime;
}
它运行得几乎完美,但它有一些抖动,每秒几次而不是_x(它在更新中所做的所有测试实体是x++)向右移动1个像素,它实际上移动了2个像素向右,这是一个明显的 lag/jitter 效果。我猜 clock() 不够准确。那么我可以做些什么来改进这个游戏循环呢?
如果重要,我会使用 SDL 和 SDL_image。
编辑:在做了比时钟更准确的事情之后没有任何改变。但是,我发现,这一切都要归功于 timeDelta。这就是我做这个 post:
时定义 timeDelta 的方式
double timeDelta = 1000/60;
但是当我把它定义为别的东西时,我在胡闹...
double timeDelta = 16.666666;
我注意到游戏开始的前几秒非常流畅。但就在几秒钟后,游戏卡顿得很厉害,然后又恢复流畅,如此反复。我添加的 6(或 .really 之后的任何内容)越多,游戏最初流畅的时间就越长,并且在进行时遇到的滞后就越严重。似乎浮动错误正在攻击。那我该怎么办呢?
EDIT2:我已经尝试了很多东西,现在它甚至都不好笑...有人可以帮我解决循环的数学部分吗?因为这就是造成这种情况的原因...
EDIT3:我向一些人发送了一个测试程序,一些人说它非常流畅,而另一些人则说它像我描述的那样紧张。对于任何愿意在这里测试它的人来说,它是(src):https://www.mediafire.com/?vfpy4phkdj97q9j
EDIT4:我将 link 更改为源代码。
几乎可以肯定是因为 clock()
的准确性
使用 std::chrono
或 SDL_GetTicks()
来测量自纪元以来的时间。
我推荐使用 std::chrono
只是因为我更喜欢 C++ api,这里有一个例子:
int main(){
using clock = std::chrono::high_resolution_clock;
using milliseconds = std::chrono::milliseconds;
using std::chrono::duration_cast;
auto start = clock::now(), end = clock::now();
uint64_t diff;
while(running){
diff = duration_cast<milliseconds>(end - start).count();
start = clock::now();
// do time difference related things
end = clock::now();
}
}
要仅在指定的增量后更新,您可以像这样执行循环:
int main(){
auto start = clock::now(), end = clock::now();
uint64_t diff = duration_cast<milliseconds>(end - start).count();
auto accum_start = clock::now();
while(running){
start = clock::now();
diff = duration_cast<milliseconds>(end - start).count();
if(duration_cast<nanoseconds>(clock::now() - accum_start).count() >= 16666666){
// do render updates every 60th of a second
accum_start = clock::now();
}
end = clock::now();
}
}
start
和 end
都属于 std::chrono::time_point<clock>
类型,其中 clock
之前我们定义为 std::chrono::high_resolution_clock
.
2 time_point
之间的差异将是 std::chrono::duration
,可以是纳秒、毫秒或您喜欢的任何其他单位。我们将其转换为毫秒,然后将 count()
分配给我们的 uint64_t
。如果您愿意,可以使用其他整数类型。
我的 diff
是你应该如何计算你的 timeDelta
。您应该 而不是 将其设置为常量。即使你确定它是正确的,你也错了。每一帧都会有不同的增量,即使它是一秒的最小分数。
如果要设置恒定的帧差,请使用SDL_GL_SetSwapInterval设置垂直同步。
编辑
为了您,我在 main.cpp
中创建了 this example git repo. 通知,我在其中乘以 diff
以获得每帧的调整差异。这种调整(或缺乏)是您感到不安的地方。
这是我的游戏循环代码:
while (shouldUpdate)
{
timeSinceLastUpdate = 0;
startTime = clock();
while (timeAccumulator >= timeDelta)
{
listener.handle();
em.update();
timeAccumulator -= timeDelta;
timeSinceLastUpdate += timeDelta;
}
rm.beginRender();
_x->draw();
rm.endRender();
timeAccumulator += clock() - startTime;
}
它运行得几乎完美,但它有一些抖动,每秒几次而不是_x(它在更新中所做的所有测试实体是x++)向右移动1个像素,它实际上移动了2个像素向右,这是一个明显的 lag/jitter 效果。我猜 clock() 不够准确。那么我可以做些什么来改进这个游戏循环呢?
如果重要,我会使用 SDL 和 SDL_image。
编辑:在做了比时钟更准确的事情之后没有任何改变。但是,我发现,这一切都要归功于 timeDelta。这就是我做这个 post:
时定义 timeDelta 的方式double timeDelta = 1000/60;
但是当我把它定义为别的东西时,我在胡闹...
double timeDelta = 16.666666;
我注意到游戏开始的前几秒非常流畅。但就在几秒钟后,游戏卡顿得很厉害,然后又恢复流畅,如此反复。我添加的 6(或 .really 之后的任何内容)越多,游戏最初流畅的时间就越长,并且在进行时遇到的滞后就越严重。似乎浮动错误正在攻击。那我该怎么办呢?
EDIT2:我已经尝试了很多东西,现在它甚至都不好笑...有人可以帮我解决循环的数学部分吗?因为这就是造成这种情况的原因...
EDIT3:我向一些人发送了一个测试程序,一些人说它非常流畅,而另一些人则说它像我描述的那样紧张。对于任何愿意在这里测试它的人来说,它是(src):https://www.mediafire.com/?vfpy4phkdj97q9j
EDIT4:我将 link 更改为源代码。
几乎可以肯定是因为 clock()
使用 std::chrono
或 SDL_GetTicks()
来测量自纪元以来的时间。
我推荐使用 std::chrono
只是因为我更喜欢 C++ api,这里有一个例子:
int main(){
using clock = std::chrono::high_resolution_clock;
using milliseconds = std::chrono::milliseconds;
using std::chrono::duration_cast;
auto start = clock::now(), end = clock::now();
uint64_t diff;
while(running){
diff = duration_cast<milliseconds>(end - start).count();
start = clock::now();
// do time difference related things
end = clock::now();
}
}
要仅在指定的增量后更新,您可以像这样执行循环:
int main(){
auto start = clock::now(), end = clock::now();
uint64_t diff = duration_cast<milliseconds>(end - start).count();
auto accum_start = clock::now();
while(running){
start = clock::now();
diff = duration_cast<milliseconds>(end - start).count();
if(duration_cast<nanoseconds>(clock::now() - accum_start).count() >= 16666666){
// do render updates every 60th of a second
accum_start = clock::now();
}
end = clock::now();
}
}
start
和 end
都属于 std::chrono::time_point<clock>
类型,其中 clock
之前我们定义为 std::chrono::high_resolution_clock
.
2 time_point
之间的差异将是 std::chrono::duration
,可以是纳秒、毫秒或您喜欢的任何其他单位。我们将其转换为毫秒,然后将 count()
分配给我们的 uint64_t
。如果您愿意,可以使用其他整数类型。
我的 diff
是你应该如何计算你的 timeDelta
。您应该 而不是 将其设置为常量。即使你确定它是正确的,你也错了。每一帧都会有不同的增量,即使它是一秒的最小分数。
如果要设置恒定的帧差,请使用SDL_GL_SetSwapInterval设置垂直同步。
编辑
为了您,我在 main.cpp
中创建了 this example git repo. 通知,我在其中乘以 diff
以获得每帧的调整差异。这种调整(或缺乏)是您感到不安的地方。