В настоящее время я начинаю изучать OpenGL в школе, и на днях я начал делать простую игру (самостоятельно, а не для школы). Я использую freeglut и собираю его на C, поэтому для своего игрового цикла я действительно использовал переданную мне функцию, glutIdleFunc
чтобы обновить все чертежи и физику за один проход. Это было хорошо для простых анимаций, которые меня не особо волновали по частоте кадров, но поскольку игра в основном основана на физике, я действительно хочу (нужно) связать, насколько быстро она обновляется.
Поэтому моя первая попытка состояла в том, чтобы мою функцию я передал glutIdleFunc
( myIdle()
), чтобы отслеживать, сколько времени прошло с момента его предыдущего вызова, и обновлять физику (и текущую графику) каждые миллисекунды. Я timeGetTime()
делал это (используя <windows.h>
). И это заставило меня задуматься, действительно ли использование функции простоя является хорошим способом прохождения игрового цикла?
Мой вопрос: как лучше реализовать игровой цикл в OpenGL? Следует ли мне избегать использования функции ожидания?
Ответы:
Простой ответ - нет, вы не хотите использовать обратный вызов glutIdleFunc в игре, в которой есть какая-то симуляция. Причина этого в том, что эта функция отделяет анимацию и рисует код от обработки событий окна, но не асинхронно. Другими словами, получение и передача событий окна останавливает отрисовку кода (или чего-либо, что вы поместили в этот обратный вызов), это прекрасно для интерактивного приложения (взаимодействие, затем ответ), но не для игры, в которой должно развиваться физическое состояние или состояние игры. независимо от взаимодействия или рендеринга времени.
Вы хотите полностью отделить обработку ввода, состояние игры и отрисовку кода. Существует простое и понятное решение, которое не включает графическую библиотеку напрямую (т.е. она переносима и легко визуализируется); Вы хотите, чтобы весь игровой цикл производил время, а симуляция потребляла произведенное время (кусками). Ключевым моментом, однако, является то, чтобы затем интегрировать количество времени, которое ваша симуляция потребляла в вашу анимацию
Лучшее объяснение и учебник, который я нашел по этому вопросу, - это « Исправление твоего временного шага» Гленна Фидлера.
В этом уроке содержится полное описание, однако, если у вас нет реального физического моделирования, вы можете пропустить истинную интеграцию, но основной цикл все еще сводится к (в подробном псевдокоде):
Делая это таким образом, задержки в вашем коде рендеринга, обработке ввода или операционной системе не приводят к отставанию вашего игрового состояния. Этот метод также переносим и независим от графической библиотеки.
GLUT - хорошая библиотека, однако она строго основана на событиях. Вы регистрируете обратные вызовы и запускаете основной цикл. Вы всегда передаете контроль над своим основным циклом, используя GLUT. Есть обходные пути , вы также можете подделать внешний цикл, используя таймеры и тому подобное, но другая библиотека, вероятно, является лучшим (более простым) способом. Есть много альтернатив, вот несколько (с хорошей документацией и быстрыми учебниками):
источник
Я думаю, что
glutIdleFunc
это нормально; это, по сути, то, что вы бы в конечном итоге сделали вручную, если бы не использовали его. Неважно, что у вас будет жесткий цикл, который не вызывается в фиксированном шаблоне, и вам нужно выбрать, хотите ли вы преобразовать его в цикл с фиксированным временем или оставить его в цикле с переменным временем и сделать уверен, что все ваши математические счета для интерполяции времени. Или даже смесь из них, так или иначе.У вас, безусловно, может быть
myIdle
функция, которую вы передаетеglutIdleFunc
, и в рамках которой вы измеряете прошедшее время, делите ее на свой фиксированный временной интервал и вызываете другую функцию много раз.Вот что не нужно делать:
fixedTimestep не будет вызываться каждые 10 миллисекунд. На самом деле он будет работать медленнее, чем в режиме реального времени, и вы можете в конечном итоге выдумать цифры, чтобы компенсировать, когда на самом деле корень проблемы заключается в потере остатка при делении
timeDifference
на 10.Вместо этого сделайте что-то вроде этого:
Теперь эта структура цикла гарантирует, что ваша
fixedTimestep
функция будет вызываться один раз в 10 миллисекунд, без потери каких-либо миллисекунд в качестве остатка или чего-либо подобного.Из-за многозадачности операционных систем нельзя полагаться на функцию, вызываемую точно каждые 10 миллисекунд; но вышеприведенный цикл будет происходить достаточно быстро, и, надеюсь, он будет достаточно близок, и вы можете предположить, что каждый вызов
fixedTimestep
будет увеличивать вашу симуляцию на 10 миллисекунд.Пожалуйста, прочитайте этот ответ и особенно ссылки, приведенные в ответе.
источник