Постоянная скорость игры Независимо от переменной FPS в OpenGL с GLUT?

Я читал подробную статью Коена Виттерса о различных решениях игровых циклов, но у меня возникли некоторые проблемы с реализацией последнего с помощью 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) в статье Коэна. [ РЕДАКТИРОВАТЬ: Я так понимаю, что GLUT управляемый событиями и этот игровой цикл начинается, когда я вызываю glutMainLoop () (который никогда не возвращается), да?]

Я думал, что могу зарегистрировать неактивный обратный вызов с помощью glutIdleFunc () и использовать его как замену glutTimerFunc () , рендеринг только при необходимости (а не всегда, как обычно), но когда я тестировал это с пустым обратным вызовом (например, void gameLoop () {} ), он практически ничего не делал, только черный экран, ЦП поднялся до 25% и оставался там, пока я не убил игру и она не вернулась в нормальное состояние. Так что я не думаю, что по этому пути следует идти.

Использование glutTimerFunc () определенно не лучший подход для выполнения всех движений / анимаций на основе этого, поскольку я ограничиваю свою игру постоянный FPS, не круто. Или, может быть, я неправильно его использую и моя реализация неверна?

Как именно я могу добиться постоянной скорости игры с переменным FPS? Точнее как правильно реализовать Koen ' s Постоянная скорость игры с максимальным FPS решение (четвертое в его статье) с GLUT? Может это вообще невозможно с GLUT? Если нет, то каковы мои альтернативы? Как лучше всего решить эту проблему (постоянная скорость игры) с GLUT?

[РЕДАКТИРОВАТЬ] Другой подход:

Я экспериментировал, и вот что я смог достичь сейчас. Вместо вычисления прошедшего времени с помощью временной функции (которая ограничивает частоту кадров моей игры) я теперь делаю это в 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 () каждый раз, когда сцена что-то меняет, и продолжать вызывать ее, пока не останется ничего нового для перерисовки. Я думаю, что для лучшего случая к коду добавилась небольшая сложность.

Что вы думаете?

8
задан Ricardo Amaral 18 March 2011 в 23:49
поделиться