Top > SDL > FPS

SDLでFPS(framerate)を環境を越えて一定に保ちたい場合がある。速いハードだと無茶苦茶早く動いてしまうとか。そのへんの制御のやり方を紹介してみます。適当なので、検証すらしてません。あんまり信用してはいけません。

C

適当に考えて思い付く方針は、前回値を覚えておいて、現在値との差分で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());

などとしてフレーム毎に呼び出す。

Scheme(Gauche-sdl)

同じ方法を、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))

とかする感じで使う。

C#-SDL

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も計測可能。