Frame rate independent movement

BlitzMax Forums/BlitzMax Programming/Frame rate independent movement

Arska(Posted 2015) [#1]
Hi!

I noticed that my upcoming game runs under 60 fps on my laptop and everything moves much slower. That is real problem, because my game is multiplayer. With Leadwerks i've been using Appspeed() function that does this:
Returns the current application speed. Movement values can be multiplied by this value for framerate-independent motion. If the world update speed is slower than 60 updates per second, AppSpeed() will return a value higher than 1.0 to compensate. If the program is running faster than 60 UPS, a value lower than 1.0 will be returned.


So i have just all values multiplied with appspeed and everything moves in same speed no matter what FPS is. Now i would need same kind of function? How it's calculated? Or is this even good way to do this?


Derron(Posted 2015) [#2]
search the forum for "delta timing".

A sample implementation could be found in my framework:
https://github.com/GWRon/Dig/blob/master/base.util.deltatimer.bmx

- manually assign two functions (render and update)
- run a loop updating the deltatimer (repeat; GetDeltaTimer().loop(); until KeyHit(KEY_ESCAPE) or Appterminate())

the Deltavalue is then the fraction of a second gone since the last call to the update function. Means you define your objects speed as "pixels per second" and the movement in this tick is "delta * px/s".

bye
Ron


Scaremonger(Posted 2015) [#3]
The Ledworks function is a Delta Time function. The code below is based on some code by @WAVE.

Basically you call Delta.start() before you enter your game loop and then call Delta.Update() at the end of each loop.

Within your code you multiply all speeds by Delta.Time to get everything to run at the same speed.

For example:
x :+ SPEED * Delta.Time

Type Delta
Global Time#, TimeDelay%

	Function Start()
	TimeDelay = MilliSecs()
	End Function

	Function Update()
	Time = ( MilliSecs() - TimeDelay )*0.001 TimeDelay = MilliSecs()
	End Function
End Type


Hope this helps.


Derron(Posted 2015) [#4]
Keep in Mind that "millisecs()" returns a negative value on computers with an uptime of more than 28 days (32bit rollover) -> this is why my linked sample contains an import of "base.util.time.bmx" which takes care of this.

You might need to include something similar depending on your way of using millisecs().

It also might be of interest to limit both: updates and renderings (to lower cpu consumption). How this could be done is shown in the link too (accumulator).


bye
Ron


Scaremonger(Posted 2015) [#5]
@Derron: Very nice class you have there.


Scaremonger(Posted 2015) [#6]
@Derron: I have just looked at your time code and it uses the same source as mine for MilliSecsLong(): @ImaginaryHuman.

The difference is that I have it in a nested function and you have it in a class. Works the same but it might be easier to implement this into the class I posted earlier for the beginner.

'------------------------------------------------------------
'# Fix for Millisecs() integer over-run
'# ImaginaryHuman
'# http://www.blitzbasic.com/Community/post.php?topic=84114&post=950107
Function MilliSecsLong:Long()
Global MilliSeconds:Long=0:Long			'Initialize before use
Global LastMilliSeconds:Long=get()		'Initialize before use
	Return get()
	'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	Function get:Long()
	Local Milli:Long=Long(MilliSecs())+2147483648:Long           'Convert to 32-bit unsigned
	If Milli<LastMilliSeconds Then MilliSeconds:+4294967296:Long 'Accumulate 2^32
	LastMilliSeconds=Milli
	Return MilliSeconds+Milli
	End Function
End Function




Arska(Posted 2015) [#7]
What if i change millisecs() value to positive if it's less than 0?


Derron(Posted 2015) [#8]
The problem is that "Millisecs()" counts still positive in that case:
-120000 (+1)
-119999 (+1)
-119998

with positive being
119998 (+1)
119999 (+1)
120000

So when "abs"ing negative values you get
120000 (-1)
119999 (-1)
119998


Checkout this file:
https://github.com/GWRon/Dig/blob/master/base.util.time.bmx

it shows a way to handle it - and does not rely on other files (except brl-modules) which makes it easy to integrate.


EDIT: just saw your post @ scaremonger:
Yes, a function might be easier to copy paste, but sooner or later he will have to organize things and making code "reuseable" surely has benefits here and there.

I added some other functions because I sometimes need some kind of "virtual Millisecs()" to make savegame-stored-timers work properly. So when loading in savegame I can adjust the time to the moment of the savegame.
Sorry if that just adds to much complication.


bye
Ron


Arska(Posted 2015) [#9]
Thanks guys this was really helpfull. Stay tuned for more problems... :D