我对带有增量时间和插值的固定时间步长的解决方案是错误的吗?
Is my solution to fixed timestep with delta time and interpolation wrong?
我正在尝试使用 用于物理和插值的固定增量时间 在渲染状态之前编写简单循环。我在固定时间步长的游戏教程中使用 Gaffer,我试图理解它并使其发挥作用。
float timeStep = 0.01;
float alpha = 1.0;
while (isOpen()) {
processInput();
deltaTime = clock.restart(); // get elapsed time
if (deltaTime > 0.25) { deltaTime = 0.25; } // drop frame guard
accumulator += deltaTime;
while (accumulator >= timeStep) {
// spritePosBefore = sprite.getPosition();
accumulator -= timeStep;
// sprite.move(velocity * timeStep, 0);
// spritePosAfter = sprite.getPosition();
}
if (accumulator > timeStep) { alpha = accumulator / timeStep; } else { alpha = 1.0; }
// sprite.setPosition(Vector2f(spritePosBefore * (1 - alpha) + spritePosAfter * alpha));
clear();
draw(sprite);
display();
}
现在,一切看起来都很好。我有固定的物理时间步长,在物理更新后尽可能绘制并在两个位置之间进行插值。它应该可以完美运行,但我仍然可以看到精灵卡顿,甚至偶尔会后退一个像素。为什么会这样?我的代码有问题吗?我花了两天时间试图了解游戏循环,这将确保我的动作完美无缺,但它似乎并没有像我想象的那样工作。知道可以改进什么吗?
我不认为你这样做的方式一定是错误的,但它看起来有点过于复杂。我不明白你到底想做什么,所以我只想分享我在我的 SFML 应用程序中实现“固定时间步长”的方式。
以下是最简单的方法,对于大多数应用程序来说“足够好”。虽然它不是最精确的(测量时间和实际时间之间可能会有一点误差):
sf::Clock clock;
sf::Event event;
while (window_.isOpen()) {
while (window_.pollEvent(event)) {}
if (clock.getElapsedTime().asSeconds() > FLT_FIXED_TIME_STEP) {
clock.restart();
update(FLT_FIXED_TIME_STEP);
}
render();
}
如果您真的需要精度,您可以添加一个浮点变量作为“缓冲区”:
sf::Clock clock;
sf::Event event;
float timeBeforeNextStep = 0.f; // "buffer"
float timeDilation = 1.f; // Useful if you want to slow or speed up time ( <1 for slowmo, >1 for speedup)
while (window_.isOpen()) {
while (window_.pollEvent(event)) {}
timeBeforeNextStep -= clock.restart().asSeconds() * timeDilation;
if (timeBeforeNextStep < FLT_FIXED_TIME_STEP) {
timeBeforeNextStep += FLT_FIXED_TIME_STEP; // '+=', not '=' to make sure we don't lose any time.
update(FLT_FIXED_TIME_STEP);
// Rendering every time you update is not always the best solution, especially if you have a very small time step.
render();
}
}
您可能想要使用另一个缓冲区进行渲染(例如,如果您想要 运行 正好是 60 FPS)。
您应该删除 if 语句并始终计算 alpha; if 语句将永远不会执行,因为在退出 while 循环后条件始终为 false!
循环后累加器将介于 0 和 timeStep 之间,因此您只需绘制最新位置而不是插值。
我正在尝试使用 用于物理和插值的固定增量时间 在渲染状态之前编写简单循环。我在固定时间步长的游戏教程中使用 Gaffer,我试图理解它并使其发挥作用。
float timeStep = 0.01;
float alpha = 1.0;
while (isOpen()) {
processInput();
deltaTime = clock.restart(); // get elapsed time
if (deltaTime > 0.25) { deltaTime = 0.25; } // drop frame guard
accumulator += deltaTime;
while (accumulator >= timeStep) {
// spritePosBefore = sprite.getPosition();
accumulator -= timeStep;
// sprite.move(velocity * timeStep, 0);
// spritePosAfter = sprite.getPosition();
}
if (accumulator > timeStep) { alpha = accumulator / timeStep; } else { alpha = 1.0; }
// sprite.setPosition(Vector2f(spritePosBefore * (1 - alpha) + spritePosAfter * alpha));
clear();
draw(sprite);
display();
}
现在,一切看起来都很好。我有固定的物理时间步长,在物理更新后尽可能绘制并在两个位置之间进行插值。它应该可以完美运行,但我仍然可以看到精灵卡顿,甚至偶尔会后退一个像素。为什么会这样?我的代码有问题吗?我花了两天时间试图了解游戏循环,这将确保我的动作完美无缺,但它似乎并没有像我想象的那样工作。知道可以改进什么吗?
我不认为你这样做的方式一定是错误的,但它看起来有点过于复杂。我不明白你到底想做什么,所以我只想分享我在我的 SFML 应用程序中实现“固定时间步长”的方式。
以下是最简单的方法,对于大多数应用程序来说“足够好”。虽然它不是最精确的(测量时间和实际时间之间可能会有一点误差):
sf::Clock clock;
sf::Event event;
while (window_.isOpen()) {
while (window_.pollEvent(event)) {}
if (clock.getElapsedTime().asSeconds() > FLT_FIXED_TIME_STEP) {
clock.restart();
update(FLT_FIXED_TIME_STEP);
}
render();
}
如果您真的需要精度,您可以添加一个浮点变量作为“缓冲区”:
sf::Clock clock;
sf::Event event;
float timeBeforeNextStep = 0.f; // "buffer"
float timeDilation = 1.f; // Useful if you want to slow or speed up time ( <1 for slowmo, >1 for speedup)
while (window_.isOpen()) {
while (window_.pollEvent(event)) {}
timeBeforeNextStep -= clock.restart().asSeconds() * timeDilation;
if (timeBeforeNextStep < FLT_FIXED_TIME_STEP) {
timeBeforeNextStep += FLT_FIXED_TIME_STEP; // '+=', not '=' to make sure we don't lose any time.
update(FLT_FIXED_TIME_STEP);
// Rendering every time you update is not always the best solution, especially if you have a very small time step.
render();
}
}
您可能想要使用另一个缓冲区进行渲染(例如,如果您想要 运行 正好是 60 FPS)。
您应该删除 if 语句并始终计算 alpha; if 语句将永远不会执行,因为在退出 while 循环后条件始终为 false!
循环后累加器将介于 0 和 timeStep 之间,因此您只需绘制最新位置而不是插值。