ゲームループの話

ツクールMVのゲームループ

jsでゲームループを作る、つまりフレームごとの処理を行うには、requestAnimationFrameを使用してディスプレイのリフレッシュレートと同期します。requestAnimationFrameは結構最近の話で、2012年ぐらいに私がjsでゲームを作っていたころはsetTimeoutを使用していた覚えがあります。

ツクールMVではSceneManagerでrequestAnimationFrameが使用されています。
SceneManager.updateがrequestAnimationFrameのコールバックで呼ばれ、そこから各updateが伝搬していく形になっています。

updateMain

特に大事なところが以下のSceneManager.updateMainです。
Utils.isMobileSafari()の方はデバイス対応だと思うので今回は無視して良いです。
大きく分けて2つの要素があります。処理の更新(update)と描画(render)です。

  • 処理の更新(updateScene)
    キャラクターが移動してどこの座標にいくか、ウィンドウに何を表示するかなどのゲーム全体の処理

  • 描画(renderScene)
    updateの結果を元に画面を描画しなおす

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
SceneManager.updateMain = function() {
if (Utils.isMobileSafari()) {
this.changeScene();
this.updateScene();
} else {
var newTime = this._getTimeInMsWithoutMobileSafari();
if (this._currentTime === undefined) { this._currentTime = newTime; }
var fTime = (newTime - this._currentTime) / 1000;
if (fTime > 0.25) { fTime = 0.25; }
this._currentTime = newTime;
this._accumulator += fTime;
while (this._accumulator >= this._deltaTime) {
this.updateInputData();
this.changeScene();
this.updateScene();
this._accumulator -= this._deltaTime;
}
}
this.renderScene();
this.requestUpdate();
};

60FPS出ている場合

上記処理を簡単な図で示すと以下のようになります。
60FPSで動作するので、1秒間に60回更新・描画されることになります。
つまり、1フレーム 1/60[s]=16.6666[ms]です(省略して16[ms]と表記します)。
処理落ちしなければ以下の図のように毎フレームごとに1回ずつupdateとrenderが走ります。

処理落ちした場合

仮にupdateで重い処理が1フレーム走って16[ms]内に収まらなかった場合、以下の図のようになります。
リフレッシュレートに同期しているので3フレーム目の頭から始まり、3フレーム目に辻褄が合うようにupdateを2回実行します。
renderは結局、最後の結果を描画すればいいので常に1回です。

実際にDeveloper Toolで見ると、updateSceneが2回呼ばれているのがわかります。

処理落ちし続けた場合

先の例だと1フレームだけ重い処理なので、すぐにfpsは安定します。
しかし、いわゆる重い並列コモンを実行してしまい解除しなかった場合、以下のようにどんどんupdateが積み重なってしまい、動作しなくなってしまいます。
よく並列コモンの最後に1フレームのウェイトを入れる話を聞きますが、この図から推察すると意味があるように見えます。
2回実行されるupdateのうちの片方が小さくなるので、積み重なっていくのが小さくなる可能性があるからです。

ちなみに積み重なったupdateはDeveloper Toolで見るとこんな感じです。

終わりに

実際動かすと今回の図のようにきれいな感じにならないです。
requestAnimationFrameに誤差があるのか、微妙にずれていきますが、そういう仕様なのかイマイチ私はわかっていません。

RPGツクールMVカテゴリの記事
リリース時のチェック項目
回想の実装を考える
環境依存系のエラー調査など
アイコンセットの画像が大きすぎると黒い四角が表示される
マップのリフレッシュの話
最近制作した細かいプラグイン
ツクールのピクチャ番号の扱いについて
容量の大きい音声ファイルをロードするとクラッシュする
テストのレポートを作るプラグイン
フィルタをかけると画面全体が拡大されてしまう
並列実行中のコモンを最初から実行するプラグイン
メニューとマップのスクリーンショットについて
イベントのテストでエラーになるプラグインのケース
ロードした結果が異なる場合がある
プラグインの使用状況を出すプラグイン
装備を記憶、ロードするプラグイン
ピクチャの色あいを変えるプラグイン
ピクチャの色相を変えるプラグイン
デバッグ画面にショートカットキーを追加するプラグイン
BGSを保存・再開するプラグイン
敵キャラにデフォルトのステートを付与するプラグイン
ゲーム画面外演出を考える
ニコニコっぽいテキストを表示するプラグイン
一括入力がしやすいテキストエディタプラグインの紹介
ウィンドウをたくさん出すプラグイン
マスクを使ってゲージを作る
複雑な計算式はプラグインで記述する
シーンからマップに場所移動
シーンに引数を渡す
ウィンドウに画像を表示する
スプレッドシートからjsonにしてツクールに取り込む
ランダムにアイテムを取得するプラグイン(MV版)
全てのアイテムを取得するプラグイン
TweenプラグインのTorigoya_Tween.jsが便利
独自のデータをセーブする
loadBitmapを使うときはsmoothに注意する