BASIC

Author Topic: AppTimer - independant movement speed  (Read 7493 times)

MrTAToad

  • Guest
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&#39;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
 
« Last Edit: 2010-Apr-21 by MrTAToad »

Offline okee

  • Dr. Type
  • ****
  • Posts: 324
    • View Profile
Re: AppTimer - independant movement speed
« Reply #1 on: 2010-Apr-21 »
hi

Any examples of this in use ?

Thanks

okee
Android: Samsung Galaxy S2 -  ZTE Blade (Orange San Francisco) - Ainol Novo 7 Aurora 2
IOS: 2 x Ipod Touch (1G)

MrTAToad

  • Guest
Re: AppTimer - independant movement speed
« Reply #2 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.
« Last Edit: 2010-Apr-21 by MrTAToad »

Offline bigsofty

  • Community Developer
  • Prof. Inline
  • ******
  • Posts: 2640
    • View Profile
Re: AppTimer - independant movement speed
« Reply #3 on: 2010-Apr-21 »
A very handy technique for cross-platform work, well done Mr. T.  :good:
Cheers,

Ian.

“It is practically impossible to teach good programming style to students that have had prior exposure to BASIC.  As potential programmers, they are mentally mutilated beyond hope of regeneration.”
(E. W. Dijkstra)

MrTAToad

  • Guest
Re: AppTimer - independant movement speed
« Reply #4 on: 2010-Apr-21 »
Its actually a conversion from Leadwerks BlitzMax code :)  So I cant take all the credit, unfortunately :)
« Last Edit: 2010-Apr-21 by MrTAToad »

MrTAToad

  • Guest
Re: AppTimer - independant movement speed
« Reply #5 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&#39;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

Offline WPShadow

  • Administrator
  • Prof. Inline
  • *******
  • Posts: 1667
    • View Profile
    • http://lostrevenant.blogspot.com
Re: AppTimer - independant movement speed
« Reply #6 on: 2010-May-11 »
I will try it in my game!  :nw:
AMD X2 4600, 2 GB Ram, ATI X1950 XTX, XP PRO SP2: GLB Premium 10.beta_dingsi, <(´.´<) Kirby Dance (>`.`)>
http://lostrevenant.blogspot.com
alea iacta est

Offline Bursar

  • Mr. Drawsprite
  • **
  • Posts: 63
    • View Profile
Re: AppTimer - independant movement speed
« Reply #7 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?

MrTAToad

  • Guest
Re: AppTimer - independant movement speed
« Reply #8 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.

Offline Bursar

  • Mr. Drawsprite
  • **
  • Posts: 63
    • View Profile
Re: AppTimer - independant movement speed
« Reply #9 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.

MrTAToad

  • Guest
Re: AppTimer - independant movement speed
« Reply #10 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]
« Last Edit: 2010-Sep-03 by MrTAToad »

Offline Bursar

  • Mr. Drawsprite
  • **
  • Posts: 63
    • View Profile
Re: AppTimer - independant movement speed
« Reply #11 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.

MrTAToad

  • Guest
Re: AppTimer - independant movement speed
« Reply #12 on: 2010-Sep-03 »
fair enough!