Я делал ядро своей игры на прошлой неделе, и я ударил стену, потому что рендеринг был просто недостаточно хорош. Движение было отрывистым, я слезился, и вообще было много лагов. Я подумал, что это может быть не ошибка моего игрового движка, поэтому я протестировал рендеринг с очень простым геймплеем:
sf::RenderWindow window(sf::VideoMode(1024, 768), "Testing");
window.setVerticalSyncEnabled(true);
sf::Clock clock;
sf::Event event;
float elapsed;
while(window.isOpen())
{
elapsed += clock.restart().asSeconds();
std::cout << 1.f/elapsed << std::endl;
while(elapsed > 1.f/60.f)
{
while(window.pollEvent(event))
{
if (event.type == sf::Event::Closed || event.key.code == sf::Keyboard::Escape)
{
window.close();
}
}
elapsed -= 1.f/60.f;
}
window.clear();
window.display();
}
Частота кадров начинается с 40, увеличивается до 60, а затем падает до 30, снова увеличивается и повторяется. Если я установлю VSynct в false, я получу где-то между 30-500 кадров в секунду. Либо я не проверяю частоту кадров правильно, либо что-то не так с моими драйверами nvidia (я переустанавливал их 2 раза без изменений). Любая помощь приветствуется!
Вы указали мне на материал, который похож на ваш код, но вы написали его по-другому.
От: gameprogrammingpatterns.com/game-loop.html
double previous = getCurrentTime();
double lag = 0.0;
while (true)
{
double current = getCurrentTime();
double elapsed = current - previous;
previous = current;
lag += elapsed;
processInput();
while (lag >= MS_PER_UPDATE)
{
update();
lag -= MS_PER_UPDATE;
}
render();
}
Вы, кажется, используете одну переменную elapsed
для обоих, elapsed
а также lag
, Это то, что меня озадачило. Ваш калечащийся с elapsed
делает его непригодным для измерения времени. Я думаю, что ваш код должен выглядеть примерно так:
sf::RenderWindow window(sf::VideoMode(1024, 768), "Testing");
window.setVerticalSyncEnabled(true);
sf::Clock clock;
sf::Event event;
float lag;
float elapsed;
while(window.isOpen())
{
lag = elapsed = clock.restart().asSeconds();
std::cout << 1.f/elapsed << std::endl;
while(lag > 1.f/60.f)
{
while(window.pollEvent(event))
{
if (event.type == sf::Event::Closed || event.key.code == sf::Keyboard::Escape)
{
window.close();
}
}
lag -= 1.f/60.f;
}
window.clear();
window.display();
}
Я все еще не уверен, будет ли это правильно. Я не знаю что clock.restart().asSeconds()
делает точно. Лично я бы реализовал это построчно, как в примере. Зачем перепроектировать рабочий код?
Изменить: OP подтвердил, что, используя elapsed
поскольку «троттлинг» нарушал свое назначение как временная переменная измерения.
Вот что я считать это происходит. Если я правильно понимаю ваш код, вы хотите, чтобы временной интервал симуляции оставался постоянным — 60 раз в секунду. Вы используете своего рода аккумулятор, чтобы отслеживать, сколько раз вы должны выполнять цикл. Проблема при использовании такого аккумулятора заключается в том, что вы должны помнить, что ваш цикл симуляции требует времени. Чтобы проиллюстрировать, почему это может быть проблемой, давайте рассмотрим некоторые случаи. Я буду использовать для временного шага 20 миллисекунд вместо 16,6666 (1/60 секунды) для ясности. Давайте предположим:
elapsed >= 20
, это сбрасывается, и ваш цикл симуляции запущен. В следующий раз, когда вы вернетесь к вершине, elapsed == 10 + c
где c
это что-то маленькое, и оно пропускает. В конце концов, elapsed >= 20
снова, и это повторяется. Все отлично.elapsed
получает до 20, а внутренний цикл принимает 20
миллисекунды, чтобы бежать. В следующий раз, когда вы запустите свой цикл, elapsed == 20
, Это повторяется. Все в принципе нормально.elapsed
добирается до 20, внутренний цикл работает, занимая 40 миллисекунд. Затем внешний цикл запускается снова, на этот раз с elapsed == 40
, Затем внутренний цикл запускается дважды, занимая 80 миллисекунд. elapsed
сейчас 80. Внутренний цикл выполняется 4 раза, занимая 160 миллисекунд. elapsed
сейчас 160. Внутренний цикл работает 8 раз, и занимает 320 миллисекунд … И так далее.Конечно, время, которое занимает цикл симуляции, никогда не будет постоянным, как в этих примерах, но идея та же. Если вы можете предположить, что цикл никогда не займет намного больше времени, чем ваш шаг моделирования, это нормально. Как только это займет больше времени, чем 1/60
Через секунду, чтобы запустить ваш цикл, вы начинаете получать своего рода цикл обратной связи, где внешний цикл занимает все больше и больше времени, чтобы закончить.
Обычный способ исправить проблему, связанную с движением, зависящим от частоты кадров, — передать дельта-время всем update()
-тип функции у меня есть, где бы они ни были. Затем все движения / атаки / все, что зависит от этой дельты. Например, для движения у меня было бы что-то вроде этого:
const static pixelsPerSecond = 100
update(float delta) { //delta is seconds
x += moveX * pixelsPerSecond * delta;
y += moveY * pixelsPerSecond * delta;
}
РЕДАКТИРОВАТЬ: Только что увидел ссылку, которую вы опубликовали выше, в которой упоминаются недостатки, связанные с переменным шагом по времени, как я рекомендовал. В целом, однако, если вы не выполняете точную физическую симуляцию или мультиплеер, все должно работать нормально. (Как ни странно, ваша ссылка содержит ссылку на Статья Гленна Филдера, который также упоминает «спираль смерти», которую я описал выше.)