我一直在阅读有关不同游戏循环解决方案的Koen Witters detailed article,但是我在使用GLUT实现最后一个方面遇到了一些问题,这是推荐的解决方案。
在阅读过其他一些关于如何实现恒定游戏速度的文章,教程和代码后,我认为我目前实现的(我将在下面的代码中发布)就是Koen Witters所说的依赖于可变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()函数中,而不要放在update函数中。自然地,您的更新函数将按照计时器指定的速度被调用,并且您将elapsedTime视为帧之间时间的度量。通常,这是正确的,因为您调用glutPostRedisplay的速度很慢,并且在不需要时glut不会尝试更新屏幕(如果场景未更改,则无需重绘)。但是,有时还会调用renderScene。例如,如果您在窗口上拖动某些东西。如果这样做的话,您会看到更高的FPS(如果您在渲染功能中正确跟踪了FPS)。
关于c - 恒定的游戏速度与GLUT在OpenGL中与可变FPS无关吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5357119/