Application Timer

Previous topic - Next topic

MrTAToad

Can't find my original post, but never mind as it's been updated slightly - added a few new functions, made sure a help file can now be created, and most importantly, the variables are now in their own TYPE.

This code is for making sure all movement is at a fixed speed, independent of processor speed or platform.  

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


Hemlos

#1
I see a few issues with this library.
Perhaps i dont understand what the intention is.

From what i see, this system would cause irrelavant movement from one screensize to the next.
Also, it would cause multiple computers to go out of sync in relation to movement(multiplayer limits, such as player erradical movement while downloading and playing simultaneously..i say this because this is a common issue).
The individual timers, due to GPU downlimited to 60 fps, would also cause irrelavant movement(multiplayer, when one or more pc stalls due to cpu activity).

Curious, why not use GETTIMER() and a couple bounds delimiters, instead?


This is how I make all movement relative to any FPS, and any Screen Size, with minimal code:
Code (glbasic) Select

GetFrameTime = GETTIMER() / 1000.0
GetFrameTime > 1.0 THEN GetFrameTime = 1.0 // prevent out of control movement, from a stalled pc.
GetFrameTime < 0.0001 THEN GetFrameTime = 0.0001 // avoid negligble numbers
MovementUnit =  GetFrameTime * SQR( ScreenY * ScreenY * 2 ) //relative movement for any screen size
Bing ChatGpt is pretty smart :O

MrTAToad

No, it wont cause irregular movement of screensizes, because it's based on a fixed FPS.

How do you mean going out of sync ?  There will be a bigger jump if there is a bigger delay, but that evens itself out when the computer gets back to normal.

The sleep command makes sure there are no errors.

Moru

Found this in swedish somewhere... fitting.
QuoteWe are programmers. Programmers are in their hearts architects and the first
they want to do when they come to a new buildingplace is to bulldoze the old
rubbish and build something magnificent instead. We do not get excited by
additive renovating; tinker, improve or planting flower beds.

Hemlos

Quote from: MrTAToad on 2009-Sep-21
No, it wont cause irregular movement of screensizes, because it's based on a fixed FPS.

Right, fixed FPS, but screen sizes arent fixed, it varies from computer to computer.
So, in this case, if an object moves 100 pixels on a 20" monitor, on a 5" handheld, 30"+ HD screen is an irregular movement.
Hence my algorithm above, perhaps you would want to incorporate this into your system?

Quote from: MrTAToad on 2009-Sep-21
How do you mean going out of sync ? 

Well if your timers make up for movment at any FPS above 60fps, this is good.
But lets look at a case where people try to cheat movement rate with FPS manipulation.
When a player attempts to cheat the system in an online game, its common knowledge amongst players that movement factors jump the player alot, causing erradical movements.
This is achieved by downloading some arbitrary large file.
So in effect, they would force the FPS to drop below 60, to accomplish this type of cheating, down less than 10 fps in most cases.
Bing ChatGpt is pretty smart :O

MrTAToad

With network stuff you would need a whole lot more than a simple movement timer, including movement prediction and interpolation - this routine, as it stands wont help there.

As for screen resolution - it wont be a problem, a 100 pixels is a 100 pixels no matter the size of the output device.


Hemlos

100 pixels on a screen 2000 wide is insignificant, this isnt the case on per say a handheld.


If something moves across a screen and reaches the other side of the screen in 1 second, it would need to on any screensize.
Hence my note about relative movement size:

// Size & FPS relative movement unit.
MovementUnit =  (GetTimer()/1000) * SQR( ScreenY * ScreenY * 2 )

No matter what the speed, and no matter what screen size it is, its relative to Size & FPS.
Relative movement on any computer speed(FPS), and any size monitor.
The point of this is simple, If an object is supposed to traverse the screen in one second, it will be identical on any computer.

Maybe you can merge this with your system, only relative to the locked FPS, incorporated with screen size relavance.
Keep in mind, my algorithm is squaring the height of the screen.
A truely size relavance would be (GetTimer()/1000) * SQR(x*x+y*y )
The difference would be a scaling of width, but movement isnt square, only relative to actual size of screen.

In the y*y*2 formula, this would give wide screens an advantage, of additional sight, while maintaining the squared movement size.

See i have no idea how to merge this idea with yours, perhaps you can?
Bing ChatGpt is pretty smart :O

Moru

It's two totally different cases. You don't always want an object to move from one side of the screen to the other in exactly one second. If you are working with 2D grafics that isn't being scaled to fit, you might want all movement locked to pixels, not screensize. See for example the older Sim-City games, every tile is a certain number of pixels, the bigger the screen is the more tiles fit. But all movement is relative only in pixels per second, not screensize per second. Otherwise cars would move faster on a bigger screen and that is not what we want.

Hemlos

Of course, pixel movement is nessecary.
However, a movement timer with size relevance is just as useful.
Almost all online games are size relavant, not pixel based.
Bing ChatGpt is pretty smart :O

MrTAToad

Updated to use the new 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 TAppTime_Initialise%:frameRate%=60
self.AppTime_UPS=0
self.AppTime_Iterator=0.0
self.AppTime_CurrentTime=0.0
self.AppTime_PauseStart=0.0
self.AppTime_Speed=1.0
self.AppTime_DesiredLoopTime=1000.0/60.0
self.AppTime_LastUpdateTime=0.0
self.AppTime_LastUPSTime=0.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 TAppTime_Update%:
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.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.0 // Its a float as it can go very large...

IF self.AppTime_CurrentTime-self.AppTime_LastUPSTime>=1000.0
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 TAppTime_Pause%: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.TAppTime_Update()
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 TAppTime_AppTime:
RETURN self.AppTime_CurrentTime
ENDFUNCTION

//! This function returns the frame rate
//\param No parameters are passed
//\return Frame rate
FUNCTION TAppTime_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 TAppTime_UPS:
RETURN self.AppTime_UPS
ENDFUNCTION
ENDTYPE