恒定的游戏速度独立于OpenGL与GLUT中的可变FPS?

我一直在阅读关于不同游戏循环解决方案的Koen Witters detailed article,但是我在使用GLUT实现最后一个时遇到了一些问题,这是推荐的.

在阅读了几篇关于如何实现恒定游戏速度的文章,教程和代码之后,我认为我目前已实现的内容(我将在下面发布代码)是Koen Witters所谓的Game Speed依赖于可变FPS ,第二篇关于他的文章.

首先,通过我的搜索经验,有几个人可能有帮助,但不知道什么是GLUT,我将尝试解释(随意纠正我)相关的功能我的这个OpenGL工具包的问题.如果您知道GLUT是什么以及如何使用它,请跳过此部分.

GLUT工具包:

> GLUT是一个OpenGL工具包,可以帮助完成OpenGL中的常见任务.
> glutDisplayFunc(renderScene)接受一个指向renderScene()函数回调的指针,该回调将负责渲染所有内容. renderScene()函数仅在回调注册后调用一次.
> glutTimerFunc(TIMER_MILLISECONDS,processAnimationTimer,0)在调用回调processAnimationTimer()之前需要经过的毫秒数.最后一个参数只是一个传递给计时器回调的值. processAnimationTimer()不会在每个TIMER_MILLISECONDS中调用,而只需调用一次.
> glutPostRedisplay()函数请求GLUT渲染一个新帧,所以每当我们改变场景中的某些东西时我们都需要调用它.
> glutIdleFunc(renderScene)可用于向renderScene()注册回调(这不会使glutDisplayFunc()无关)但应避免使用此函数,因为在未接收到事件时会持续调用空闲回调,从而增加CPU加载.
> glutGet(GLUT_ELAPSED_TIME)函数返回自调用glutInit(或首次调用glutGet(GLUT_ELAPSED_TIME))以来的毫秒数.这是我们与GLUT的计时器.我知道高分辨率计时器还有更好的选择,但是现在让我们继续使用这个计时器.

我认为这是关于GLUT如何渲染帧的足够信息所以那些不了解它的人也可以投入这个问题来试着帮助它们如果它们像它一样堕落.

目前的实施:

现在,我不确定我是否正确实施了Koen提出的第二种解决方案,游戏速度依赖于可变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中的游戏循环是什么,你知道,Koen的文章中的while(game_is_running)循环. [编辑:我的理解是GLUT是事件驱动的,当我调用glutMainLoop()(它永远不会返回)时,游戏循环开始,是吗?]

我以为我可以使用glutIdleFunc()注册一个空闲回调并使用它作为glutTimerFunc()的替换,只在必要时呈现(而不是像往常一样)但是当我用空回调测试它时(如void gameLoop()) {})它基本上什么都不做,只有一个黑屏,CPU飙升到25%并保持在那里直到我杀死游戏并恢复正常.所以我不认为这是要遵循的道路.

使用glutTimerFunc()绝对不是一个很好的方法来执行基于此的所有动作/动画,因为我将我的游戏限制为恒定的FPS,而不是很酷.或者也许我使用它错了,我的实现不对?

如何通过可变FPS获得恒定的游戏速度?更准确地说,我如何使用GLUT正确实施Koen的恒定游戏速度和最大FPS解决方案(他的文章中的第四个)?也许这对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();
}

结论,它似乎正在发挥作用.如果我不移动相机,CPU使用率很低,没有任何渲染(出于测试目的,我只有一个网格扩展为4000.0f,而zFar设置为1000.0f).当我开始移动相机时,场景开始重绘.如果我一直按下移动键,CPU使用率会增加;这是正常的行为.当我停止移动时它会回落.

除非我遗漏了什么,否则现在似乎是一个好方法.我确实在iDevGames上找到了this interesting article,这个实现可能会受到该文章中描述的问题的影响.你对此有何看法?

请注意,我只是为了好玩而这样做,我无意创建一些游戏来分发或类似的东西,至少在不久的将来.如果我这样做,除了GLUT之外,我可能会选择其他的东西.但是因为我正在使用GLUT,而不是iDevGames上描述的问题,你认为这个最新的实现对于GLUT来说已经足够吗?我现在能想到的唯一真正的问题是,每当场景发生变化时我都需要继续调用glutPostRedisplay()并继续调用它,直到没有什么新的重绘方式.我认为,为了更好的原因,为代码增加了一点复杂性.

你怎么看?

过剩是设计成游戏循环.当你调用glutMainLoop()时,它会执行’for循环’,除了exit()信号之外没有终止条件.您可以像现在一样实现您的程序,但是您需要进行一些小的更改.首先,如果您想知道FPS是什么,您应该将该跟踪放入renderScene()函数中,而不是放在更新函数中.当然,您的更新函数的调用速度与计时器指定的一样快,您可以将elapsedTime视为帧之间时间的度量.一般来说,这是真的,因为你正在调用glutPostRedisplay而且如果它不需要则不会尝试更新屏幕(如果场景没有改变则不需要重绘).但是,还有其他时候会调用renderScene.例如,如果您在窗口中拖动某些内容.如果你这样做,你会看到更高的FPS(如果你在渲染功能中正确跟踪FPS).
相关文章
相关标签/搜索