GLBasic forum

Codesnippets => Math => Topic started by: MrTAToad on 2009-Aug-22

Title: AppTimer - independant movement speed
Post by: MrTAToad on 2009-Aug-22
Thought I had posted this before, but as I can't find it, I'll do it now.  Essentially, it is a movement multiplier, independent of computer speed.
All you need to do is call iniAppTime and then call updateAppTime in each loop, using the return value multiplied by a speed value

Code (glbasic) Select
// --------------------------------- //
// Project: AppTimer
// Start: Thursday, September 11, 2008
// IDE Version: 5.360

//! This help file contains information for using the AppTiming system, which can be used to give a consistent movement speed
//! independant of the processor speed
//! The original code was for BlitzMax, and was written by Leadwerks
//! Converted to GLBasic by Nicholas Kingsley
//! You call the routines like :
//! initAppTime()
//! speed=updateAppTime()

TYPE tAppTime
AppTime_UPS
AppTime_Iterator
AppTime_CurrentTime
AppTime_PauseStart
AppTime_Speed
AppTime_DesiredLoopTime
AppTime_LastUpdateTime
AppTime_LastUPSTime
AppTime_DesiredFrequency%
ENDTYPE

GLOBAL _appTime AS tAppTime

//! This function initialises the AppTimer system, and must be called first.
//\param frameRate% - This is the frame rate that you want all movement to be based on.  It defaults to 60 FPS, which is the lowest rate that should be used.
//\return Nothing is returned
FUNCTION initAppTime:frameRate%=60
_appTime.AppTime_UPS=0
_appTime.AppTime_Iterator=0
_appTime.AppTime_CurrentTime=0
_appTime.AppTime_PauseStart=0
_appTime.AppTime_Speed=1.0
_appTime.AppTime_DesiredLoopTime=1000.0/60.0
_appTime.AppTime_LastUpdateTime=0
_appTime.AppTime_LastUPSTime=0
_appTime.AppTime_DesiredFrequency%=frameRate%
ENDFUNCTION

//! This function updates the timing system.  It needs to be called once per loop.
//\param No parameters are passed
//\return A movement value is returned.  This should be muliplied by the amount you want to move.  If the routine is paused, then 0.0 is returned
FUNCTION updateAppTime:
LOCAL time
LOCAL elapsed

IF _appTime.AppTime_PauseStart<>0
RETURN 0.0
ENDIF

_appTime.AppTime_DesiredLoopTime=1000.0/_appTime.AppTime_DesiredFrequency%

time=GETTIMERALL()

IF _appTime.AppTime_LastUpdateTime=0
_appTime.AppTime_Speed=1.0
_appTime.AppTime_LastUpdateTime=time
_appTime.AppTime_CurrentTime=time
_appTime.AppTime_LastUPSTime=time
ELSE
elapsed=time-_appTime.AppTime_LastUpdateTime
IF elapsed=0.0
elapsed=1.0
SLEEP 1
INC time,1.0
ENDIF

_appTime.AppTime_Speed=elapsed/_appTime.AppTime_DesiredLoopTime
_appTime.AppTime_CurrentTime=time
_appTime.AppTime_LastUpdateTime=time
ENDIF

INC _appTime.AppTime_Iterator,1

IF _appTime.AppTime_CurrentTime-_appTime.AppTime_LastUPSTime>=1000
_appTime.AppTime_UPS=_appTime.AppTime_Iterator/((_appTime.AppTime_CurrentTime-_appTime.AppTime_LastUPSTime)/1000)
_appTime.AppTime_LastUPSTime=_appTime.AppTime_CurrentTime
_appTime.AppTime_Iterator=0
ENDIF

RETURN _appTime.AppTime_Speed
ENDFUNCTION

//! This function is used when your program is paused or not.  If it is, then the step speed is still updated
//\param pause% - If this is TRUE, then your program is paused.  If FALSE, then it isn't
//\return Nothing is returned
FUNCTION pauseUnPauseAppTime:pause%
LOCAL elapsed

IF pause%=TRUE
IF _appTime.AppTime_PauseStart=0.0
_appTime.AppTime_PauseStart=GETTIMERALL()
_appTime.AppTime_UPS=0
_appTime.AppTime_Speed=0
ENDIF
ELSE
IF _appTime.AppTime_PauseStart<>0.0
IF _appTime.AppTime_LastUpdateTime<>0.0
elapsed=GETTIMERALL()-_appTime.AppTime_PauseStart
INC _appTime.AppTime_LastUpdateTime,elapsed
ENDIF

_appTime.AppTime_PauseStart=0.0
updateAppTime()
ENDIF
ENDIF
ENDFUNCTION

