Framerate Manager (FPS)

Advanced game related topics
User avatar
flaith
Enthusiast
Enthusiast
Posts: 704
Joined: Mon Apr 25, 2005 9:28 pm
Location: $300:20 58 FC 60 - Rennes
Contact:

Framerate Manager (FPS)

Post by flaith »

Hi guys,

here is a code that can handle your fps, it's originally from SDL_gfx by ferzkopp
The file is SDL_framerate.c

Here is the include file
FPS_Manager.pbi :

Code: Select all

; From SDL_gfx by ferzkopp
; SDL_framerate.c
; http://www.ferzkopp.net/Software/SDL_gfx-2.0/

;-CONSTANTS
; Highest possible rate supported by framerate controller in Hz (1/s).
#FPS_UPPER_LIMIT    = 200
; Lowest possible rate supported by framerate controller in Hz (1/s).
#FPS_LOWER_LIMIT    = 1
; Default rate of framerate controller in Hz (1/s).
#FPS_DEFAULT        = 30

;-METHOD
Interface FPSObject
  FPS_setFramerate.i(__rate)
  FPS_getFramerate.i()
  FPS_getFramecount.i()
  FPS_framerateDelay.i()
EndInterface

;-TEMPLATE
; Structure holding the state and timing information of the framerate controller. 
Structure FPSmanager
  *vTable
  obj.FPSObject                      ; To be able to call class inside class
  ; Private
  framecount.i
  rateticks.f
  baseticks.i
  lastticks.i
  rate.i
EndStructure

;-DECLARE
Declare.i FPS_initFramerate()
Declare.i CLASS_setFramerate(*manager.FPSmanager, __rate.i)
Declare.i CLASS_getFramerate(*manager.FPSmanager)
Declare.i CLASS_getFramecount(*manager.FPSmanager)
Declare.i CLASS_framerateDelay(*manager.FPSmanager)

;-VIRTUAL TABLES
DataSection 
  VTable_FPSClass: 
    Data.i @CLASS_setFramerate()
    Data.i @CLASS_getFramerate()
    Data.i @CLASS_getFramecount()
    Data.i @CLASS_framerateDelay()
EndDataSection

;-INTERNAL PROCEDURE
Procedure.i getTicks()
  Protected.i _ticks = ElapsedMilliseconds()

  If _ticks = 0
    ProcedureReturn 1
  Else
    ProcedureReturn _ticks
  EndIf
EndProcedure

;-*************************************************************
;-Initialize framerate manager
; set Default framerate of 30Hz and reset delay interpolation.
; \param manager : Pointer to the framerate manager.
; \return        : Pointer for sucess and 0 for error.
Procedure.i FPS_initFramerate()
  Protected *manager.FPSManager

  *manager = AllocateMemory(SizeOf(FPSmanager))
  If *manager
    ;Need to initialize structure for the call of class inside another class
    InitializeStructure(*manager, FPSmanager)
    ;Make sure the *vTable field points to our virtual table.
    *manager\vTable     = ?VTable_FPSClass
    ;now the call of class inside class can be done
    *manager\obj        = *manager
    ;Initialise the variables if any
    *manager\framecount = 0
    *manager\rate       = #FPS_DEFAULT
    *manager\rateticks  = 1000.0 / #FPS_DEFAULT
    *manager\baseticks  = getTicks()
    *manager\lastticks  = *manager\baseticks
    ;Return a pointer to our new object.
    ProcedureReturn *manager
  Else
    ProcedureReturn 0
  EndIf
EndProcedure

