これまでゲームの更新処理をするときにsetInterval関数を使ってきました。ただ困ったことにブラウザによって更新間隔が違ってくるという問題が起きるのです。鳩でもわかるC#管理人はFirefoxというブラウザを使っています。できあがったゲームをGoogle Chromeなどの他のブラウザで動かして「あれ?動作速度が違う」ということがよくおきるのです。これはFirefox特有の問題なのでしょうか? Microsoft EdgeだとGoogle Chromeとほとんどかわりません。

FirefoxでsetInterval関数を実行するとおかしい?

以下のコードを書いて各ブラウザで実行してみるとFirefoxだけ遅いです。

Microsoft EdgeだとGoogle Chromeで上記のコードを実行すると「10秒間で 303 回の更新がおこなわれました」と300回前後の値が表示されます。1秒あたり30回なのでsetInterval関数の引数とだいたい合っています。ところがFirefoxだと「10秒間で 214 回の更新がおこなわれました」と全2者と比べると低い値が出てきます。

requestAnimationFrame関数じゃダメなの?

ではrequestAnimationFrame関数を使えばどうなるでしょうか?

この場合はFirefoxでもGoogle ChromeでもMicrosoft Edgeでも「10秒間で 602 回の更新がおこなわれました」(多少のズレはあり)と1秒間に60回の更新処理がおこなわれていることが確認できます。

だったらsetTimeout関数ではなくrequestAnimationFrame関数のほうがよいのではないかと思われるのですが、これだと更新処理の間隔を自分で指定することができない ・・・ そう思い込んでいました。

requestAnimationFrame関数だと時間間隔を指定できない?

ところがrequestAnimationFrame関数の実行間隔を擬似的に指定することは可能です。requestAnimationFrame関数が呼び出されたらその時刻を記録しておき、1000 / 30ミリ秒経過した場合だけ更新処理をおこなうようにすればよいのです。

MyIntervalクラスを定義する

グローバル変数ばっかりにしたくないのでクラスにしました。

MyIntervalクラスを使ってみる

MyIntervalクラスを継承してOnUpdated関数をオーバーライドすることで1000 / 60ミリ秒おきではなく1000 / 30ミリ秒おきに更新処理をおこなうことができるようになります。これだとブラウザに関係なく1秒間に30回の更新処理がおこなわれます。

ただし、普通のモニターのリフレッシュレートは60Hzなので、これよりも短い間隔で更新処理をしようとするとこの方法は使えません。引数のintervalの値は 1000 / 60かそれより大きな値でなければなりません(interval = 1000 / 100みたいなのは不可)。