//! This function returns the time taken to complete a loop
//\param No parameters are passed
//\return Time taken (in milliseconds) to complete a loop
FUNCTION AppTime:
RETURN _appTime.AppTime_CurrentTime
ENDFUNCTION

//! This function returns the frame rate
//\param No parameters are passed
//\return Frame rate
FUNCTION AppSpeed:
RETURN _appTime.AppTime_Speed
ENDFUNCTION

//!  This function returns the updates per second, and is an approximation
//\param No parameters are passed
//\return Updates per second
FUNCTION UPS:
RETURN _appTime.AppTime_UPS
ENDFUNCTION
Title: Re: AppTimer - independant movement speed
Post by: okee on 2010-Apr-21
hi

Any examples of this in use ?

Thanks

okee
Title: Re: AppTimer - independant movement speed
Post by: MrTAToad on 2010-Apr-21
Here is an example program, showing a bouncing ball :

Code (glbasic) Select
LOCAL speed,x,y,dirX,dirY

initAppTime() // We want everything to be as near to 60FPS as possible
speed=0.0
x=100.0+(RND(16)-8)
y=100.0+(RND(16)-8)
dirX=1.0
dirY=1.0
WHILE TRUE
DRAWRECT x,y,16,16,RGB(255,0,0)
PRINT "Loop time : "+AppTime(),0,0
PRINT "Frame rate : "+AppSpeed(),0,12
PRINT "Updates per second : "+UPS(),0,24

SHOWSCREEN

INC x,speed*dirX*0.75
INC y,speed*dirY*0.75

IF x<40 OR x>200
dirX=0.0-dirX
ENDIF

IF y<40 OR y>200
dirY=0.0-dirY
ENDIF

speed=updateAppTime() // Update the timer
WEND


I have found that AppTimer produces more consistent results across multiple platforms than the simpler one of using the square of the screen width.
Title: Re: AppTimer - independant movement speed
Post by: bigsofty on 2010-Apr-21
A very handy technique for cross-platform work, well done Mr. T.  :good:
Title: Re: AppTimer - independant movement speed
Post by: MrTAToad on 2010-Apr-21
Its actually a conversion from Leadwerks BlitzMax code :)  So I cant take all the credit, unfortunately :)
Title: Re: AppTimer - independant movement speed
Post by: MrTAToad on 2010-May-10
This is the version using the extended TYPE system :

Code (glbasic) Select
// --------------------------------- //
// Project: AppTimer
// Start: Thursday, September 11, 2008
// IDE Version: 5.360

//! This help file contains information for using the AppTiming system, which can be used to give a consistent movement speed
//! independant of the processor speed
//! The original code was for BlitzMax, and was written by Leadwerks
//! Converted to GLBasic by Nicholas Kingsley
//! You call the routines like :
//! LOCAL blah AS TAppTime
//! blah.Initialise_AppTime()
//! speed=blah.Update_AppTime()

TYPE TAppTime
AppTime_UPS;AppTime_Iterator;AppTime_CurrentTime;AppTime_PauseStart;AppTime_Speed;AppTime_DesiredLoopTime;AppTime_LastUpdateTime
AppTime_LastUPSTime;AppTime_DesiredFrequency%

//! This function initialises the AppTimer system, and must be called first.
//\param frameRate% - This is the frame rate that you want all movement to be based on.  It defaults to 60 FPS, which is the lowest rate that should be used.
//\return Nothing is returned
FUNCTION Initialise_AppTime%:frameRate%=60
self.AppTime_UPS=0
self.AppTime_Iterator=0
self.AppTime_CurrentTime=0
self.AppTime_PauseStart=0
self.AppTime_Speed=1.0
self.AppTime_DesiredLoopTime=1000.0/60.0
self.AppTime_LastUpdateTime=0
self.AppTime_LastUPSTime=0
self.AppTime_DesiredFrequency%=frameRate%
ENDFUNCTION

//! This function updates the timing system.  It needs to be called once per loop.
//\param No parameters are passed
//\return A movement value is returned.  This should be muliplied by the amount you want to move.  If the routine is paused, then 0.0 is returned
FUNCTION Update_AppTime%:
LOCAL time
LOCAL elapsed

IF self.AppTime_PauseStart<>0
RETURN 0.0
ENDIF

self.AppTime_DesiredLoopTime=1000.0/self.AppTime_DesiredFrequency%

time=GETTIMERALL()

IF self.AppTime_LastUpdateTime=0
self.AppTime_Speed=1.0
self.AppTime_LastUpdateTime=time
self.AppTime_CurrentTime=time
self.AppTime_LastUPSTime=time
ELSE
elapsed=time-self.AppTime_LastUpdateTime
IF elapsed=0.0
elapsed=1.0
SLEEP 1
INC time,1.0
ENDIF

