Help with game timing

BlitzMax Forums/BlitzMax Beginners Area/Help with game timing

Festay(Posted 2012) [#1]
I am developing a game and I have the following code which is derived from the Digesteroids example that comes with Blitzmax.
I am struggling with the timing logic.

What I want is a fixed rate timer that is able to determine if updates are lagging so that something can be done to ensure updates occur at the desired time. I also want my game to 'feel' the same if updates aren't occurring at the desired rate (or if the user chooses a different update rate by setting the targetFPS variable to something else E.G. 60)

Here is my code so far:

SuperStrict

AppTitle = "Test Game"

Graphics 800, 600
SetClsColor(100, 149, 237)

New TGame.Run()

Type TGame

	Field targetFPS:Int = 30
	Field isRunning:Int = True
	
	Field isRunningSlow:Int = False
	
	Field x:Float = 30
	Field y:Float = 60

	Method Update(delta:Float)
		If KeyHit(KEY_ESCAPE) Then isRunning = False
		
		If KeyDown(KEY_UP) Then y:- 60 * delta
		If KeyDown(KEY_DOWN) Then y:+ 60 * delta
		
		If KeyDown(KEY_LEFT) Then x:- 60 * delta
		If KeyDown(KEY_RIGHT) Then x:+ 60 * delta
	End Method
	
	Method Draw()
		Cls
		DrawRect(x, y, 20, 20)
	End Method
	
	Method Run()
	
		' ---------------------------------------------------------------
	    ' This is the amount of time (in milliseconds) that should occur
	    ' between each frame before it is drawn.
		' ---------------------------------------------------------------
		Local targetElapseTime:Int = 1000 / TargetFPS
	
		' ---------------------------------------------------------------
		' Store the initial start time and calculate how long to wait
		' before the first frame is drawn.
		' These values will be recalculated after all required updates
		' have been made but before the game is drawn.
		' ---------------------------------------------------------------
		Local frameStartTime:Int = MilliSecs()
		Local frameDelay:Int = MilliSecs() + targetElapseTime
		
		' ---------------------------------------------------------------
		' These variables will be used to calculate how many updates need
		' to be made.
		' ---------------------------------------------------------------
		Local timeMissed:Int
		Local updatesRequired:Float
		
		While isRunning
		
			' ---------------------------------------------------------------
			' If the game is running faster than it needs to, the game will
			' continuously loop until it is time to perform an update.
			' ---------------------------------------------------------------
		
			Repeat
			Until MilliSecs() > frameDelay
			
			' ---------------------------------------------------------------
			' Next we calculate if we are running behind; this can occur
			' on subsequent iterations (E.G. if Draw() has taken a long time)
			' so we calculate how much time has been missed to determine how
			' many updates need to be made in order to catch up.
			' ---------------------------------------------------------------
			
			timeMissed = (MilliSecs() - frameStartTime)
			updatesRequired = Float(timeMissed) / Float(targetElapseTime)
			
			' ---------------------------------------------------------------
			' This 'caps' the updates if too many are required and provides
			' notification so that you can do something about it.
			' ---------------------------------------------------------------
			
			If updatesRequired > targetElapseTime Then
				updatesRequired = targetElapseTime / 2
				isRunningSlow = True
			Else
				isRunningSlow = False
			End If
			
			Local delta:Float = (MilliSecs() - Float(frameStartTime)) / 1000
			
			If updatesRequired > 1.0 Then
	        	    For Local Count:Int = 1 To Int(updatesRequired)
	            	        Self.Update(delta)
	                    Next
			End If
			
			frameStartTime = MilliSecs()
			frameDelay = MilliSecs() + targetElapseTime
			
			Self.Draw()
			Flip
		
		End While
	
	End Method

End Type


My code appears to do what I want it to do but something doesn't seem right and I can't quite put my finger on it.
I'm hoping someone who has a better understanding of this stuff can review my code and let me know if I am on the right lines.

I also have a few questions that have occurred to me whilst learning about game timing:

1. When people talk about game timing, when they say 'updates per second' do they really mean 'updates and render' per second or just updates?

2. Which operation would you usually expect to take the longest (Update or render; I am guessing rendering would usually take longer).

3. The main addition I have made to the Digesteroids timing code is the inclusion of a cap on the amount of updates that can be made. My thinking behind this was that if the game is running slow, it will try and catch up by calling more updates. If the updates are also running slow then this can cause the game to try and catch up on the next loop and so on causing the game to potentially grind to a halt. When I cap the number of updates I set a variable that says 'hey, the game is running slow, you best do something about it'. Is my thinking behind this correct? If so, does my implementation make sense?

Last edited 2012

Last edited 2012


Wiebo(Posted 2012) [#2]
Answers to your questions:

1) I don't. Updates should happen at fixed times, rendering as often as possible. The trick is to de-couple logic updates and rendering.

2) Depends on your game, but most of the time it's rendering. Which is why it makes sense to renders as often as possible.