;-Set the framerate in Hz 
; Sets a new framerate For the manager And reset delay interpolation.
; Rate values must be between FPS_LOWER_LIMIT And FPS_UPPER_LIMIT inclusive To be accepted.
; \param manager : Pointer to the framerate manager.
; \param rate    : The new framerate in Hz (frames per second).
; \return        : 0 For sucess and -1 for error.
Procedure.i CLASS_setFramerate(*manager.FPSmanager, __rate.i)
  If (__rate >= #FPS_LOWER_LIMIT) And (__rate <= #FPS_UPPER_LIMIT)
    *manager\framecount = 0
    *manager\rate       = __rate
    *manager\rateticks  = 1000.0 / __rate
    ProcedureReturn 0
  Else
    ProcedureReturn -1
  EndIf
EndProcedure

;-Return current target framerate 
; Get the currently set framerate of the manager in Hz.
; \param manager : Pointer To the framerate manager.
; \return        : Current framerate in Hz or -1 for error.
Procedure.i CLASS_getFramerate(*manager.FPSmanager)
  If *manager = #Null
    ProcedureReturn -1
  Else
    ProcedureReturn *manager\rate
  EndIf
EndProcedure

;-Return current framecount
; Get the current framecount from the framerate manager. 
; A frame is counted each time SDL_framerateDelay is called.
; \param manager : Pointer To the framerate manager.
; \return        : Current frame count or -1 for error.
Procedure.i CLASS_getFramecount(*manager.FPSmanager)
  If *manager = #Null
    ProcedureReturn -1
  Else
    ProcedureReturn *manager\framecount
  EndIf
EndProcedure

;-Delay execution
; Maintain a constant framerate and calculate fps.
; Generate a delay to accomodate currently set framerate. Call once in the
; graphics/rendering loop. If the computer cannot keep up with the rate (i.e.
; drawing too slow), the delay is zero And the delay interpolation is reset.
; \param manager : Pointer To the framerate manager.
; \return        : The time that passed since the last call to the function in ms. May return 0.
Procedure.i CLASS_framerateDelay(*manager.FPSmanager)
  Protected.i _current_ticks
  Protected.i _target_ticks
  Protected.i _the_delay
  Protected.i _time_passed = 0

  ; No manager, no delay
  If *manager = #Null : ProcedureReturn 0 : EndIf
 
  ; Initialize uninitialized manager 
  If *manager\baseticks = 0 : *manager = FPS_initFramerate() : EndIf

  ; Next frame
  *manager\framecount + 1

  ; Get/calc ticks 
  _current_ticks      = getTicks()
  _time_passed        = _current_ticks - *manager\lastticks
  *manager\lastticks  = _current_ticks
  _target_ticks       = *manager\baseticks + (*manager\framecount * *manager\rateticks)

  If _current_ticks <= _target_ticks
    _the_delay = _target_ticks - _current_ticks
    Delay(_the_delay)
  Else
    *manager\framecount = 0
    *manager\baseticks  = getTicks()
  EndIf

  ProcedureReturn _time_passed
EndProcedure
And a test code :

Code: Select all

XIncludeFile "FPS_Manager.pbi"

; from the sample : TestFramerate.c of SDL_gfx
#scr_WIDTH  = 640
#scr_HEIGHT = 480
i=0
x=#scr_WIDTH/2
y=#scr_HEIGHT/2
larg_circle = 60
dx=7
dy=5
time_passed = 0
message.s = ""
message2.s = ""

InitSprite():InitKeyboard()
If OpenWindow(0, 0, 0, #scr_WIDTH, #scr_HEIGHT, "FPS Example", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  OpenWindowedScreen(WindowID(0), 0, 0, #scr_WIDTH, #scr_HEIGHT, #True, 0, 0)
  If CreateSprite(0, larg_circle, larg_circle) And StartDrawing(SpriteOutput(0))

    For Radius = larg_circle/2 To 1 Step -5
      Circle(larg_circle/2, larg_circle/2, Radius, RGB(Random(255), Random(255), Random(255)))
    Next

    StopDrawing()
  EndIf
  TransparentSpriteColor(0, 0)

  ; Set our FPS Manager, default:30
  fpsm.FPSObject
  fpsm = FPS_initFramerate()
  rate = fpsm\FPS_getFramerate()
  message = "Framerate set to "+Str(rate)+" Hz ..."

  Repeat
    Repeat
      Event = WindowEvent()

      Select Event 
        Case #PB_Event_Gadget
          If EventGadget() = 0
            quit = 1
          EndIf
        
        Case #PB_Event_CloseWindow
          quit = 1 
      EndSelect
    Until Event = 0

    ExamineKeyboard()
    If KeyboardPushed(#PB_Key_Escape) : quit = 1 : EndIf

    i - 1
    If i<0
      ; Set new rate
      rate=5+5*(Random(100) % 10)
      fpsm\FPS_setFramerate(rate)
      message = "Framerate set to "+Str(rate)+" Hz ..."
      ; New timeout
      i=2*rate
    EndIf
  
    ClearScreen(0)
    StartDrawing(ScreenOutput())  
    DrawText(#scr_WIDTH/2-4*Len(message),#scr_HEIGHT-15,message)
    If time_passed > 0
      message2 = "Delay is "+Str(time_passed)+" ms / Measured framerate "+Str(1000/time_passed)+" Hz ..." 
      DrawText(#scr_WIDTH/2-4*Len(message2),#scr_HEIGHT-30,message2)
    EndIf
    StopDrawing()
   
    ; Move
    x + dx
    y + dy

    ; Reflect
    If x<0 Or x>#scr_WIDTH-larg_circle  : dx = -dx : EndIf
    If y<0 Or y>#scr_HEIGHT-larg_circle : dy = -dy : EndIf
    DisplayTransparentSprite(0, x, y)

    FlipBuffers()
    time_passed = fpsm\FPS_framerateDelay()
  Until Quit = 1
EndIf
“Fear is a reaction. Courage is a decision.” - WC