Существует долгая история того, как мы пришли к этому общему соглашению, с множеством захватывающих задач на этом пути, поэтому я постараюсь мотивировать его поэтапно:
1. Проблема: устройства работают на разных скоростях
Вы когда-нибудь пытались сыграть в старую игру для DOS на современном ПК, и она работает неиграбельно быстро - просто размытие?
У многих старых игр был очень наивный цикл обновления - они собирали входные данные, обновляли состояние игры и рендерили так быстро, как позволяло оборудование, без учета того, сколько времени прошло. Это означает, что как только меняется оборудование, меняется и игровой процесс.
Как правило, мы хотим, чтобы наши игроки имели постоянный опыт и ощущения от игры на различных устройствах (при условии, что они соответствуют минимальным требованиям), независимо от того, используют ли они прошлогодний телефон или новейшую модель, игровой настольный компьютер высшего класса или ноутбук среднего уровня.
В частности, для игр, которые являются конкурентоспособными (многопользовательские или через таблицы лидеров), мы не хотим, чтобы игроки, работающие на одном устройстве, имели преимущество перед другими, потому что они могут работать быстрее или иметь больше времени для реакции.
Верным решением здесь является блокировка скорости, с которой мы делаем обновления состояния геймплея. Таким образом, мы можем гарантировать, что результаты всегда будут одинаковыми.
2. Так почему бы просто не заблокировать частоту кадров (например, с помощью VSync) и по-прежнему запускать обновления и рендеринг состояния игрового процесса в режиме lockstep?
Это может работать, но не всегда приемлемо для аудитории. Было долгое время, когда работа на скорости 30 кадров в секунду считалась золотым стандартом для игр. Теперь игроки обычно ожидают минимальную полосу 60 кадров в секунду, особенно в многопользовательских экшен-играх, а некоторые старые игры теперь выглядят заметно изменчиво, поскольку наши ожидания изменились. Также есть вокальная группа игроков на ПК, которые вообще возражают против блокировок кадров. Они платят много за свое новейшее оборудование и хотят использовать эту вычислительную мощь для максимально плавного и высокоточного рендеринга, на который он способен.
В VR, в частности, частота кадров является королем, и стандарт продолжает набирать обороты. В начале недавнего возрождения виртуальной реальности игры часто работали со скоростью около 60 кадров в секунду. Теперь 90 более стандартен, и аппаратное обеспечение, подобное PSVR, начинает поддерживать 120. Это может продолжаться и дальше. Таким образом, если игра VR ограничивает частоту кадров тем, что выполнимо и принято сегодня, она может остаться позади, так как аппаратное обеспечение и ожидания будут развиваться дальше.
(Как правило, будьте осторожны, когда говорят, что «игроки не могут воспринимать что-либо быстрее, чем XXX», поскольку это обычно основано на определенном типе «восприятия», например, на распознавании кадра в последовательности. Восприятие непрерывности движения обычно гораздо более чувствительный.)
Последняя проблема заключается в том, что игра, использующая заблокированную частоту кадров, также должна быть консервативной - если вы когда-нибудь попадете в момент, когда вы обновляете и отображаете необычно большое количество объектов, вы не хотите пропустить свой кадр. срок и вызвать заметное заикание или заминку. Поэтому вам нужно либо установить достаточно низкий бюджет для содержания, чтобы оставить запас, или инвестировать в более сложные функции динамической настройки качества, чтобы избежать привязки всего игрового процесса к худшей производительности на минимальном оборудовании.
Это может быть особенно проблематично, если проблемы с производительностью обнаруживаются на поздних стадиях разработки, когда все ваши существующие системы собраны и настроены с учетом частоты кадров при рендеринге, которую вы не всегда можете решить. Разъединение скорости обновления и рендеринга дает больше гибкости при работе с изменчивостью производительности.
3. Не имеет ли обновление с фиксированным временным шагом те же проблемы, что и (2)?
Я думаю, что это основа первоначального вопроса: если мы отделяем наши обновления и иногда визуализируем два кадра без обновлений между состояниями игры, то разве это не то же самое, что рендеринг в режиме lockstep при более низкой частоте кадров, поскольку видимых изменений на экран?
На самом деле есть несколько разных способов, которыми игры могут эффективно использовать развязку этих обновлений:
а) частота обновления может быть выше, чем частота кадров
Как отмечает Tyjkenn в другом ответе, физика, в частности, часто имеет более высокую частоту, чем рендеринг, что помогает минимизировать ошибки интеграции и дает более точные столкновения. Таким образом, вместо 0 или 1 обновлений между визуализированными кадрами у вас может быть 5, 10 или 50.
Теперь игрок, выполняющий рендеринг со скоростью 120 кадров в секунду, может получать 2 обновления за кадр, в то время как игрок с более низкой спецификацией аппаратного рендеринга со скоростью 30 кадров в секунду получает 8 обновлений за кадр, и обе их игры работают с одинаковой скоростью «тики в секунду в реальном времени». Более качественное оборудование делает его более гладким, но не меняет радикально, как работает геймплей.
Здесь есть риск, что, если частота обновления не соответствует частоте кадров, вы можете получить «частоту ударов» между ними . Например. у большинства фреймов у нас достаточно времени для 4 обновлений состояния игры и небольшого остатка, затем время от времени у нас достаточно накопленных ресурсов, чтобы сделать 5 обновлений в фрейме, делая небольшой скачок или заикание в движении. Это можно решить с помощью ...
б) интерполяция (или экстраполяция) игрового состояния между обновлениями
Здесь мы часто будем позволять игровому состоянию жить в одном фиксированном временном шаге в будущем и хранить достаточно информации из двух самых последних состояний, чтобы мы могли отобразить произвольную точку между ними. Затем, когда мы готовы показать новый кадр на экране, мы смешиваемся с соответствующим моментом только для отображения (т. Е. Здесь мы не изменяем основное игровое состояние)
Если все сделано правильно, это делает движение более плавным и даже помогает скрыть некоторые колебания частоты кадров, пока мы не опустимся слишком низко.
c) Добавление плавности к изменениям не игрового состояния
Даже без интерполяции состояния геймплея мы все же можем получить некоторые плавные выигрыши.
Чисто визуальные изменения, такие как анимация персонажей, системы частиц или VFX, и элементы пользовательского интерфейса, такие как HUD, часто обновляются отдельно от фиксированного временного шага состояния игрового процесса. Это означает, что если мы помечаем наше игровое состояние несколько раз за кадр, мы не платим их стоимость с каждым тиком - только на последнем проходе рендеринга. Вместо этого мы масштабируем скорость воспроизведения этих эффектов, чтобы соответствовать длине кадра, чтобы они воспроизводились настолько плавно, насколько позволяет частота кадров рендеринга, не влияя на скорость или честность игры, как обсуждалось в (1).
Движение камеры может сделать это тоже - особенно в VR, мы иногда показываем один и тот же кадр более одного раза, но перепроектируем его, чтобы учесть движение головы игрока между ними , чтобы мы могли улучшить воспринимаемую задержку и комфорт, даже если мы можем не рендерит все так быстро. Некоторые системы потоковой передачи игр (где игра запускается на сервере, а игрок запускает только тонкий клиент) также используют эту версию.
4. Почему бы просто не использовать этот стиль (с) для всего? Если это работает для анимации и пользовательского интерфейса, разве мы не можем просто масштабировать наши обновления состояния игрового процесса в соответствии с текущей частотой кадров?
Да * это возможно, но нет, это не просто.
Этот ответ уже немного длинный, поэтому я не буду вдаваться в подробности, просто краткое резюме:
Умножение на deltaTime
работает, чтобы приспособиться к обновлениям переменной длины для линейного изменения (например, движение с постоянной скоростью, обратный отсчет таймера или прогресс по временной шкале анимации)
К сожалению, многие аспекты игр являются нелинейными . Даже такая простая вещь, как гравитация, требует более сложных методов интеграции или подшагов с более высоким разрешением, чтобы избежать расхождения результатов при различных частотах кадров. Ввод и управление плеером само по себе является огромным источником нелинейности.
В частности, результаты дискретного обнаружения и разрешения коллизий зависят от частоты обновления, что приводит к ошибкам туннелирования и дрожания, если кадры становятся слишком длинными. Таким образом, переменная частота кадров заставляет нас использовать более сложные / дорогие методы непрерывного обнаружения столкновений для большей части нашего контента или терпеть изменчивость нашей физики. Даже при непрерывном обнаружении столкновений возникают проблемы, когда объекты движутся по дугам, что требует более коротких временных шагов ...
Таким образом, в общем случае для игры средней сложности, поддержание согласованного поведения и справедливости полностью за счет deltaTime
масштабирования находится где-то между очень сложным и интенсивным обслуживанием до невозможного.
Стандартизация частоты обновления позволяет нам гарантировать более согласованное поведение в различных условиях , часто с более простым кодом.
Сохранение этой частоты обновлений в отрыве от рендеринга дает нам гибкость в управлении плавностью и производительностью опыта без изменения логики игрового процесса .
Даже тогда мы никогда не получим действительно «идеальную» независимость от частоты кадров, но, как и многие другие подходы в играх, она дает нам управляемый метод, позволяющий набрать «достаточно хороший» для потребностей данной игры. Вот почему это обычно преподносится как полезная отправная точка.