OpenGLでゲームループを構築する良い方法


31

現在、私は学校でOpenGLを学び始めており、先日(学校ではなく自分で)簡単なゲームを作り始めました。私はfreeglutを使用しており、Cで構築しているため、ゲームループでは、渡された関数を使用して、glutIdleFuncすべての描画と物理を1回のパスで更新していました。これは、フレームレートをあまり気にしないシンプルなアニメーションでは問題ありませんでしたが、ゲームはほとんど物理ベースであるため、更新の速度を抑える必要があります。

そのため、私の最初の試みは、関数をglutIdleFuncmyIdle())に渡して、前回の呼び出しから経過した時間を追跡し、物理(および現在のグラフィックス)を数ミリ秒ごとに更新することでした。timeGetTime()(これを使用して)これを実行していました<windows.h>。そして、これは私に考えさせられました、アイドル機能を使用することは本当にゲームループを進める良い方法ですか?

私の質問は、OpenGLでゲームループを実装するより良い方法は何ですか?アイドル機能の使用を避けるべきですか?



私はこの質問は、OpenGLに、より具体的で、forループGLUTを使用することが賢明であるかどうかを感じる
ジェフ

1
ここでは、大きなプロジェクトに GLUT 使用しないでください。ただし、小さいものには問題ありません。
ボボボボ

回答:


29

簡単な答えは「いいえ」です。何らかのシミュレーションがあるゲームでは、glutIdleFuncコールバックを使用したくありません。この理由は、この関数がアニメーションを離婚し、ウィンドウイベント処理からコードを描画しますが、非同期ではないためです。言い換えると、ウィンドウイベントのストールを受け取って処理すると、コード(またはこのコールバックに入れるもの)が描画されます。これは、対話型アプリケーション(インタラクション、応答)にはまったく問題ありませんが、物理学またはゲームの状態が進行する必要があるゲームには適していません相互作用やレンダリング時間に依存しません。

入力処理、ゲームの状態、描画コードを完全に分離したい場合。これには、グラフィックライブラリを直接使用しない、簡単でクリーンなソリューションがあります(つまり、ポータブルで視覚化が容易です)。ゲームループ全体で時間を生成し、シミュレーションで生成された時間を(チャンク単位で)消費するようにします。ただし、重要なのは、シミュレーションで消費した時間をアニメーションに統合することです。

これについて私が見つけた最良の説明とチュートリアルは、グレンフィードラーの「タイムステップを修正する」です。

このチュートリアルには完全な処理が含まれていますが、実際の物理シミュレーションがない場合は、真の統合をスキップできますが、基本的なループは(詳細な擬似コードで)続きます:

// The amount of time we want to simulate each step, in milliseconds
// (written as implicit frame-rate)
timeDelta = 1000/30
timeAccumulator = 0
while ( game should run )
{
  timeSimulatedThisIteration = 0
  startTime = currentTime()

  while ( timeAccumulator >= timeDelta )
  {
    stepGameState( timeDelta )
    timeAccumulator -= timeDelta
    timeSimulatedThisIteration += timeDelta
  }

  stepAnimation( timeSimulatedThisIteration )

  renderFrame() // OpenGL frame drawing code goes here

  handleUserInput()

  timeAccumulator += currentTime() - startTime 
}

このようにすることで、レンダリングコード、入力処理、またはオペレーティングシステムのストールによってゲームの状態が遅れることはありません。このメソッドは、移植可能であり、グラフィックスライブラリに依存しません。

GLUTは優れたライブラリですが、厳密にイベント駆動型です。コールバックを登録し、メインループを起動します。GLUTを使用して、メインループの制御を常に引き渡します。それを回避するためのハックがあり、タイマーなどを使用して外部ループを偽造することもできますが、おそらく別のライブラリがより良い(より簡単な)方法です。多くの選択肢がありますが、ここにいくつかあります(優れたドキュメントとクイックチュートリアルを含むもの)。

  • GLFWは、入力イベントをインラインで(独自のメインループで)取得する機能を提供します。
  • SDLですが、その重点は特にOpenGLではありません。

良い記事。OpenGLでこれを行うには、glutMainLoopを使用せず、代わりに独自のものを使用しますか?それをした場合、glutMouseFuncやその他の機能を使用できますか?それが理にかなっていることを願っています、私はまだこれすべてにかなり新しいです
ジェフ

残念ながら違います。GLUTに登録されたすべてのコールバックは、イベントキューが空になり、繰り返されると、glutMainLoop内から起動されます。回答本文に代替案へのリンクをいくつか追加しました。
チャースター

1
GLUTを他のものに捨てる場合は+1。私の知る限り、GLUTはテストアプリケーション以外のものには使用できません。
ヤリコンパ

それでもシステムイベントを処理する必要はありますか?私の理解では、glutIdleFuncを使用することで、(Windowsで)GetMessage-TranslateMessage-DispatchMessageループを処理し、取得するメッセージがない場合は常に関数を呼び出します。とにかく自分でそれを行うか、アプリケーションが応答しないというフラグを立てることにOSの焦りを感じますか?
リケット

@Ricket技術的には、glutMainLoopEvent()は登録されたコールバックを呼び出すことで着信イベントを処理します。glutMainLoop()は事実上{glutMainLoopEvent(); IdleCallback(); ここで重要な考慮事項は、再描画はイベントループ内から処理されるコールバックでもあるということです。イベント駆動型ライブラリを時間駆動型ライブラリのように動作させることはできます、MaslowのHammerとその他すべて
...-charstar

4

glutIdleFuncは大丈夫だと思う; 基本的には、使用しなかった場合、結局手作業で行うことになります。固定パターンで呼び出されないタイトループが何をしようとしても、それを固定時間ループに変換するか、可変時間ループにして保持するかを選択する必要があります補間時間のすべての数学の説明を確認してください。またはこれらの混合物でさえ、どういうわけか。

あなたは確かにあなたがにmyIdle渡す関数を持つことができglutIdleFunc、その中で、経過した時間を測定し、それをあなたの固定タイムステップで割って、何度も別の関数を呼び出します。

ここで何しませんし。

void myFunc() {
    // (calculate timeDifference here)
    int numOfTimes = timeDifference / 10;
    for(int i=0; i<numOfTimes; i++) {
        fixedTimestep();
    }
}

fixedTimestepは10ミリ秒ごとに呼び出されるわけではありません。実際、それはリアルタイムよりも遅く実行され、実際に問題の根本がtimeDifference10で割ったときの残りの損失にある場合、補正するために数字を混乱させる可能性があります。

代わりに、次のようなことをしてください。

long queuedMilliseconds; // static or whatever, this must persist outside of your loop

void myFunc() {
    // (calculate timeDifference here)
    queuedMilliseconds += timeDifference;
    while(queuedMilliseconds >= 10) {
        fixedTimestep();
        queuedMilliseconds -= 10;
    }
}

現在、このループ構造により、fixedTimestep残りのミリ秒などを失うことなく、10ミリ秒に1回関数が呼び出されます。

オペレーティングシステムのマルチタスクの性質により、10ミリ秒ごとに呼び出される関数に依存することはできません。しかし、上記のループは十分に速く発生するので、うまくいけば十分に近くなり、の各呼び出しはfixedTimestepシミュレーションを10ミリ秒の値だけ増やすと仮定できます。

この回答、特に回答に記載されているリンクもお読みください。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.