2008/02/24(日)autoFPS フレームスキップ モジュール

はてブ数 2008/02/24 18:04 プログラミング::HSP3つーさ

ゲーム向け?の言語のくせに、フレームスキップとかやってくれる命令ないですよね。
await は半端だし、フレームスキップの処理とか割とFAQなんじゃないのかな。
HSPで解説してるとこあまり見ないなぁ。
まぁ、DirectXとか使えば、その辺勝手にやってくれるし、標準命令でゲーム作る人あまりいないのかも。
まぁ、元々標準命令でゲーム作るんなら速度は出ないし、そんなもん気にしないで済むように作れってことなんだろうなぁ。
まぁいいや、とりあえずモジュール化しちゃえ。

このモジュールはスクリプトに組み込んで自由に使用できます。

標準命令を使ったゲームにおいて重い処理とは主に描画です。
(組み方によっちゃ他の命令も重くなりうるが、そう言うモノはスクリの組み方でどうにでもなる
描画をもし飛ばせたらゲームのスピードは10倍とか100倍とかにできちゃいます。
つまり、速度が欲しかったら描画を飛ばしたらいいんですよ。
描画してる時間がありそうなら描画、なさそうならぶっ飛ばす。
フレームスキップとは要するにそう言う処理です。
んでまぁ、こいつはその辺の時間管理を勝手にやってくれそうなモジュールです。
出来ることはサンプルがすべてです。

このモジュールはあなたのプログラムに組み込んで使用できます。
ですが、無断でのソースコードの転載・再配布はご遠慮ください。

/* mod_autoFPS.hsp */
// 自動 フレームスキップ モジュール v0.01 / 月影とも
#addition "mod_qpc.hsp"
#module mod_AutoFPS
#uselib "kernel32.dll"
#func Sleep "Sleep" int
#uselib "winmm.dll"
#ifdef timeGetTimeDouble
#define timeGetTime timeGetTimeDouble
#else
#cfunc timeGetTime "timeGetTime"
#endif
#define CurrentTime (1.0*timeGetTime@mod_AutoFPS())
#func timeBeginPeriod "timeBeginPeriod" int

#deffunc autoFps_SetFPS int fps
    if StartTime == 0 : StartTime = CurrentTime
    SampleCount = fps*3
    FrameWait = 1000.0/fps
    NextDrawTime = CurrentTime + FrameWait
    dimtype LapTime, vartype("double"), 1
    DrawnFrames = 0
    LastDrawStart = CurrentTime
    return

#deffunc autoFps_StartInternal
    if CurrentTime < NextDrawTime  : Sleep 1 : return 1
    NextDrawTime += FrameWait : StartInternalTime = CurrentTime
    return 0

#deffunc autoFps_StartDraw
    CallCount++
    if /* CallCount\\8=0 | */ NextDrawTime>CurrentTime {
        LapTime(DrawnFrames\\SampleCount) = CurrentTime - LastDrawStart
        LastDrawStart = CurrentTime
        DrawnFrames++
        return 0
    }
    return 1

#define global autoFps_CurrentFps _autoFps_CurrentFps()
#defcfunc _autoFps_CurrentFps
    t = 0.0
    repeat Length(LapTime)
        t+=LapTime.cnt
    loop
    return 1000.0/t*length(LapTime)

#define global autoFps_mmtimer (CurrentTime@mod_autoFps-StartTime@mod_autoFps)
#global
/* そのサンプル */
#include "mod_autoFPS.hsp"

    font MSGothic, 32
    psx = 8
    psy = 8
    psg = 0

    winx = ginfo_winx-32
    winy = ginfo_winy-32

    // FPSモジュール初期化
    autoFps_SetFPS 60 // 引数: 理想FPS値

*mainloop
    await 0
    // ひたすらな内部処理。
    // たとえば音ゲの音出しの精度は60fpsじゃ足りない。
    InternalCounter1++

    // フレーム毎 内部処理(描画以外の処理全部)
    autoFps_StartInternal // 0が戻ったら内部処理開始。
    if stat : goto *mainloop // まだ時ではない
    InternalCounter2++
    psy += psg
    plx+=psx: ply+=psy
    if plx<0 : plx=0 : psx=-psx
    if ply<0 : ply=0 : psy=-psy
    if plx>winx : plx=winx : psx=-psx
    if ply>winy : ply=winy : psy=-psy

    // 描画処理
    autoFps_StartDraw // 0が戻ったら描画。
    if stat : goto *mainloop // 時すでに遅し。次のチャンスを待て!
    DrawnCounter++
    redraw 0
        color 0,0,0:boxf:color 255,255,255
        pos 20,20:
        mes "内部処理カウンタ1  : " + InternalCounter1
        mes "内部処理カウンタ2  : " + InternalCounter2
        mes "描画済みフレーム数 : " + DrawnCounter
        mes "スキップ済フレーム : " + (InternalCounter2 - DrawnCounter)
        mes "起動後経過時間     : " + strf("%.2f", autoFps_mmtimer/1000.0)
        mes "FPS:"+ strf("%6.1f",autoFps_CurrentFps)
        pos plx,ply : mes "●"
        mes 
    redraw 1

    // 描画に時間のかかるマシンのエミュレーション
    //wait 5
goto *mainloop

おまけ。

/* mod_qpc.hsp */
// HSP3 で QueryPerformanceCounter しちゃうモジュール ver0.02
// まぁ、そこそこ精度のいいタイマーってことで使えないこともない。
#module mod_qpc
#uselib "kernel32.dll"
#func QueryPerformanceCounter "QueryPerformanceCounter" var
#func QueryPerformanceFreqency "QueryPerformanceFrequency" var
#defcfunc timeGetTimeDouble
    if qp = 0 : qp = 0.0 : QueryPerformanceFreqency qp.0
    QueryPerformanceCounter qp.1
    return qp.1/qp.0*1000
#global

マシン語は要らないみたいです。
ヒントは 2^(-1022-52)にあるようで、いやはや^^;