さまざまなゲームループソリューションに関するKoenWittersの詳細な記事を読んでいますが、推奨されるものであるGLUTを使用して最後のソリューションを実装する際に問題が発生しています。
一定のゲーム速度を達成する方法に関する他の人々からのいくつかの記事、チュートリアル、およびコードを読んだ後、私が現在実装しているもの(以下のコードを投稿します)は、KoenWittersが可変FPSに依存するゲーム速度と呼んだものだと思います、彼の記事の2番目。
まず、私の検索経験を通して、おそらくこれを支援する知識を持っているが、GLUTが何であるかを知らない人が何人かいます。私は、関連する機能を説明しようとします(私を修正してください)。このOpenGLツールキットの私の問題。GLUTとは何か、GLUTの操作方法を知っている場合は、このセクションをスキップしてください。
GLUT Toolkit:
- GLUTはOpenGLツールキットであり、OpenGLの一般的なタスクを支援します。
- は、すべてのレンダリングを担当する関数コールバック
glutDisplayFunc(renderScene)
へのポインタを取ります。renderScene()
このrenderScene()
関数は、コールバック登録後に1回だけ呼び出されます。 - は
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がフレームをレンダリングする方法についての十分な情報であると思います。そのため、GLUTについて知らなかった人も、この質問に答えて、気に入らなかった場合に助けてもらうことができます。
現在の実装:
さて、Koenによって提案された2番目のソリューションである可変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
1秒あたりのフレーム数になります。これは正しくないと感じます。強力なハードウェアを制限するべきではありません。間違っています。ただし、まだ計算する必要があることは私の理解ですelapsedTime
。GLUTにタイマーコールバックを毎回呼び出すように指示しているからといってTIMER_MILLISECONDS
、それが常に時間どおりに行われるとは限りません。]
どうすればこれを修正できるかわかりません。正直に言うと、GLUTのゲームループが何であるかわかりません。Koenwhile( game_is_running )
の記事のループです。[編集: GLUTはイベント駆動型であり、電話をかけるとゲームループが開始することを理解していますglutMainLoop()
(これは決して戻りません)、そうですか?]
アイドル状態のコールバックをに登録し、glutIdleFunc()
それをの代わりに使用してglutTimerFunc()
、必要な場合にのみレンダリングできると思いました(通常のように常にではなく)が、これを空のコールバック(のようにvoid gameLoop() {}
)でテストしたところ、基本的に何も行われず、画面が黒い場合、CPUは25%に急上昇し、ゲームを終了して通常に戻るまでそこに留まりました。だから私はそれがたどる道ではないと思います。
glutTimerFunc()
ゲームをクールではなく一定のFPSに制限しているため、使用することは、それに基づいてすべての動き/アニメーションを実行するための良いアプローチではありません。または、間違って使用していて、実装が正しくない可能性がありますか?
可変FPSで一定のゲーム速度をどの程度正確に得ることができますか?より正確には、GLUTを使用してKoenのConstant Game Speed with Maximum FPSソリューション(彼の記事の4番目のソリューション)を正しく実装するにはどうすればよいですか?たぶんこれは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()
、シーンが何かを変えるたびに電話をかけ続け、再描画する新しいものがなくなるまで電話をかけ続ける必要があるということです。より良い原因のためにコードに少し複雑さが加わったと思います。
どう思いますか?