1 秒あたりの表示フレーム数をゲーム ロジックから独立させるにはどうすればよいでしょうか? これは、ビデオ カードのレンダリング速度に関係なく、ゲーム ロジックが同じ速度で実行されるようにするためです。
8 に答える
この質問は、ゲーム エンジンがどのように設計されるべきかについての誤解を少し明らかにしていると思います。それらは正しく理解するのが難しい非常に複雑なものであるため、これはまったく問題ありません;)
フレーム レートの独立性と呼ばれるものが必要であるという正しい印象を受けています。ただし、これはレンダリング フレームだけを指すわけではありません。
シングル スレッド ゲーム エンジンのフレームは、一般にティックと呼ばれます。Tick ごとに、入力を処理し、ゲーム ロジックを処理し、処理結果に基づいてフレームをレンダリングします。
あなたがしたいことは、任意の FPS (1 秒あたりのフレーム数) でゲーム ロジックを処理し、決定論的な結果を得られるようにすることです。
これは、次の場合に問題になります。
入力の確認: - 入力はキー: 'W' です。これは、プレイヤー キャラクターを 10 ユニット前方に移動することを意味します。
playerPosition += 10;
これをすべてのフレームで実行しているため、30 FPS で実行している場合、1 秒あたり 300 単位移動します。
しかし、代わりに 10 FPS で実行している場合、1 秒あたり 100 ユニットしか移動しません。したがって、ゲーム ロジックはフレーム レートに依存しません。
幸いなことに、この問題を解決してゲーム プレイ ロジックをフレーム レートに依存しないようにするのは、かなり単純な作業です。
まず、各フレームのレンダリングにかかる時間をカウントするタイマーが必要です。この秒単位の数値 (Tick を完了するのに 0.001 秒) に、フレーム レート非依存にしたい値を掛けます。したがって、この場合:
「W」を押したとき
playerPosition += 10 * frameTimeDelta;
(デルタは「何かを変える」という意味の派手な言葉です)
したがって、プレーヤーは 1 ティックで 10 の一部を移動し、1 秒のティックの後、10 ユニット全体を移動したことになります。
ただし、加速する車両など、時間の経過とともに変化率も変化するプロパティに関しては、これは低下します。これは、「Verlet」などのより高度なインテグレーターを使用することで解決できます。
マルチスレッドアプローチ
あなたの質問への回答にまだ興味がある場合は (私は回答していませんが、代替案を提示しているため)、ここにあります。ゲーム ロジックとレンダリングを別のスレッドに分離します。ただし、それには欠点があります。大部分のゲーム エンジンがシングル スレッドのままであるのに十分です。
これは、いわゆるシングル スレッド エンジンで実行されるスレッドが 1 つだけであると言っているわけではありません。ただし、すべての重要なタスクは通常、1 つの中央スレッドにあります。衝突検出のようなものはマルチスレッド化されている場合がありますが、一般に、Tick の衝突フェーズはすべてのスレッドが戻るまでブロックされ、エンジンは単一の実行スレッドに戻ります。
マルチスレッディングは、非常に大きなクラスの問題全体を提示します。コンテナを含むすべてがスレッドセーフでなければならないため、一部のパフォーマンスの問題も含まれます。また、ゲーム エンジンは非常に複雑なプログラムであるため、マルチスレッド化による複雑さを追加する価値はほとんどありません。
固定時間ステップ アプローチ
最後に、別のコメンターが指摘したように、固定サイズの時間ステップを持ち、ゲーム ロジックを「ステップ」する頻度を制御することも、これを処理する非常に効果的な方法であり、多くの利点があります。
完全を期すためにここにリンクされていますが、他のコメント投稿者もそれにリンクしています: Fix Your Time Step
Koen Witters には、さまざまなゲーム ループのセットアップに関する非常に詳細な記事があります。
彼はカバーします:
- 一定のゲーム速度に依存する FPS
- 可変FPSに依存するゲーム速度
- 最大 FPS で一定のゲーム速度
- 可変 FPS に依存しない一定のゲーム速度
(これらは、望ましい順に、記事から引き出された見出しです。)
ゲーム ループは次のようになります。
int lastTime = GetCurrentTime();
while(1) {
// how long is it since we last updated?
int currentTime = GetCurrentTime();
int dt = currentTime - lastTime;
lastTime = currentTime;
// now do the game logic
Update(dt);
// and you can render
Draw();
}
次にUpdate()
、時間差を考慮して関数を作成するだけです。たとえば、ある速度で移動するオブジェクトがある場合、フレームごとにv
その位置を更新します。v * dt
これについては、昔、フリップコードに関する優れた記事がありました。掘り下げてご紹介したいと思います。
http://www.flipcode.com/archives/Main_Loop_with_Fixed_Time_Steps.shtml
これは、ゲームを実行するためのよく考えられたループです。
- シングルスレッド
- 一定のゲーム クロックで
- 補間されたクロックを使用して可能な限り高速なグラフィックスで
まあ、少なくとも私はそう思います。:-) 残念ながら、この投稿の後に追求された議論は見つけにくいです。おそらく、ウェイバックマシンが役立つでしょう。
time0 = getTickCount();
do
{
time1 = getTickCount();
frameTime = 0;
int numLoops = 0;
while ((time1 - time0) TICK_TIME && numLoops < MAX_LOOPS)
{
GameTickRun();
time0 += TICK_TIME;
frameTime += TICK_TIME;
numLoops++;
// Could this be a good idea? We're not doing it, anyway.
// time1 = getTickCount();
}
IndependentTickRun(frameTime);
// If playing solo and game logic takes way too long, discard pending
time.
if (!bNetworkGame && (time1 - time0) TICK_TIME)
time0 = time1 - TICK_TIME;
if (canRender)
{
// Account for numLoops overflow causing percent 1.
float percentWithinTick = Min(1.f, float(time1 - time0)/TICK_TIME);
GameDrawWithInterpolation(percentWithinTick);
}
}
while (!bGameDone);
グラフィックを表示する前に時間遅延のあるシングル スレッド ソリューションは問題ありませんが、プログレッシブな方法は、1 つのスレッドでゲーム ロジックを実行し、別のスレッドで表示することだと思います。
ただし、スレッドを正しく同期する必要があります ;) 実装には時間がかかるため、ゲームが大きすぎない場合は、シングルスレッド ソリューションで問題ありません。
また、GUI を別のスレッドに抽出することは、優れたアプローチのようです。RTS ゲームでユニットが動き回っているときに「ミッション完了」のポップアップ メッセージを見たことがありますか? それは私が話しているものです :)
Enginuityには、少し異なるが興味深いアプローチがあります。タスク プールです。
これは、より高度なプログラムの抽象化、つまりステートマシンなどをカバーしていません。
フレーム タイム ラプスでそれらを調整して、動きと加速を制御しても問題ありません。しかし、2.55 秒後にサウンドをトリガーしたり、18.25 秒後にゲーム レベルを変更したりなどはどうでしょうか。
これは経過フレーム時間アキュムレータ (カウンター) に結び付けることができますが、フレーム レートがステート スクリプトの解像度を下回る場合、つまり、より高いロジックで 0.05 秒の粒度が必要で 20fps を下回る場合、これらのタイミングが台無しになる可能性があります。
ゲームロジックが別の「スレッド」(私がこれを好むソフトウェアレベル、またはOSレベル)で実行され、fpsに依存しない固定タイムスライスで実行される場合、決定論を維持できます。
ペナルティは、あまり起こっていない場合、フレーム間で CPU 時間を浪費する可能性があることかもしれませんが、おそらくそれだけの価値があると思います。
私の経験から(それほど多くはありません)、ジェシーとアダムの答えはあなたを正しい軌道に乗せるはずです。
これがどのように機能するかについてさらに詳しい情報と洞察を得たい場合は、TrueVision 3Dのサンプル アプリケーションが非常に役立つことがわかりました。