Как установить фиксированную скорость рисования и мировые шаги в Box2D и SFML? - PullRequest
0 голосов
/ 12 февраля 2019

Есть простой проект, Box2D занимается физикой, рисует SFML.На разных машинах с разной производительностью скорость работы программы различна.Как установить фиксированную скорость?Чтобы на мощном, среднем и слабом компьютере скорость программы (движение объектов) была бы одинаковой?
Нужно заставить Box2D работать на разных компьютерах с одинаковой скоростью.Он также дает координаты, а SFML рисует квадрат по заданным координатам.Как решить эту проблему?

1 Ответ

0 голосов
/ 13 февраля 2019

Постоянная частота кадров - это то, что вы ищете.Это хорошо известная проблема.

Как вы говорите, разные машины будут иметь разную частоту процессора.Это может быть связано с тем, что компьютер с лучшим аппаратным обеспечением обрабатывает ваш игровой цикл больше раз в секунду.

Если вы хотите установить фиксированную частоту кадров, скажем, 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. Создание игрового тика

Фиксированные временные шаги

Решение, которое мы придумали до сих пор, достаточно для многих случаев.Но это не идеальное решение [...], поскольку каждый кадр уникален, и вы не можете гарантировать, что время дельты останется неизменным.Учтите, что кадр может иногда занимать три раза среднее время дельты.Это может привести к серьезным ошибкам в игровой логике, например, когда игрок преодолевает расстояние в три раза и проходит через стену, с которой он обычно сталкивается.Вот почему физические движки ожидают, что время дельты будет зафиксировано.

Ниже приведен рисунок, описывающий проблему, на которую мы ссылаемся:

enter image description here

Теперь мы будем использовать технику, называемую фиксированными временными шагами.Мы пишем код, который гарантирует, что при любых обстоятельствах мы всегда даем одно и то же время дельты функции обновления, независимо от того, что происходит.Если вам кажется, что это звучит трудно, нет большой разницы с тем, что у нас уже есть.Нам просто нужно сделать некоторые записи в нашем коде, чтобы узнать, сколько времени прошло с тех пор, как мы в последний раз вызывали функцию update().

Код, аналогичный приведенному выше, с внутренним while (timeSinceLastUpdate > TimePerFrame)

Фактический эффект этого изменения состоит в том, что мы накапливаем, сколько времени прошло в переменной timeSinceLastUpdate.Когда мы превышаем требуемую сумму для одного кадра, мы вычитаем желаемую длину этого кадра (а именно TimePerFrame) и обновляем игру.Мы делаем это до тех пор, пока снова не окажемся ниже необходимой суммы.Это решает проблему с переменным временем дельты, поскольку мы гарантируем, что всегда выполняется одинаковое количество кадров.В загружаемом приложении частота логических кадров будет установлена ​​равной 60 кадрам в секунду, если постоянная TimePerFrame равна sf::seconds(1.f / 60.f).

...