self.AppTime_Speed=elapsed/self.AppTime_DesiredLoopTime
self.AppTime_CurrentTime=time
self.AppTime_LastUpdateTime=time
ENDIF

INC self.AppTime_Iterator,1

IF self.AppTime_CurrentTime-self.AppTime_LastUPSTime>=1000
self.AppTime_UPS=self.AppTime_Iterator/((self.AppTime_CurrentTime-self.AppTime_LastUPSTime)/1000)
self.AppTime_LastUPSTime=self.AppTime_CurrentTime
self.AppTime_Iterator=0
ENDIF

RETURN self.AppTime_Speed
ENDFUNCTION

//! This function is used when your program is paused or not.  If it is, then the step speed is still updated
//\param pause% - If this is TRUE, then your program is paused.  If FALSE, then it isn't
//\return Nothing is returned
FUNCTION Pause_AppTime%:doPause%
LOCAL elapsed

IF doPause%=TRUE
IF self.AppTime_PauseStart=0.0
self.AppTime_PauseStart=GETTIMERALL()
self.AppTime_UPS=0
self.AppTime_Speed=0
ENDIF
ELSE
IF self.AppTime_PauseStart<>0.0
IF self.AppTime_LastUpdateTime<>0.0
elapsed=GETTIMERALL()-self.AppTime_PauseStart
INC self.AppTime_LastUpdateTime,elapsed
ENDIF

self.AppTime_PauseStart=0.0
self.Update_AppTime()
ENDIF
ENDIF
ENDFUNCTION

//! This function returns the time taken to complete a loop
//\param No parameters are passed
//\return Time taken (in milliseconds) to complete a loop
FUNCTION AppTime:
RETURN self.AppTime_CurrentTime
ENDFUNCTION

//! This function returns the frame rate
//\param No parameters are passed
//\return Frame rate
FUNCTION AppSpeed:
RETURN self.AppTime_Speed
ENDFUNCTION

//!  This function returns the updates per second, and is an approximation
//\param No parameters are passed
//\return Updates per second
FUNCTION UPS:
RETURN self.AppTime_UPS
ENDFUNCTION
ENDTYPE
Title: Re: AppTimer - independant movement speed
Post by: WPShadow on 2010-May-11
I will try it in my game!  :nw:
Title: Re: AppTimer - independant movement speed
Post by: Bursar on 2010-Aug-28
I'm getting some odd readings from the Extended Type version of this code.

The following displays between 60 and 65fps when testing my game (with 60FPS limit) :
Code (glbasic) Select
dtime = GETTIMER()

fps = 1000/dtime
fpstimer = fpstimer+dtime

IF fpstimer>500 // 1/2 sec
fpstimer = 0
realfps = fps
ENDIF

PRINT "FPS: "+realfps,640,570


If I call:
Code (glbasic) Select
LOCAL moveTimer AS TAppTime
moveTimer.Initialise_AppTime(60)

outside of my main loop, and inside the loop:
Code (glbasic) Select
moveTimer.Update_AppTime()

and then add:
Code (glbasic) Select
newfps = moveTimer.AppSpeed()
PRINT "AS: "+newfps,640,520


The original FPS counter displays correctly at around 60fps, but AS flicks between 0 and 1, which obviously breaks any movement I tie into the returns from your functions.

Have I missed something out?
Title: Re: AppTimer - independant movement speed
Post by: MrTAToad on 2010-Aug-29
No - I made a slight mistake with the type of the function Update_AppTime - it shouldn't return integers, so just remove the % after the name.
Title: Re: AppTimer - independant movement speed
Post by: Bursar on 2010-Sep-03
It's taken me a couple of days to get to this, so apologies for the delay. Removing the % doesn't help. It still gives me between 0 and 1. AppTime_UPS gives me a set of numbers that looks about right though.
Title: Re: AppTimer - independant movement speed
Post by: MrTAToad on 2010-Sep-03
I'll have to look into that - I've never properly used AppSpeed for the FPS, so I'll have to see if thats correct.

One thing to note - the return value from the Update function is the one that should be used for movement speeds.

I've included the latest version as well, as now GLBasic allows functions with the same name in types, I've made the function names a bit quicker to type.



[attachment deleted by admin]
Title: Re: AppTimer - independant movement speed
Post by: Bursar on 2010-Sep-03
Don't worry too much, I'm going to convert some code from DarkBasic that averages the frame rate over x number of previous frames, and uses that to smooth out movement.
Title: Re: AppTimer - independant movement speed
Post by: MrTAToad on 2010-Sep-03
fair enough!