ゲームエンジンの最も重要なパーツの一つに、ゲームループと呼ばれるものがあります。
これは、ゲームエンジンの中心的な部品で、実行中のゲームロジックのバランスを調整し、実行結果の描画を行う役割があります。
もっとも基本的なゲームループは、
JavaScriptでもこんな風に見えます。(これは、ブラウザで動きません!)
A very basic game loop would look something like this in JavaScript (this does not work in Browsers!):ブラウザで動くようにゲームループを書くのは、これよりも少しだけトリッキーになります。
Whileの無限ループは、
JavaScriptが実行中はブラウザのウィンドウ描画をブロックしてしまい、ゲームがロックアップ(固まった)ように見えてしまうので使えません。∩(´・∀・`)∩
そこで私たちは、インターフェイスをロックせずに、かつ、ブラウザの描画が終わるたびにコントロールが戻されるループをエミュレートしてみることにしました。
■setInterval で救出!
window.setInterval は、まさに私たちが探しているものです。これはループの実行の合間にブラウザがウィンドウを再描画を可能にする方法を提供します。
Game.fps = 50;
Game.run = function() {
Game.update();
Game.draw();
};
// Start the game loop
Game._intervalId = setInterval(Game.run, 1000 / Game.fps);
...
// To stop the game, use the following:
clearInterval(Game._intervalId);
setInterval を使ったゲームループの
サンプルページを見てください。
■これで大丈夫?
ええと、そうです。少なくとも基本的なゲームにおいては。この種のループの主な問題は、更新と描画の操作が相互に接着されてしまうことです。
(つまり、あなたのゲームロジックが描画処理と同じ程度に早く実行されないといけないことを意味します)
なので、もしフレームレートが60fps 以下に落ちてしまうなら、あなたのゲームは実行が遅くなったように見えてしまうでしょう。
幾つかのゲームでは、これは問題になりません。しかしそれ以外の場合、この動作は操作のカクカク感(jerkiness)を引き起こします。
また、複数プレイヤーのコンポーネントを追加したい場合、あなたのロジックは全てのクライアントで同じ速度で実行されて欲しいと思うはずです。
これを実現するには、"game loop with fixed time steps(※)" と呼ばれる方法を使う必要があります。
※一定時間でのゲームループ??ゲーム開発で適切な名称があれば、教えてください(´・ω・`)
基本的には、1秒間に可能な限り多くのフレームを描画しようとしながら、1秒毎に決められた回数のロジックを実行するよう試みます。
Game.run = (function() {
var loops = 0, skipTicks = 1000 / Game.fps,
maxFrameSkip = 10,
nextGameTick = (new Date).getTime();
return function {
loops = 0;
while ((new Date).getTime() > nextGameTick && loops < maxFrameSkip) {
Game.update();
nextGameTick += skipTicks;
loops++;
}
Game.draw();
};
})();
// Start the game loop
Game._intervalId = setInterval(Game.run, 0);
このコードは、可能な限り頻繁に描画処理を実行しながら、1秒あたり50回のゲームアップデートを実行するゲームループです。
これは、とてもとてもスムーズなアニメーションを(私のパソコンでは)提供します。
Google Chromeで、秒間50回のゲームアップデート処理を保ちつつ、秒間200回程の描画オペレーションを実行できました。
サンプルは
こちらです。
■でもまだ問題が?!
しかし、私たちはその見返りに別の問題に直面しました。プレイヤーにスムーズなアニメーションを提供するのに必要な量よりも多量のCPUサイクルを使ってしまうのです!
大抵のコンピューター画面のリフレッシュレートは、50か60 Hz なので、秒間200回ものレンダリングを行うことはやりすぎですし、Chromeのタスクマネージャーによると私のCPU使用率も48%に達していました。
これは、Mozillaが1秒毎のリフレッシュ回数に合わせて描画処理の回数を制限する簡単な方法として、
window.mozRequestAnimationFrame を導入した理由です。
(WebKit にも双子のような window.webkitRequestAnimationFrame があります)
(現在、mozRequestAnimationFrameとwebkitRequestAnimationFrame の両方とも画面のリフレッシュレートに完全には同期しませんが、描画処理を節度ある回数に制限することを簡単に助けますし、いずれは実際の同期も実装されるでしょう)
window.*RequestAnimationFrame を実装しないブラウザとの互換性の為に、setInterval も準備しておきましょう。
しかしこちらは、1秒間に60フレームに制限されます。
(function() {
var onEachFrame;
if (window.webkitRequestAnimationFrame) {
onEachFrame = function(cb) {
var _cb = function() { cb(); webkitRequestAnimationFrame(_cb); }
_cb();
};
} else if (window.mozRequestAnimationFrame) {
onEachFrame = function(cb) {
var _cb = function() { cb(); mozRequestAnimationFrame(_cb); }
_cb();
};
} else {
onEachFrame = function(cb) {
setInterval(cb, 1000 / 60);
}
}
window.onEachFrame = onEachFrame;
})();
window.onEachFrame(Game.run);
このコードは、CPUの炎上なしにスムーズなゲームプレイを提供します。
(Chromeのタスクマネージャーによると、webkitRequestAnimationFrame を使ったときで CPU使用率は10~20%でした)
こちらのサンプルを見てください。
■結論
わお。一定のペースでゲームを実行しながら、スムーズなアニメーションを実現するゲームループのスタートまでに、なんだか長い旅でした。
私は、
JavaScriptとHTML5を使ったゲーム開発についてさらに多くの投稿を数週間以内にはアップできると思います。
時々、チェックしにきてくださいね!
--- 翻訳ここまで
今年は英語力アップを目指すぞー。∩(´・∀・`)∩
宜しくお願いしまっす!