monoの開発ブログ

HSPTVで正確に60fpsを出すメモ

await命令はミリ秒単位で待ち時間を指定することができるようになっていますが、精度が悪く実際には10ミリ秒単位で指定するのが限界といったところです。通常のゲームであればawait 30とすれば33fps程度で動作するため必要十分かもしれませんが、市販のゲームのように滑らかに動くゲームを作るのであれば60fpsは欲しいところです。

そこで、高精度で60fpsを維持できるタイマを書いてみました。OS標準のDLLしか使わないので、コンテストのHSPTV部門でも利用可能です。

#uselib "kernel32"
#func Sleep "Sleep" int

#uselib "winmm"
#func  timeBeginPeriod "timeBeginPeriod" int
#func  timeEndPeriod   "timeEndPeriod"   int
#cfunc timeGetTime     "timeGetTime"

timeBeginPeriod 1
onexit *close

tick = timeGetTime()

#ifdef _DEBUG
prev = tick
#endif

repeat
    await
    d = 17 - (cnt \ 3 == 0) - (timeGetTime() - tick)
    if d > 0 {
        Sleep d
    }
#ifdef _DEBUG
    if (cnt + 1) \ 60 == 0 {
        current = timeGetTime()
        title strf("FPS:%.1f", 60000.0 / (current - prev))
        prev = current
    }
#endif
    tick = timeGetTime()
loop

*close
    timeEndPeriod 1
    end

基本的な処理はtimeGetTime関数で前フレームからの経過時間を取得して、1フレームあたりの時間に達するまでSleep関数で待つだけのシンプルなものです。ただ、1フレームあたりの時間は60fpsの場合1000[ms]/60=16.66…[ms]となり割り切れないため、1フレームあたりの時間を16,17,17,16,17,17,…[ms]のように可変にすることで、3フレーム単位で見たときに1フレームあたり16.66…[ms]となるようにしています。また、起動時にtimeBeginPeroid関数を呼び出してtimeGetTime関数の精度を引き上げ、終了時にtimeEndPeroid関数で戻すようにしています。

はじめにHSPTV部門でも利用可能と述べたので、容量についても検証してみました。先程のサンプルのstart.axを生成すると523バイト、フレームの長さを16または17[ms]に固定した場合は499バイトになります。何も書いていない状態の114バイトと比較すると、385または409バイトの増加。ちょっと厳しいですね。使えなくはないと思いますが、fpsを上げてうれしい場合はそう多く無いと思うので慎重に考えたほうがよさそうです。