こちらの記事を参考に、ゲームループの処理を見てみます。
(やってることは分かるんですが、自分の中にしっかり落とし込めるまで理解できてなかったのでメモとして書いておきます)
ゲームループ
ゲームは常に画面が動き続け、かつユーザからの操作を待ちます。
また最近のゲームでは物理演算などが当たり前に利用されているので、そうした計算処理を一定の間隔でループさせて処理する必要があります。
そうした諸々の処理のループを一般的に「ゲームループ」と呼びます。
詳細についてはこのあたりの記事が参考になりそうです。
解説
さて、ではさっそく記事を見ていきます。
といってもやっていることは比較的単純で、記事の言葉を引用すると以下になります。
1秒間に可能な限り多くのフレームを描画しようとしながら、1秒毎に決められた回数のロジックを実行するよう試みます。
ということです。
紹介されているコードを少し整形して紹介。
Game.fps = 50;
Game.run = (function() {
var loops = 0;
var skipTicks = 1000 / Game.fps;
var maxFrameSkip = 10;
var nextGameTick = Date.now();
return function {
loops = 0;
while (Date.now() > nextGameTick && loops < maxFrameSkip) {
Game.update();
nextGameTick += skipTicks;
loops++;
}
Game.draw();
};
})();
// Start the game loop
Game._intervalId = setInterval(Game.run, 0);
[追記]
以下、コメントで間違いを指摘いただいたので修正しています。
skipTicks
は 1000ms
を FPS
で割っています。つまり 1FPS
あたりの時間を計算しています。
maxFrameSkip
は何フレーム分 update
を実行したら描画を実行するかを決めています。
つまり「1秒毎に決められた回数のロジックを実行するよう試み」ているわけですね。
なのでこの場合だと 10回
ということになります。
ここ、だいぶ勘違いをしていました。
コメントで指摘してもらってだいぶクリアになった気がします。
ここについてはコメントを見てもらったほうがいいと思いますが、自分なりの解釈も書いておきます。
解釈
まず前提として、
「 描画処理は指定したFPS関係なく、関数が呼ばれた回数分「できるだけ」実行し、update
関数はFPSの指定通りの回数実行できるよう努力する 」
という見方をします。
さて、では問題をシンプルにするために while
文をまるまる取って考えてみましょう。
すると run
が呼ばれた回数だけ draw
が実行されるのは自明ですね。
ただこれではゲームループとしてアップデートしてほしい内容がなにも更新されなくなってしまいます。
理想として update
がコスト0で終えられるなら、前述の通り run
実行回数分だけ draw
も実行されます。
しかし現実はそううまくいかないのでここにそれなりのコストがかかります。
さて、そのコストがかかった分をどうするか、が while
文で行っている処理と見ることができます。
update
に要した時間が 1フレーム分の時間 == skipTicks
より小さい場合、次の while
の評価は当然 ((Date.now() > nextGameTick) == false
) になりループを抜けます。
この状態が維持され続ければ update
と draw
は1対1で実行され続けますね。
ただもし、update
の処理が重くなってしまった場合どうなるでしょうか。
結果として Date.now() > nextGameTick
は true
になります。
(1フレーム分進めた時間よりも現在時刻のほうが先に行ってしまっている)
そうすると遅れた分を取り戻すためにループが開始されます。
ただ、update
が毎回重くなってしまうと永久にループを抜けられなくなってしまうため、maxFrameSkip
によってキャップを設け、その回数分実行したらループを抜けて一回描画を行う、という処理になります。
・・・ふぅ、この認識であってるかな( ;´Д) 要は「遅れた分を取り戻す処理が
while` の中身」ってことだと思いますw