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