ほとんどのゲーム開発にはメイン ゲーム ループが必要ですが、なぜそれが必要なのかわかりません。イベントリスナーを実装して、すべてのユーザーアクションに応答できないでしょうか? イベントが発生したときにアニメーション (など) を再生できます。
メイン ゲーム ループの目的は何ですか?
ほとんどのゲーム開発にはメイン ゲーム ループが必要ですが、なぜそれが必要なのかわかりません。イベントリスナーを実装して、すべてのユーザーアクションに応答できないでしょうか? イベントが発生したときにアニメーション (など) を再生できます。
メイン ゲーム ループの目的は何ですか?
「そうでなければイベントリスナーを呼び出すため、ループが必要だ」という議論は水を保持しません。確かに、どのメインストリーム OS でも、実際にそのようなループがあり、イベント リスナーはそのように動作しますが、いかなる種類のループもなしに動作する割り込み駆動型システムを作成することは完全に可能です。
しかし、それでもゲームをそのように構成したくないでしょう。
ループを最も魅力的な解決策にするのは、ループがリアルタイム プログラミングで「巡回エグゼクティブ」と呼ばれるものになることです。これは、さまざまなシステム アクティビティの相対的な実行率を相互に決定論的にすることができるという考え方です。ループの全体的な速度はタイマーによって制御される場合があり、そのタイマーは最終的には割り込みになる可能性がありますが、最新の OS では、セマフォ (またはその他の同期メカニズム) を待機するコードとしてその割り込みの証拠が見られる可能性があります。 「メインループ」の一部。
では、なぜ決定論的な振る舞いが必要なのでしょうか? ユーザーの入力と悪役 AI の相対的な処理速度を考慮してください。すべてを純粋なイベントベースのシステムに入れる場合、AI がユーザーよりも多くの CPU 時間を取得しないという保証はありません。また、スレッドの優先度をある程度制御できない限り、その逆もありません。タイミングを一定に保つのが難しい傾向があります。
ただし、すべてをループに入れると、AI のタイムラインがユーザーの時間に対して一定の関係で進行することが保証されます。これは、AI に何をすべきかを決定するタイムスライスを与えるためにループから呼び出しを行い、ユーザー入力ルーチンを呼び出し、入力デバイスをポーリングしてユーザーがどのように行動したいかを調べることによって達成されます。あなたのレンダリングを行うために呼び出します。
このようなループでは、実際にリアルタイムで経過するよりも各パスの処理に時間がかからないように注意する必要があります。ループを 100Hz で循環させようとしている場合、ループのすべての処理は 10msec 未満で終了する必要があります。そうしないと、システムがぎくしゃくしてしまいます。リアルタイム プログラミングでは、タイム フレームのオーバーランと呼ばれます。優れたシステムでは、オーバーランにどれだけ近づいているかを監視できるため、必要に応じて処理負荷を軽減できます。
イベントリスナーは、表示されるかどうかに関係なく、呼び出しループにも依存しています。他に誰がリスナーに電話しますか?
明示的なゲーム ループを構築すると、何が起こっているかを完全に制御できるため、ツールキット/イベント処理ライブラリがイベント ループで行うことに依存することはありません。
ゲームループ (非常に単純化したものは次のとおりです)
初期化する 行う 入力 アップデート 与える ループ 掃除
これは、ゲームが描画されるすべてのフレームで発生します。したがって、60fps で実行されるゲームの場合、上記は毎秒 60 回実行されます。
これは、ゲームがスムーズに実行され、ゲームの同期が維持され、サイクルごとの更新/描画が十分に頻繁に行われることを意味します。アニメーションは単に目の錯覚です。オブジェクトは場所間を移動しますが、十分に速く再生すると、これらの場所間を移動しているように見えます。
ユーザー入力のみで更新する場合、ゲームはユーザーが入力を提供している場合にのみ反応します。AI ゲーム オブジェクトなどの他のゲーム コンポーネントは、単独では反応しません。したがって、ループはゲームを更新する最も簡単で最良の方法です。
すべての種類のゲームが専用のメインゲームループを必要とするというのは真実ではありません。
アクションゲームでは、オブジェクトが頻繁に更新され、ゲームの入力精度が高いため、このようなループが必要です。
一方、マインスイーパゲームを実装
し、通知にウィンドウメッセージを使用しました。
これは、現在のオペレーティングシステムが完全にイベントベースではないためです。多くの場合、イベントとして表されますが、次のイベントを待機して無期限に処理するループを作成する必要があります(例として、Windowsイベントループ)。Unixシグナルは、おそらくOSレベルのイベントに最も近いものですが、このような場合には十分に効率的ではありません。
実際には、他の人が指摘したように、ループが必要です。
しかし、あなたの考えは理論的には健全です。ループは必要ありません。イベントベースの操作が必要です。
簡単なレベルでは、CPU が複数のタイマーを持つように概念化できます。
もちろん、これらのタイマーは調整できます。
実際には、何が起こるかというと、次のようになります (多少省略されます)。
void int_handler1();
//...
int main()
{
//install interrupt handlers
//configure settings
while(1);
}
ゲームの性質は、通常はシミュレーションであり、外部イベントだけでなく内部プロセスにも基づいて反応するということです。これらの内部プロセスは、ポーリングの代わりに定期的なイベントで表すことができますが、実質的には同等です。
schedule(updateEvent, 33ms)
function updateEvent:
for monster in game:
monster.update()
render()
対:
while 1:
for monster in game:
monster.update()
wait(33ms)
render()
興味深いことに、pygletは従来のループの代わりにイベントベースのメソッドを実装しています。これは多くの場合うまく機能しますが、クロック解像度や vsync などによってパフォーマンスの問題や予期しない動作が発生することがあります。 )。
無期限にそこに留まり、ユーザーの入力に応答できるプログラムには、何らかのループが必要です。そうしないと、プログラムの最後に到達して終了します。
メイン ループは、イベント リスナーを呼び出します。幸運にもイベント駆動型のオペレーティング システムまたはウィンドウ マネージャーを使用している場合、イベント ループはそこに存在します。poll
それ以外の場合は、メイン ループを作成して、I/O、、、またはに基づくシステム コール インターフェイスとselect
従来のイベント駆動型アプリケーションとの間の「インピーダンスの不一致」を調整します。
PS質問に関数型プログラミングのタグを付けたので、高レベルの抽象化を低レベルのイベントベースの実装に接続する素晴らしい仕事をするFunctional Reactive Programmingをチェックしてみてください。
ゲームはリアルタイムで実行する必要があるため、1 つの CPU/コアで継続的に実行する場合に最適に機能します。イベント ドリブン アプリケーションは通常、キューにイベントがない場合、CPU を別のスレッドに渡します。CPU がプロセスに戻るまで、かなりの待機時間がかかる場合があります。ゲームでは、これは短い失速とぎくしゃくしたアニメーションを意味します。
2つの理由-
イベント駆動型システムでさえ、通常、ある種のキューからイベントを読み取り、それらをハンドラーにディスパッチするある種のループを必要とするため、たとえばWindowsでイベントループが発生し、それを拡張することもできます。
アニメーションの目的では、アニメーションのすべてのフレームに対しても、ある種の処理を行う必要があります。確かにタイマーやある種のアイドルイベントでこれを行うことはできますが、とにかくある種のループでそれらを作成することになるので、ループを直接使用する方が簡単です。
イベントを使用してすべてを処理するシステムを見てきました。各フレームの開始時にディスパッチされたイベントをリッスンするフレームリスナーがあります。内部にはまだ小さなゲームループがありますが、ウィンドウシステムイベントを処理し、フレームイベントを作成するだけです。