我一直在阅读 Koen Witters关于不同游戏循环解决方案的详细文章,但我在使用 GLUT 实现最后一篇时遇到了一些问题,这是推荐的一篇。
在阅读了其他人关于如何实现恒定游戏速度的几篇文章、教程和代码之后,我认为我目前已经实现的(我将在下面发布代码)是 Koen Witters 所说的基于可变 FPS 的游戏速度,他的文章的第二个。
首先,通过我的搜索经验,有几个人可能有知识可以帮助解决这个问题,但不知道 GLUT 是什么,我将尝试解释(请随时纠正我)相关功能我对这个 OpenGL 工具包的问题。如果您知道 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)
以来的毫秒数。这就是我们使用 GLUT 的计时器。我知道高分辨率计时器有更好的选择,但现在让我们继续使用这个。glutInit
glutGet(GLUT_ELAPSED_TIME)
我认为这是关于 GLUT 如何渲染帧的足够信息,因此不了解它的人也可以提出这个问题,以便在他们喜欢它时尝试提供帮助。
当前实施:
现在,我不确定我是否正确实施了 Koen 提出的第二个解决方案,即Game Speed 取决于 Variable 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 调用计时器回调 every TIMER_MILLISECONDS
,并不意味着它总是会准时这样做。]
我不确定如何解决这个问题,老实说,我不知道 GLUT 中的游戏循环是什么,你知道,while( game_is_running )
Koen 文章中的循环。[编辑:我的理解是 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 上找到了这篇有趣的文章,并且此实现可能受到该文章中描述的问题的影响。你对此有何看法?
请注意,我这样做只是为了好玩,我无意创建一些游戏来分发或类似的东西,至少在不久的将来不会。如果我这样做了,我可能会选择除了 GLUT 之外的其他东西。但是由于我使用的是 GLUT,除了 iDevGames 上描述的问题之外,你认为这个最新的实现对 GLUT 来说足够了吗?我现在唯一能想到的真正问题是,glutPostRedisplay()
每次场景发生变化时我都需要继续调用它,并一直调用它,直到没有新的东西可以重绘。我认为,为了更好的原因,为代码添加了一点复杂性。
你怎么看?