3) Your thinking is correct, but your solution to the problem will cause stuttering and slowed down gameplay, as you mention. A better solution IMO is to keep the updates going at a steady rate, and render less when needed but use tweening to smooth out movement. That is why separating logic and rendering is important.


I'm working on a small game engine, which uses fixed updates and render tweening for situations like this. It will use a lot more but the engine itself is perfect as an example for you.

code: http://code.google.com/p/noisycode/source/browse?repo=game2d
wiki: http://code.google.com/p/noisycode/wiki/game2d

Take a look at the code and examples and see if it makes sense to you. It's not that complicated.


Midimaster(Posted 2012) [#3]
As you put your question into "beginners forum", I will give you a easy to understand solution:

This is never a good solution:
Repeat
Until MilliSecs() > frameDelay


better is to give back control to your system:
FPS=CreateTimer(60)
Repeat
      .....
     Game.Run()
     WaitTimer FPS
Until KeyHit(Key_Escape)


In the game.Run you could take care about the timing with using individual timers. The following code shows a simple system, where the Update() is called every 10msec. Also when the computer interupts the game for a longer time (f.e. 200msec) the code will prefer the updating of the game and catch up the lost updates:

This is the Run()-Methode:
.....
If UpdateTimer<Millisecs()-1000 then
     ' for the case, when game was interupted for a long timer (f.e. menu screen)
     UpdateTimer=Millisecs()-1
Endif

If UpdateTimer<Millisecs()
     UpdateTimer=UpdateTimer+10
     Update()
ElseIf DrawTimer<Millisecs()
     DrawTimer=Millisecs()+16
     Draw()
Endif


This code need no think about "delta factor" in the game code. The Run() is called max. 200 times a second. The Update() is called 100 times a second with guarantee of 100%. The Draw() is called less or max 60 times a second.


Festay(Posted 2012) [#4]
Thank you both for the help so far.

@wiebo - The information I have read so far led me to believe that it was a waste of time to render more than once per frame as you could potentially be re-rendering exactly the same thing multiple times which is a waste. Also, if rendering is taking longer than it should (i.e. longer than the desired frame time) then multiple renders are just going to make this worse or am I misunderstanding something here?

I understand my solution to point 3 could cause slowdown but the point is to flag this up to the programmer via the 'isRunningSlow' variable so that they can do something abut it to help the game recover (remove the number of entities from the screen, update every other frame etc.)

I had considered render tweening instead but this added another layer of complexity onto something I already do not fully understand.

@Midimaster - I had originally considered using a timer but reasoned that this was not a good solution as I was unable to find any code that used this method (all code I have seen so far manually calculates the frame tick times).

The main problem I am having is everyone seems to have a different opinion on how game timing should be done. This stuff is also difficult to figure out due to the small time scales being used I.E. stepping through the code doesn't really show you what is going on; I also tried simulating this using an Excel spreadsheet to see what was happening but think I have just confused myself further.


Derron(Posted 2012) [#5]

The Update() is called 100 times a second with guarantee of 100%



drag your windowed app in windows ... (we had that topic already).


for your case
superstrict
?Threaded
Import Brl.threads
?
'class for smooth framerates

Type TDeltaTimer
	field newTime:int 				= 0
	field oldTime:int 				= 0.0
	field loopTime:float			= 0.1
	field deltaTime:float			= 0.1		'10 updates per second
	field accumulator:float			= 0.0
	field tweenValue:float			= 0.0		'between 0..1 (0 = no tween, 1 = full tween)

	field fps:int 					= 0
	field ups:int					= 0
	field deltas:float 				= 0.0
	field timesDrawn:int 			= 0
	field timesUpdated:int 			= 0
	field secondGone:float 			= 0.0

	field totalTime:float 			= 0.0

	?Threaded
	Global UpdateThread:TThread
	Global drawMutex:TMutex 		= CreateMutex()
	Global useDeltaTimer:TDeltaTimer= null
	?

	Function Create:TDeltaTimer(physicsFps:int = 60)
		local obj:TDeltaTimer	= new TDeltaTimer
		obj.deltaTime			= 1.0 / float(physicsFps)
		obj.newTime				= MilliSecs()
		obj.oldTime				= 0.0
		return obj
	End Function

	?Threaded
	Function RunUpdateThread:Object(Input:Object)
		repeat
			useDeltaTimer.newTime		= MilliSecs()
			if useDeltaTimer.oldTime = 0.0 then useDeltaTimer.oldTime = useDeltaTimer.newTime - 1
			useDeltaTimer.secondGone	:+ (useDeltaTimer.newTime - useDeltaTimer.oldTime)
			useDeltaTimer.loopTime		= (useDeltaTimer.newTime - useDeltaTimer.oldTime) / 1000.0
			useDeltaTimer.oldTime		= useDeltaTimer.newTime

			if useDeltaTimer.secondGone >= 1000.0 'in ms
				useDeltaTimer.secondGone 	= 0.0
				useDeltaTimer.fps			= useDeltaTimer.timesDrawn
				useDeltaTimer.ups			= useDeltaTimer.timesUpdated
				useDeltaTimer.deltas		= 0.0
				useDeltaTimer.timesDrawn 	= 0
				useDeltaTimer.timesUpdated	= 0
			endif

			'fill time available for this loop
			useDeltaTimer.loopTime = Min(0.25, useDeltaTimer.loopTime)	'min 4 updates per seconds 1/4
			useDeltaTimer.accumulator :+ useDeltaTimer.loopTime

			if useDeltaTimer.accumulator >= useDeltaTimer.deltaTime
				'force lock as physical updates are crucial
				LockMutex(drawMutex)
				While useDeltaTimer.accumulator >= useDeltaTimer.deltaTime
					useDeltaTimer.totalTime		:+ useDeltaTimer.deltaTime
					useDeltaTimer.accumulator	:- useDeltaTimer.deltaTime
					useDeltaTimer.timesUpdated	:+ 1
					EventManager.triggerEvent( "App.onUpdate", TEventSimple.Create("App.onUpdate",null))
				Wend
				UnLockMutex(drawMutex)
			else
				delay( floor(Max(1, 1000.0 * (useDeltaTimer.deltaTime - useDeltaTimer.accumulator) - 1)) )
			endif
		forever
	End Function

	Method Loop()
		'init update thread
		if not self.UpdateThread OR not ThreadRunning(self.UpdateThread)
			useDeltaTimer = self
			print " - - - - - - - - - - - - "
			print "Start Updatethread: create thread"
			print " - - - - - - - - - - - - "
			self.UpdateThread = CreateThread(self.RunUpdateThread, Null)
		endif

		'if we get the mutex (not updating now) -> send draw event
		if TryLockMutex(drawMutex)
			'how many % of ONE update are left - 1.0 would mean: 1 update missing
			self.tweenValue = self.accumulator / self.deltaTime

			'draw gets tweenvalue (0..1)
			self.timesDrawn :+1
			EventManager.triggerEvent( "App.onDraw", TEventSimple.Create("App.onDraw", string(self.tweenValue) ) )
			UnlockMutex(drawMutex)
		endif
		delay(2)
	End Method
	?

	?not Threaded
	Method Loop()
		self.newTime		= MilliSecs()
		if self.oldTime = 0.0 then self.oldTime = self.newTime - 1
		self.secondGone		:+ (self.newTime - self.oldTime)
		self.loopTime		= (self.newTime - self.oldTime) / 1000.0
		self.oldTime		= self.newTime

		if self.secondGone >= 1000.0 'in ms
			self.secondGone 	= 0.0
			self.fps			= self.timesDrawn
			self.ups			= self.timesUpdated
			self.deltas			= 0.0
			self.timesDrawn 	= 0
			self.timesUpdated	= 0
		endif

		'fill time available for this loop
		self.loopTime = Min(0.25, self.loopTime)	'min 4 updates per seconds 1/4
		self.accumulator :+ self.loopTime

		'update gets deltatime - fraction of a second (speed = pixels per second)
		While self.accumulator >= self.deltaTime
			self.totalTime		:+ self.deltaTime
			self.accumulator	:- self.deltaTime
			self.timesUpdated	:+ 1
			EventManager.triggerEvent( TEventSimple.Create("App.onUpdate",null) )
		Wend
		'how many % of ONE update are left - 1.0 would mean: 1 update missing
		self.tweenValue = self.accumulator / self.deltaTime

		'draw gets tweenvalue (0..1)
		self.timesDrawn :+1
		EventManager.triggerEvent( TEventSimple.Create("App.onDraw", string(self.tweenValue) ) )
		'Delay(1)
	End Method
	?
	'tween value = oldposition*tween + (1-tween)*newPosition
	'so its 1 if no movement, 0 for full movement to new position
	'each drawing function has to take care of it by itself
	Method getTween:float()
		return self.tweenValue
	End Method

	'time between physical updates as fraction to a second
	'used to be able to give "velocity" in pixels per second (dx = 100 means 100px per second)
	Method getDeltaTime:float()
		return self.deltaTime
	End Method
End Type


local timer:TDeltaTimer = TDeltaTimer.Create(60) '60 update calls per second / "physical updates"

repeat
	timer.loop() 'will call the update and draw functions
until AppTerminate OR KeyHit(KEY_ESCAPE)


Replace the "EventManager..."-lines with the corresponding "Update"/"Draw"-functions (eg from your game object, screen manager, ...).

What it does:
it runs updates at the interval set in the TDeltaTimer.Create()
it runs draw functions as often as possible (between the update cycles).

if compiled in threaded mode, the loop will be different and updates are running in different threads (drawing has to be done in main thread).
Benefit from using the threaded one ? - you can move the window of your app in windows and the updates are still processed instead of being freezed (eg. when using Millisecs() to see if an object was visible long enough or a network message is to old and should be send to trash bin).

It is easily edited in a way to use a similar style of accumulator/deltaTiming to limit the fps to a specific value (if you want less than the one flip 1/monitor refreshrate provides)


edit:
the getDeltaTime() method can be used for movement within your update-function.
eg. to move a object 10px per second ...
object.x :+ 10*timer.getDeltaTime()

as the time is only a fraction of a second and after 60 updates the second is gone (60 times the update is 60*(0.0...*10)).



bye
Ron

Last edited 2012


Wiebo(Posted 2012) [#6]
@Festay: What I mean with 'rendering as many times as possible' is this: the less time your update loop is taking, the more times you can render. BUT: You update and then render, and never do update,render,render (for instance)

I can choose to update my game 30 times a second, and still render at 60 fps.
That is what de-coupling means.

Read this article: http://gafferongames.com/game-physics/fix-your-timestep/


Midimaster(Posted 2012) [#7]
the article is nice. it describes the main problem you will have with a delta timing: in a case of a game "delay", a jumping of 10pix in one step is not the same as 10 times call a function, which jumps 1pix. the second solution is more save, when you think about collisions, etc....


Festay(Posted 2012) [#8]
I re-implementing my game loop based on the code here.

My code now looks like this:

SuperStrict

Graphics 800, 600
SetClsColor(100, 149, 237)

Const TICKS_PER_SECOND:Int = 25
Const SKIP_TICKS:Int = 1000 / TICKS_PER_SECOND
Const MAX_FRAMESKIP:Int = 5;

Global x:Float
Global y:Float

Global xpos:Int = 20
Global ypos:Int = 20

New tgame.run()

Type TGame

	Field isRunning:Int = True
	Field framesPerSecond:Int

	Method Run() Final
		
		Local nextGameTick:Int = MilliSecs()
		Local loops:Int
		Local tween:Float
		Local frameStartTime:Float
		
		While isRunning
		
			loops = 0
			frameStartTime = MilliSecs()
	
			While (MilliSecs() > nextGameTick And loops < MAX_FRAMESKIP)
				update()
				nextGameTick:+ SKIP_TICKS
				loops:+ 1
			Wend
	
			tween = Float(MilliSecs() + SKIP_TICKS - nextGameTick) / Float(SKIP_TICKS)
			draw(tween)
			Print 1000 / (MilliSecs() - framestartTime)
		
		Wend		
		
	End Method
	
	Method Draw(tween:Float)
		Cls
		xpos:+ x + (x * tween)
		ypos:+ y + (y * tween)
		DrawRect(xpos,ypos,20,20)
		Flip
	End Method
	
	Method Update()
		If KeyHit(KEY_ESCAPE) Then isRunning = False

		If KeyDown(KEY_UP) Then y = -1
		If KeyDown(KEY_DOWN) Then y = 1
		
		If KeyDown(KEY_LEFT) Then x = -1
		If KeyDown(KEY_RIGHT) Then x = 1
	
		If Not KeyDown(KEY_LEFT) And Not KeyDown(KEY_RIGHT) Then x = 0
		If Not KeyDown(KEY_UP) And Not KeyDown(KEY_DOWN) Then y = 0
	End Method

End Type


Last edited 2012


Midimaster(Posted 2012) [#9]
This is not constant on different computers!

Test it: With a FLIP 0 you will see, that your moving speed rises. Is this really, what you want?

To add values to the xpos inside the drawing is never a good solution. The only way to update the values is inside the update() function. With your model you add +1 to the xpos every time the draw() is called... additional to the tween-related x-value. with a higher vsync you will add it more often than on a slow computer.

this could be a more constant way:
	Method Draw(tween:Float)
		Cls
		xpos:+ (x * tween)
		ypos:+ (y * tween)
		DrawRect(xpos,ypos,20,20)
		Flip 0
	End Method
	
	Method Update()
		Xpos:+X
		Ypos:+Y
		X=0
		Y=0
		If KeyHit(KEY_ESCAPE) Then isRunning = False
.....



but I keep on saying, that my way is a solution with a high quality:
SuperStrict

Graphics 800, 600
SetClsColor(100, 149, 237)

Const SKIP_TICKS% = 7
Const DRAW_TICKS% = 15

Global x:Float
Global y:Float

Global xpos:Int = 20
Global ypos:Int = 20

Global xpos_B:Double = 20
Global xpos_C:Double = 20
New tgame.run()

Type TGame

	Field isRunning:Int = True
	Field Timer:TTimer=CreateTimer(500)
	Method Run() Final
		
		Local nextGameTick:Int,nextDrawTick:Int

		While isRunning
		
	
			If MilliSecs() > nextGameTick +1000
				nextGameTick= MilliSecs()-1
			EndIf
			If MilliSecs() > nextGameTick 
				update()
				nextGameTick:+ SKIP_TICKS
				Print "Update" + nextgametick
			ElseIf MilliSecs() > nextDrawTick 
				nextDrawTick= MilliSecs()+DRAW_TICKS
				draw (1)
				Print "Draw"+ nextDrawtick

			EndIf
			Print "Timer" +MilliSecs()
			WaitTimer Timer
		Wend		
		
	End Method
	
	Method Draw(tween:Float)
		Cls
		DrawRect(xpos,ypos,20,20)
		DrawRect(xpos_B,200,20,20)
		DrawRect(xpos_C,300,20,20)
		Flip 0
	End Method
	
	Method Update()
		If KeyHit(KEY_ESCAPE) Then isRunning = False

		If KeyDown(KEY_UP) Then y = -1
		If KeyDown(KEY_DOWN) Then y = 1
		
		If KeyDown(KEY_LEFT) Then x = -1
		If KeyDown(KEY_RIGHT) Then x = 1
	
		If Not KeyDown(KEY_LEFT) And Not KeyDown(KEY_RIGHT) Then x = 0
		If Not KeyDown(KEY_UP) And Not KeyDown(KEY_DOWN) Then y = 0
		xpos:+ x
		ypos:+ y 


		xpos_B:+ 1.1
		xpos_C:+ 1.3

	End Method

End Type



If You really want to add tweening to your code, you have to take care about, that you have two variables for each time related value. One, who will represent the "past" status, and one who will show the "future" status. With this the Draw() methode would be able to interpolate between this values. and alway when the "future" is reached, the future values have to become the "past" values for the next cylce.


Festay(Posted 2012) [#10]
@Midimaster: thank you for explaining the flaws in my code, I really appreciate your feedback. Could you please explain with some more detail what your example is doing please to help me understand it better?

Field Timer:TTimer=CreateTimer(500)


I think this will 'tick' every 2 milliseconds? Why has this been chosen over 60 which you used in your original example?

If MilliSecs() > nextGameTick +1000
    nextGameTick = MilliSecs() - 1
EndIf


Could you explain what this line of code is for (why Millisecs() - 1)?

Is there a reason why updateGameTick is updated AFTER the update but drawGameTick is updated BEFORE the draw?

Also, what happens if the update takes longer than usual (or draw, or both)? Does this protect against this scenario or will it just spiral out of control?

Finally, do you have any tips for learning this sort of code? I noticed you have included print statements and this shows (on my machine anyway) that the game is updating 3 times and then rendering but it doesn't explain to me why. I have tried stepping through the code in the debugger to see what happens but due to the small time-scales involved stepping through the code doesn't give an accurate reflection of what would actually happen (e.g. if I step through the code you have supplied, draw never gets called).

Sorry for asking a lot of questions but I'm really keen to understand this; it's tempting to just use some pre-existing code and get on with my game but I don't like using other peoples code unless I understand it first.

Last edited 2012


Midimaster(Posted 2012) [#11]
for my music programs i often need a more accurate timer than 60 ticks a second. so why not using a tick of 200, which is 5msec or 500, which is 2msec?

the effect is, that the system is calling your function every 5msec to check, if one of the "internal" timers is necessary for ticking. in 80% of this it imediately returns without doing anything. this is very performant.

but if there is to do something it will be done. the update-timer steps forward in relation to his past time. the draw-timer always in relation to now.

this causes, that the update-timer will catch up the lost updates.

f.e.:
the update timer normaly ticks with 10msec, but the computer stopps your program for 60msec. after this my code will prefer the updates and will run 6 times the update function, but only one timer the drawing.

if now the game stopps for a much longer time, f.e. 3 seconds you do not really want to catch up all 300 lost updates, because this looks or sounds very strange. for this cases you should have a function, that forgets the 300 updates and continue with a new setting of the timer.

Millisecs()-1 because I want to start immediately with a update()-call. The update is only called, when Millisecs() is greater than the timer.

The PRINT statements are exactly for displaying, how the timing works. You cannot do that in step mode. Only the printing shows when which function was called. With this I can see, whether my timing is working as expected.

by the way... even PRINT already disturbes the timing. so (later) without the statements it will run still better (accurate)

there is nothing more to learn. The Update() is called exactly with a fixed rate on each computer. All movements (addings) should be placed here. you only have to find out how fast your "actors" should move in one tick.

I suggest not to start with a timer of 500. It may be enough accurate, if you use 200. I only switched it in my example, that you can see, that there is no need to use a certain value. the game speed depends on the Skip_Ticks value

f.e. you could use Skip_Ticks=15 in level 1 and Skip_Ticks=10 in level 2, which makes the game running 50% faster without changing any game code.

move the mousex() to see the effect:
SuperStrict

Graphics 800, 600
SetClsColor(100, 149, 237)

Global SKIP_TICKS% = 10
Const DRAW_TICKS% = 15


Global xpos_B:Double = 20
Global xpos_C:Double = 20
New tgame.run()

Type TGame

	Field isRunning:Int = True
	Field Timer:TTimer=CreateTimer(500)
	Method Run() Final
		
		Local nextGameTick:Int,nextDrawTick:Int

		While isRunning
			If  MouseX()>10 And MouseX()<500
				Skip_Ticks=Sqr(MouseX())

			EndIf
			If MilliSecs() > nextGameTick +1000
				nextGameTick= MilliSecs()-1
			EndIf
			If MilliSecs() > nextGameTick 
				update()
				nextGameTick:+ SKIP_TICKS
				Print "Update" + nextgametick + " " 
			ElseIf MilliSecs() > nextDrawTick 
				nextDrawTick= MilliSecs()+DRAW_TICKS
				draw (1)
				Print "Draw"+ nextDrawtick

			EndIf
			'Print "Timer" +MilliSecs()
			WaitTimer Timer
		Wend		
		
	End Method
	
	Method Draw(tween:Float)
		Cls
		DrawRect(xpos_B,200,20,20)
		DrawRect(xpos_C,300,20,20)
		Flip 0
	End Method
	
	Method Update()
		If KeyHit(KEY_ESCAPE) Then isRunning = False
		xpos_B:+ 1.1
		xpos_C:+ 1.3
		If xpos_B > 800 Then Xpos_B=0
		If xpos_C > 800 Then Xpos_C=0
		
	End Method

End Type



Derron(Posted 2012) [#12]
Don't use the timer mode (with a low resolution) if checking optimizations in graphical operations
   1 second
----------------  -  UpdatesPerSecond  =  Maximum FPS
timer resolution     (elseif...)


Blitzmax-Timers are not working as expected with higher resolution. So take this into consideration when working on engine parts of your app.


First I thought that hickups in the update would make the loop skipping updates but the timer seems to get rid of that.


The most important problem of the loop midimaster posted:
what is the speed of the X-coord-changes within the update loop?
It is
pixels per "(Second minus FPS)/timerRes)
-- see code snippet at the beginning of my post.


That is why people are using deltaTime and tweenValue:

deltaTime ... is the fraction of a second this update is able to "spend".
All updates within one second could sum up their deltaTime to the result of ~1.00000x seconds.

tweenValue ...
if your objects keep the last coordinates they used as a backup you can use that value. tweenValue indicates how many percents of the next step are reached. So tweenValues of 0.8 indicate:
newPosX = oldPosX + 0.8 * (newPosX - oldPosX)
(or newPosX = 0.8*newPosX - 0.2*oldPosX)

What is the benefit?
Animations may look not smooth when jumping between positions with x2-x1 > 1.0 (jumping more than one pixel). Exceptions are fast moving objects with a dX/dY of 10,20...px/second.


But like always: all methods have their benefits. (simplicity VS exact predictions, easy "slowMo"-functionality ...)


bye
Ron