Я читал Koen Witters подробную статью о различных решениях игрового цикла, но у меня возникли некоторые проблемы при реализации последнего с GLUT, который является рекомендуемым.
Послечитая пару статей, учебных пособий и кода от других людей о том, как добиться постоянной скорости игры, я думаю, что то, что я в настоящее время реализовал (я опубликую код ниже), это то, что Коен Виттерс назвал Скорость игры зависит отПеременная FPS , вторая в его статье.
Во-первых, благодаря моему поисковому опыту, есть пара человек, которые, вероятно, обладают знаниями, чтобы помочь с этим, но не знают, что такое GLUT, и яЯ собираюсь попытаться объяснить (не стесняйтесь меня поправлять) соответствующие функции для моей проблемы этого набора инструментов OpenGL.Пропустите этот раздел, если вы знаете, что такое GLUT и как с ним играть.
GLUT Toolkit:
- GLUT - это набор инструментов OpenGL, который помогает выполнять общие задачив OpenGL.
glutDisplayFunc(renderScene)
получает указатель на функцию обратного вызова renderScene()
, которая будет отвечать за отображение.Функция renderScene()
будет вызываться только один раз после регистрации обратного вызова. glutTimerFunc(TIMER_MILLISECONDS, processAnimationTimer, 0)
принимает количество миллисекунд, которое должно пройти перед вызовом обратного вызова processAnimationTimer()
.Последний аргумент - это просто значение для передачи в обратный вызов таймера.processAnimationTimer()
будет вызываться не каждый TIMER_MILLISECONDS
, а только один раз. - Функция
glutPostRedisplay()
запрашивает GLUT для рендеринга нового кадра, поэтому мы должны вызывать его каждый раз, когда мы что-то меняем в сцене. glutIdleFunc(renderScene)
можно использовать для регистрации обратного вызова на renderScene()
(это не делает glutDisplayFunc()
неактуальным), но этой функции следует избегать, поскольку простой обратный вызов непрерывно вызывается, когда события не принимаются, увеличиваязагрузка процессора. - Функция
glutGet(GLUT_ELAPSED_TIME)
возвращает количество миллисекунд с момента вызова glutInit
(или первого вызова glutGet(GLUT_ELAPSED_TIME)
).Это таймер у нас с GLUT.Я знаю, что есть лучшие альтернативы для таймеров с высоким разрешением, но давайте пока остановимся на этом.
Я думаю, что это достаточно информации о том, как GLUT отображает кадры, чтобы люди, которые не знали об этом, моглитакже задайте этот вопрос, чтобы попытаться помочь, если они упали, как это.
Текущая реализация:
Теперь я не уверен, что правильно выполнил второе решениеПредложенный Коеном Скорость игры зависит от переменной FPS .Соответствующий код выглядит следующим образом:
#define TICKS_PER_SECOND 30
#define MOVEMENT_SPEED 2.0f
const int TIMER_MILLISECONDS = 1000 / TICKS_PER_SECOND;
int previousTime;
int currentTime;
int elapsedTime;
void renderScene(void) {
(...)
// Setup the camera position and looking point
SceneCamera.LookAt();
// Do all drawing below...
(...)
}
void processAnimationTimer(int value) {
// setups the timer to be called again
glutTimerFunc(TIMER_MILLISECONDS, processAnimationTimer, 0);
// Get the time when the previous frame was rendered
previousTime = currentTime;
// Get the current time (in milliseconds) and calculate the elapsed time
currentTime = glutGet(GLUT_ELAPSED_TIME);
elapsedTime = currentTime - previousTime;
/* Multiply the camera direction vector by constant speed then by the
elapsed time (in seconds) and then move the camera */
SceneCamera.Move(cameraDirection * MOVEMENT_SPEED * (elapsedTime / 1000.0f));
// Requests to render a new frame (this will call my renderScene() once)
glutPostRedisplay();
}
void main(int argc, char **argv) {
glutInit(&argc, argv);
(...)
glutDisplayFunc(renderScene);
(...)
// Setup the timer to be called one first time
glutTimerFunc(TIMER_MILLISECONDS, processAnimationTimer, 0);
// Read the current time since glutInit was called
currentTime = glutGet(GLUT_ELAPSED_TIME);
glutMainLoop();
}
Эта реализация не подходит.Это работает в том смысле, что скорость игры постоянно зависит от FPS.Таким образом, перемещение из точки A в точку B занимает одно и то же время, независимо от высокой / низкой частоты кадров.Тем не менее, я считаю, что я ограничиваю частоту кадров при таком подходе.[ РЕДАКТИРОВАТЬ: Каждый кадр будет отображаться только при вызове обратного вызова времени, это означает, что частота кадров будет примерно около TICKS_PER_SECOND
кадров в секунду.Это не правильно, вы не должны ограничивать свое мощное оборудование, это неправильно.Однако я понимаю, что мне все еще нужно вычислить elapsedTime
.Просто потому, что я говорю GLUT, чтобы он вызывал таймер обратного вызова каждые TIMER_MILLISECONDS
, это не значит, что он всегда будет делать это вовремя.]
Я не уверен, как я могу это исправить и бытьсовершенно честно, я понятия не имею, что такое игровой цикл в GLUT, вы знаете, цикл while( game_is_running )
в статье Коэна.[ EDIT: Насколько я понимаю, GLUT управляется событиями , и этот игровой цикл начинается, когда я звоню glutMainLoop()
(который никогда не возвращается), да?]
Я думал, что смогу зарегистрировать неактивный обратный вызов с glutIdleFunc()
и использовать его вместо glutTimerFunc()
, только рендеринг при необходимости (вместо обычного времени), но когда я проверял это с пустым обратным вызовом (как void gameLoop() {}
)и он в основном ничего не делал, только черный экран, процессор ускорился до 25% и оставался там до тех пор, пока я не убил игру, и она не пришла в норму.Поэтому я не думаю, что это путь для подражания.
Использование glutTimerFunc()
определенно не является хорошим подходом для выполнения всех движений / анимаций, основанных на этом, поскольку я ограничиваю свою игру постоянным FPS, а не круто.Или, может быть, я использую это неправильно, и моя реализация не так?
Как именно я могу иметь постоянную скорость игры с переменной FPS?Точнее, как правильно реализовать решение Koen Constant Game Speed с максимальным FPS (четвертое в его статье) с GLUT?Может быть, это невозможно вообще с GLUT?Если нет, каковы мои альтернативы? Каков наилучший подход к этой проблеме (постоянная скорость игры) с GLUT?
[EDIT] Другой подход:
Я былэкспериментировал и вот что я смог достичь сейчас.Вместо того, чтобы вычислять истекшее время по временной функции (которая ограничивает частоту кадров моей игры), я делаю это в renderScene()
.Всякий раз, когда происходят изменения в сцене, я называю glutPostRedisplay()
(то есть: перемещение камеры, некоторая анимация объекта и т. Д.), Который вызывает renderScene()
.Я могу использовать истекшее время в этой функции для перемещения моей камеры, например.
Мой код теперь превратился в это:
int previousTime;
int currentTime;
int elapsedTime;
void renderScene(void) {
(...)
// Setup the camera position and looking point
SceneCamera.LookAt();
// Do all drawing below...
(...)
}
void renderScene(void) {
(...)
// Get the time when the previous frame was rendered
previousTime = currentTime;
// Get the current time (in milliseconds) and calculate the elapsed time
currentTime = glutGet(GLUT_ELAPSED_TIME);
elapsedTime = currentTime - previousTime;
/* Multiply the camera direction vector by constant speed then by the
elapsed time (in seconds) and then move the camera */
SceneCamera.Move(cameraDirection * MOVEMENT_SPEED * (elapsedTime / 1000.0f));
// Setup the camera position and looking point
SceneCamera.LookAt();
// All drawing code goes inside this function
drawCompleteScene();
glutSwapBuffers();
/* Redraw the frame ONLY if the user is moving the camera
(similar code will be needed to redraw the frame for other events) */
if(!IsTupleEmpty(cameraDirection)) {
glutPostRedisplay();
}
}
void main(int argc, char **argv) {
glutInit(&argc, argv);
(...)
glutDisplayFunc(renderScene);
(...)
currentTime = glutGet(GLUT_ELAPSED_TIME);
glutMainLoop();
}
Вывод, он работает, или так кажется.Если я не перемещаю камеру, загрузка процессора низкая, ничего не отображается (для целей тестирования у меня есть только сетка, растягивающаяся на 4000.0f, в то время как zFar установлен на 1000.0f).Когда я начинаю двигать камеру, сцена начинает перерисовываться.Если я продолжу нажимать клавиши перемещения, загрузка процессора увеличится;это нормальное поведение.Когда я перестаю двигаться, он падает обратно.
Если я что-то не упустил, пока это кажется хорошим подходом.Я нашел эту интересную статью на iDevGames, и на эту реализацию, вероятно, влияет проблема, описанная в этой статье.Что вы думаете об этом?
Обратите внимание, что я просто делаю это для развлечения, у меня нет намерений создавать какую-то игру для распространения или что-то в этом роде, по крайней мере, в ближайшем будущем.Если бы я это сделал, я бы, вероятно, пошел с чем-то еще, кроме GLUT.Но так как я использую GLUT, а не проблему, описанную в iDevGames, вы считаете, что этой последней реализации достаточно для GLUT?Единственная реальная проблема, о которой я могу думать сейчас, это то, что мне нужно будет продолжать звонить glutPostRedisplay()
каждый раз, когда сцена меняет что-то, и продолжать вызывать это, пока не будет ничего нового для перерисовки.Я думаю, что в код добавлена небольшая сложность для лучшего дела.
Что вы думаете?