Постоянная частота кадров - это то, что вы ищете.Это хорошо известная проблема.
Как вы говорите, разные машины будут иметь разную частоту процессора.Это может быть связано с тем, что компьютер с лучшим аппаратным обеспечением обрабатывает ваш игровой цикл больше раз в секунду.
Если вы хотите установить фиксированную частоту кадров, скажем, 60 FPS, все, что вам нужно сделатьis (как указано Öö Tiib ) ограничить ваши обновления делать одно обновление каждые 1/60 секунд.
Это обычная реализация:
const sf::Time TimePerFrame = sf::seconds(1.f / 60.f); // 60 FPS
void run(){
sf::Clock clk;
sf::Time timeSinceLastUpdate = sf::Time::Zero;
while (_window.isOpen())
{
sf::Time dt = clk.restart();
timeSinceLastUpdate += dt;
while (timeSinceLastUpdate > TimePerFrame)
{
timeSinceLastUpdate -= TimePerFrame;
processInput();
update(TimePerFrame);
}
render();
}
}
}
Таким образом, если машина работает быстрее, вы будете обновлять состояние своего мира только 1 раз каждые 1/60 секунд.
TL; DR;
Когда ваша программа запускается, timeSinceLastUpdate
принимает значение 0
, вы проверяете while
и получаете время, прошедшее с момента создания (или перезапуска) clk
, скажем, это число t1 меньше 1/60 секунд.Программа добавляет его к timeSinceLastUpdate
и рисует сцену, как она была создана (начальное состояние вашей игры).
Во втором цикле вашего while
вы снова получаете истекшее время (истекшее с тех пор, какпоследний цикл, потому что вы перезапустили часы).Давайте назовем это время t2 .Опять же, это число меньше 1/60 секунд, , но , когда вы добавляете его к timeSinceLastUpdate
, оба добавляют к числу, большему 1/60 (т. Е. timeSinceLastUpdate = t1 + t2 > TimePerFrame
).На этот раз вы войдете во второй цикл и обновите свой мир, как если бы прошло 1/60 секунд.Вы вычитаете это 1/60 секунд из timeSinceLastUpdate
и проверяете снова.Если осталось более 1/60 секунд, вы будете обновлять снова, пока не останетесь в курсе .
Если вы посмотрите внимательно, вы звоните только update(TimePerFrame)
, так что вы всегда будете обновлять свой мир с помощью кусков из 1/60 секунд.
С SFML Book Development Game , Глава 1. Создание игрового тика
Фиксированные временные шаги
Решение, которое мы придумали до сих пор, достаточно для многих случаев.Но это не идеальное решение [...], поскольку каждый кадр уникален, и вы не можете гарантировать, что время дельты останется неизменным.Учтите, что кадр может иногда занимать три раза среднее время дельты.Это может привести к серьезным ошибкам в игровой логике, например, когда игрок преодолевает расстояние в три раза и проходит через стену, с которой он обычно сталкивается.Вот почему физические движки ожидают, что время дельты будет зафиксировано.
Ниже приведен рисунок, описывающий проблему, на которую мы ссылаемся:
Теперь мы будем использовать технику, называемую фиксированными временными шагами.Мы пишем код, который гарантирует, что при любых обстоятельствах мы всегда даем одно и то же время дельты функции обновления, независимо от того, что происходит.Если вам кажется, что это звучит трудно, нет большой разницы с тем, что у нас уже есть.Нам просто нужно сделать некоторые записи в нашем коде, чтобы узнать, сколько времени прошло с тех пор, как мы в последний раз вызывали функцию update()
.
Код, аналогичный приведенному выше, с внутренним while (timeSinceLastUpdate > TimePerFrame)
Фактический эффект этого изменения состоит в том, что мы накапливаем, сколько времени прошло в переменной timeSinceLastUpdate
.Когда мы превышаем требуемую сумму для одного кадра, мы вычитаем желаемую длину этого кадра (а именно TimePerFrame
) и обновляем игру.Мы делаем это до тех пор, пока снова не окажемся ниже необходимой суммы.Это решает проблему с переменным временем дельты, поскольку мы гарантируем, что всегда выполняется одинаковое количество кадров.В загружаемом приложении частота логических кадров будет установлена равной 60 кадрам в секунду, если постоянная TimePerFrame
равна sf::seconds(1.f / 60.f)
.