SDLでFPS(framerate)を環境を越えて一定に保ちたい場合がある。速いハードだと無茶苦茶早く動いてしまうとか。そのへんの制御のやり方を紹介してみます。適当なので、検証すらしてません。あんまり信用してはいけません。
適当に考えて思い付く方針は、前回値を覚えておいて、現在値との差分でDelayする時間を決定するというもの。
コードは例えば如何のようなもので実現できる。
#define FPS (60)
Uint32 delayTime(void){
static Uint32 prev_time = SDL_GetTicks();
static Uint32 tick_interval = 1000 / FPS;
Uint32 now = SDL_GetTicks();
short int delay_time = tick_interval - (now - prev_time);
prev_time = now + delay_time;
if(delay_time > 0)
return delay_time;
else
return 0;
}
SDL_Delay(delayTime());
などとしてフレーム毎に呼び出す。
int getFPS(){
static Uint32 next_time = 0;
static short fps = 0,__fps = 0;
Uint32 now;
__fps++;
now = SDL_GetTicks();
if(next_time <= now){
next_time = now+1000;
fps = __fps;
__fps = 0;
}
return(fps);
}同じ方法を、Closureを使って実装してみる。
(define (delay-time-counter fps)
(let ((prev-time 0)
(tick-time (/ 1000 fps)))
(lambda ()
(let* ((now (sdl-get-ticks))
(delay-time (- tick-time (- now prev-time))))
(set! prev-time (+ now delay-time))
(if (< 0 delay-time)
(inexact->exact (ceiling delay-time))
0)))))
などとして、
(define *delay-time* (delay-time-counter 60)) (let loop () (sdl-delay (*delay-time*)) (loop))
とかする感じで使う。
(define (fps-counter)
(let ((fps 0) (__fps 0) (next_time 0))
(lambda ()
(let ((now (sdl-get-ticks)))
(set! __fps (+ __fps 1))
(if (<= next_time now)
(begin
(set! next_time (+ now 1000))
(set! fps __fps)
(set! __fps 0)))
fps))
))public class Timer
{
private static int mWaitTT = 0;
private static int mLastTicks = 0;
public static void wait( int fps ){
if( fps == 0 ) return;
int currentTicks = SdlDotNet.Timer.Ticks;
mWaitTT = (mWaitTT & 0xffff) + (1000 * 0x10000 / fps);
int wait = mWaitTT >> 16;
int interval = currentTicks - mLastTicks;
if( interval >= wait ){
mLastTicks = currentTicks;
return;
}
if( wait - interval > 3 ){
System.Threading.Thread.Sleep( wait - interval - 3 );
}
while( SdlDotNet.Timer.Ticks - mLastTicks < wait ){}
}
}
引数に目標fpsをいれて呼び出すと必要なだけウェイトする。 Delayしてもいいけど、そこまで正確とは思えないのである程度以上はwhileでスリープします。
fpsをカウントするような拡張をして、wait(0); を呼び出し続ければ最大fpsも計測可能。