我一直在读科恩·维特斯详细文章 http://www.koonsolo.com/news/dewitters-gameloop/关于不同的游戏循环解决方案,但我在使用 GLUT 实现最后一个解决方案时遇到了一些问题,这是推荐的解决方案。
在阅读了其他人关于如何实现恒定游戏速度的几篇文章、教程和代码后,我认为我目前已经实现的(我将在下面发布代码)就是 Koen Witters 所说的游戏速度取决于可变 FPS,他的文章中的第二个。
首先,根据我的搜索经验,有一些人可能有知识可以帮助解决这个问题,但不知道什么是 GLUT,我将尝试解释(请随时纠正我)相关功能我的这个OpenGL工具包的问题。如果您知道什么是 GLUT 以及如何使用它,请跳过本节。
过剩工具包:
- GLUT 是一个 OpenGL 工具包,可帮助完成 OpenGL 中的常见任务。
- The
glutDisplayFunc(renderScene)
接受一个指向 a 的指针renderScene()
函数回调,它将负责渲染所有内容。这renderScene()
回调函数注册后只会被调用一次。
- The
glutTimerFunc(TIMER_MILLISECONDS, processAnimationTimer, 0)
调用回调之前经过的毫秒数processAnimationTimer()
。最后一个参数只是传递给计时器回调的值。这processAnimationTimer()
不会被称为每个TIMER_MILLISECONDS
但只有一次。
- The
glutPostRedisplay()
函数请求 GLUT 渲染一个新帧,因此每次我们更改场景中的某些内容时都需要调用它。
- The
glutIdleFunc(renderScene)
可用于注册回调renderScene()
(这并不使glutDisplayFunc()
不相关),但应避免使用此函数,因为在未接收到事件时会不断调用空闲回调,从而增加 CPU 负载。
- The
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 点所需的时间都是相同的。然而,我相信我用这种方法限制了游戏帧率。 [EDIT:仅当调用时间回调时才会渲染每一帧,这意味着帧率将大致约为TICKS_PER_SECOND
每秒帧数。这感觉不对,你不应该限制你强大的硬件,这是错误的。但据我了解,我仍然需要计算elapsedTime
。只是因为我告诉 GLUT 调用计时器回调TIMER_MILLISECONDS
,这并不意味着它总是会按时完成。]
我不知道如何解决这个问题,说实话,我不知道 GLUT 中的游戏循环是什么,你知道,while( game_is_running )
Koen 的文章中循环。 [EDIT:我的理解是 GLUT 是事件驱动当我打电话时游戏循环就开始了glutMainLoop()
(永远不会回来),是吗?]
我想我可以注册一个空闲回调glutIdleFunc()
并用它代替glutTimerFunc()
,仅在必要时渲染(而不是像往常一样一直渲染),但是当我用空回调测试它时(例如void gameLoop() {}
),它基本上什么也没做,只有黑屏,CPU 飙升至 25%,并一直保持在那里,直到我杀死游戏,它才恢复正常。所以我不认为这是应该遵循的道路。
Using glutTimerFunc()
绝对不是基于此执行所有运动/动画的好方法,因为我将游戏限制为恒定的 FPS,这并不酷。或者也许我使用错误并且我的实现不正确?
究竟如何才能在可变 FPS 的情况下保持恒定的游戏速度?更准确地说,我如何正确实施 Koen 的恒定的游戏速度和最大 FPSGLUT 的解决方案(他文章中的第四个)?也许对于 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使用率会增加;这是正常行为。当我停止移动时,它会回落。
除非我遗漏了什么,否则目前看来这是一个不错的方法。我确实找到了这篇有趣的文章 http://www.idevgames.com/articles/timebasedanimation在 iDevGames 上,此实现可能受到该文章中描述的问题的影响。你对此有何看法?
请注意,我这样做只是为了好玩,我无意创建一些游戏来分发或类似的东西,至少在不久的将来不会。如果我这样做,我可能会选择除了 GLUT 之外的其他东西。但由于我使用的是 GLUT,除了 iDevGames 上描述的问题之外,您认为这个最新的实现足以应对 GLUT 吗?我现在能想到的唯一真正的问题是我需要继续打电话glutPostRedisplay()
每当场景发生变化时,就不断调用它,直到没有新的内容可以重画为止。我认为,为了更好的目的而在代码中添加一点复杂性。
你怎么认为?