(In the beginning, sorry for my english ;))
Hi everybody. I know that there are some similar questions about laggy fixed time step loop on SFML forum, but I didn't find answer.
Recently I remade my loop code, because previous fixed time step loop on every computer behave differently, on slower computer game was slower, on faster computer game was faster. It was something like that:
const float FPS=100.0f;
RenderWindow window;
Clock clock;
Time time;
while(window.isOpen())
{
if(time.asSeconds()>(1.0f/FPS))
{
update();
clock.restart();
time=Time::Zero;
}
display();
time=clock.getElapsedTime();
}
So I was searching better fixed time step loop on the internet. I found some good articles, for example:
- deWiTTERS http://www.koonsolo.com/news/dewitters-gameloop/
- Gaffer http://gafferongames.com/game-physics/fix-your-timestep/
- http://temporal.pr0.pl/devblog/download/arts/fixed_step/fixed_step.pdf
I've decided to the last article. It's in polish language, but it doesn't matter. There is (for me) the best and the clearest loop:
float dt = 0.0f; //time since last update
float lastUpdateTime = GetCurrentTime(); //last update time
//exemplary GetCurrenTime() function gets
//current time from system (in SFML it's just getElapsedTime())
float accumulator = 0.0f;
const float TIME_STEP = 0.03; //time step and frame duration
//physics in seconds; here 30 miliseconds,
//about 30 updates per second
const float MAX_ACCUMULATED_TIME = 1.0; //max time gathered in single
//main loop circuit
while(true)
{
dt = GetCurrentTime() - lastUpdateTime; //calculation of the time since the last frame
lastUpdate += dt; //substitution
dt = std::max(0, dt); //we're making sure that dt>=0
accumulator += dt;
accumulator = clamp(accumulator, 0, MAX_ACCUMULATED_TIME); //we prevent
//too many updates in a given loop
//cycle
GrabInput(); //<-- keyboard, mouse, network etc. events
while(accumulator > TIME_STEP)
{
UpdateGame(TIME_STEP); //<-- physics and game logic update
accumulator -= TIME_STEP;
}
RenderGame(); //<-- display current game state on screen
}
//clamp function
template <typename T>
T clamp(const T& what, const T& a, const T& b)
{
return std::min(b, std::max(what, a));
}
In SFML implementation it looks like this:
float dt = 0.0f; //time since last update
float lastUpdateTime = 0.f;
float accumulator = 0.0f;
const float TIME_STEP = 0.03; //I initialise this variable differently: const float TIME_STEP=1.f/60.f for 60 FPS
const float MAX_ACCUMULATED_TIME = 1.0;
while(true)
{
dt = clock.getElapsedTime().asSeconds() - lastUpdateTime; //clock is restarting in constructor
lastUpdate += dt;
dt = std::max(0, dt);
accumulator += dt;
accumulator = clamp(accumulator, 0, MAX_ACCUMULATED_TIME);
GrabInput();
while(accumulator > TIME_STEP)
{
UpdateGame(TIME_STEP);
accumulator -= TIME_STEP;
}
RenderGame();
}
OK, this loop is working same on different computers, but now there is a new problem. My loop is lagging. It's not smooth as previous loop. It's not lagging all time, but it's noticeable. I tried to replace TIME_STEP (in UpdateGame function) to accumulator, deltaTime or lastUpdateTime, but as expected it didn't help. I tried to change timeStep and it didn't help too. I was experimenting with setFramerateLimit() and setVerticalSyncEnabled(), no changes. I think everything is calculated correctly, but to make sure I checked variables values:
...
while(accumulator > TIME_STEP)
{
UpdateGame(TIME_STEP);
cout << "Delta time: " << dt << endl;
cout << "Last update time: " << lastUpdateTime << endl;
cout << "Accumulator (before subtract): " << accumulator << endl;
accumulator -= TIME_STEP;
cout << "Accumulator (after subtract): " << accumulator << endl;
cout << "Time step: " << TIME_STEP << endl;
cout << "Max accumulated time: " << MAX_ACCUMULATED_TIME << endl;
cout << "------------------------------------------------------" << endl;
}
...
Here is output (random sample):
------------------------------------------------------
Delta time: 0.0154901
Last update time: 5.5339
Accumulator (before subtract): 0.0172362
Accumulator (after subtract): 0.0005695
Time step: 0.0166667
Max accumulated time: 1
------------------------------------------------------
In deWiTTERS article in the last loop example was interpolation, which smooths everything, but I'm not sure I need it. I think there is other solution. I don't know what's wrong with this code.
Thanks for help.
I just recently had issues with my time step in another thread. However, may what helped me will help you get a better idea of how to setup your loop:
SFML and Game time (http://en.sfml-dev.org/forums/index.php?topic=20367.0)
Some pseudo code for an implementation of fixed timestep:
deltaTime = 0.1 seconds
while (windowIsOpen)
{
currentTime = clock.currentTime
timeToProcess = timeToProcess + currentTime - previousTime
previousTime = currentTime
while (timeToProcess >= deltaTime)
{
applyLogicAndPhysics()
timeToProcess = timeToProcess - deltaTime
}
prepareDisplay()
drawDisplay()
}
OK, I've got small example code, I'm using loop from this link http://temporal.pr0.pl/devblog/download/arts/fixed_step/fixed_step.pdf OK, here is the code:
#include <SFML/Graphics.hpp>
#include <algorithm> //for max() and min() functions
using namespace std;
using namespace sf;
template <typename T>
T clamp(const T& what, const T& a, const T& b)
{
return min(b, max(what, a));
}
int main()
{
//setting window
ContextSettings settings;
settings.antialiasingLevel = 8;
RenderWindow window(VideoMode::getDesktopMode(), "SFML works!",Style::Fullscreen,settings);
RectangleShape shape(Vector2f(75, 75));
shape.setFillColor(Color::White);
//setting fixed time step loop
float dt = 0.0f;
float lastUpdateTime = 0.0f; //for now it's 0
float accumulator = 0.0f;
const float TIME_STEP = 1.f / 60.f; //for updating game 60 times per second
const float MAX_ACCUMULATED_TIME = 1.0; //for make sure accumulator will not be too big
Clock clock;
clock.restart();
while (window.isOpen())
{
Event event;
while (window.pollEvent(event))
{
if (event.type == Event::Closed || (event.type == Event::KeyPressed && event.key.code == Keyboard::Escape))
window.close();
}
dt = clock.getElapsedTime().asSeconds() - lastUpdateTime; //calculating time since last frame
lastUpdateTime += dt;
dt = max(0.0f, dt); //to make sure dt is not less than 0
accumulator += dt;
accumulator = clamp(accumulator, 0.0f, MAX_ACCUMULATED_TIME); //for make sure accumulator is not too big (not bigger than 1 (second))
while (accumulator > TIME_STEP)
{
//update game, in this case moving object (with no class, just a few if()'s)
if (Keyboard::isKeyPressed(Keyboard::W))
shape.move(0, -180 * TIME_STEP);
if (Keyboard::isKeyPressed(Keyboard::S))
shape.move(0, 180 * TIME_STEP);
if (Keyboard::isKeyPressed(Keyboard::A))
shape.move(-180 * TIME_STEP, 0);
if (Keyboard::isKeyPressed(Keyboard::D))
shape.move(180 * TIME_STEP, 0);
accumulator -= TIME_STEP;
}
window.clear();
window.draw(shape);
window.display();
}
return 0;
}
Try this code, please. You will probably see not smooth movement with lags. But if you change this line:
const float TIME_STEP = 1.f / 60.f; //for updating game 60 times per second
... to this:
const float TIME_STEP = 1.f / 200.f; //for updating game 200 times per second
... you will probably see smooth movement, with no lags.
I think, you will see smooth movement only in second code, with TIME_STEP = 1.f / 200.f If not... I don't know what's wrong. I have no idea.