problem calculating time

Blitz3D Forums/Blitz3D Programming/problem calculating time

Hi!
Imagine a 2d square area, divide it in 4 quadrant and let a player move in the area for 1 minute. How is the best way to calculate the time the player spent in each quadrant when the time is out?

I can’t get good results. Here is the code I’m using in the main loop:


If  EntityX (player) >= 0 And EntityZ (player) >= 0 And insideI = 0 Then ;enter the I quadrant
		startTimeI = MilliSecs()
		insideI = 1
		;-------------		
		TimeII = TimeII + insideII * (MilliSecs() - startTimeII)
		insideII = 0
		TimeIII = TimeIII + insideIII * (MilliSecs() - startTimeIII)
		insideIII = 0
		TimeIV = TimeIV + insideIV * (MilliSecs() - startTimeIV)
		insideIV = 0
		;-------------

	ElseIf EntityX (player) >= 0 And EntityZ (player) < 0 And insideII = 0 Then ;enter the II quadrant
		startTimeII = MilliSecs()
		insideII = 1
		;-------------		
		TimeI = TimeI + insideI * (MilliSecs() - startTimeI)
		insideI = 0
		TimeIII = TimeIII + insideIII * (MilliSecs() - startTimeIII)
		insideIII = 0
		TimeIV = TimeIV + insideIV * (MilliSecs() - startTimeIV)
		insideIV = 0
		;-------------

	ElseIf EntityX (player) < 0 And EntityZ (player) < 0 And insideIII = 0 Then ;enter the III quadrant
		startTimeIII = MilliSecs()
		insideIII = 1
		;-------------		
		TimeI = TimeI + insideI * (MilliSecs() - startTimeI) 
		insideI = 0
		TimeII = TimeII + insideII * (MilliSecs() - startTimeII)
		insideII = 0
		TimeIV = TimeIV + insideIV * (MilliSecs() - startTimeIV)
		insideIV = 0
		;-------------
	
	ElseIf EntityX (player) < 0 And EntityZ (player) >= 0 And insideIV = 0 Then ;enter the IV quadrant
		startTimeIV = MilliSecs()
		insideIV = 1
		;-------------		
		TimeI = TimeI + insideI * (MilliSecs() - startTimeI)
		insideI = 0
		TimeII = TimeII + insideII * (MilliSecs() - startTimeII)
		insideII = 0
		TimeIII = TimeIII + insideIII * (MilliSecs() - startTimeIII)
		insideIII = 0
		;-------------

	EndIf 

If MilliSecs() >= iniTime + 60000 
	
		Latency# = (MilliSecs() - iniTime) / 1000.0
		;-------------		
		TimeI = TimeI + insideI * (MilliSecs() - startTimeI) 
		insideI = 0
		TimeII = TimeII + insideII * (MilliSecs() - startTimeII)
		insideII = 0
		TimeIII = TimeIII + insideIII * (MilliSecs() - startTimeIII)
		insideIII = 0
		TimeIV = TimeIV + insideIV * (MilliSecs() - startTimeIV)
		insideIV = 0
		;-------------
		
		TimeIResult# = TimeI / 1000.0
		TimeIIResult# = TimeII / 1000.0
		TimeIIIResult# = TimeIII / 1000.0
		TimeIVResult# = TimeIV / 1000.0



The results (Latency#, TimeIResult#,…) I get using this code are wrong. The latency# should be 60.0 but I always obtain bigger results (like 60.012) . The sum of the 4 TimeXResult# gives the same number (i.e. 60.012).

Is it a conversion type problem? Or the lost of precision is due to the mathematic operations?
Any suggestions to do it better? I need “millisec” precision.

Thanks a lot

FiNegirO

hi, i tested this out and it looks like some commands, i think the ones related to drawing, eat up a few milliseconds, in particular i found flip was the most noticable, also cls with text occasionally caused a small timing error.

Maybe if you give the timer job to a dll it would allow you to have this accuracy. Here is the code i used.

Graphics3D 640,480,0,2

iniTime=MilliSecs()

While Not KeyHit(1)

 tempTime=MilliSecs()-iniTime
 If MilliSecs()>=iniTime+1000 ;1 second test
  If once=0
   once=1
   Latency#=(MilliSecs()-iniTime)/1000.0
   testTime=MilliSecs()-iniTime
   testFloat#=1000/1000.0
  EndIf
 EndIf

 ;If once=1
  Cls
  Text 0,0,"Latency="+Latency+" testFloat="+testFloat
  Text 0,12,"testTime="+testTime+" iniTime="+iniTime+" tempTime="+tempTime
 ;EndIf

 If once=1 Then Flip

Wend


Edit:
The alternative would be checking the time more often.
Here is my version, it does exactly the same:
	Graphics 800, 600, 0, 2
	SetBuffer BackBuffer()
	
	Dim time#(4)
	
	init = True
	
	Repeat
	
		;calculate time passed
		now# = MilliSecs()
		If init Then prev# = now#: inittime# = now: init = False
		elapsed# = now# - prev#
		prev# = now#
		
		;select quadrant
		mx = MouseX()
		my = MouseY()
		quad = (mx > 400) + (my > 300) * 2
		;set time for selected quadrant
		time(quad) = time(quad) + elapsed
		
		;graphical output
		Cls
		
		;draw grid
		Line 400, 0, 400, 600
		Line 0, 300, 800, 300
		
		;print out times
		For i = 0 To 3
			Text i Mod 2 * 400 + 200, i / 2 * 300 + 150, time#(i) / 1000
		Next
				
		;measure total time passed
		tot1# = ((time(0) + time(1) + time(2) + time(3)) / 1000)
		tot2# = (MilliSecs() - inittime#) / 1000

		;debug output		
		Text 0,  0, "You're in quadrant: " + quad
		Text 0, 20, "Total sum times:      " + tot1#
		Text 0, 40, "Total time millisecs: " + tot2#
		
		Flip

		;limit to 60 secs			
		If tot2# >= 60 Then Exit
	
	Until KeyHit(1)
	
	Text 400, 300, "GAME OVER", True, True
	
	WaitKey()
	
	End

/edit

i think now what i posted above isn't quite correct, as the error margin of 1 sec and 60 sec is the same. so it must be more like flip, etc. causes a read error/delay with the current millisecs. the solution then shouldn't need a dll, but just not calling flip on the loop before you get millisecs.

Thanks muk and bram32.
I tried your code and I get the same errors as in mine.

bram32, do your code works for you? I modified it just to display the errors between Total time and Total sum. I can't explain the results: Total sum is usually more then the wanted time and Total time is more than Total sum! Here is the code (thanks bram32):

	Graphics 800, 600, 0, 2
	SetBuffer BackBuffer()
	
	Dim time#(4)
	
.up	

	For i = 0 To 3
		time#(i) = 0.0
	Next

	
	init = True
	
	Repeat
	
		;calculate time passed
		now# = MilliSecs()
		If init Then prev# = now#: inittime# = now: init = False
		elapsed# = now# - prev#
		prev# = now#
		
		;select quadrant
		mx = MouseX()
		my = MouseY()
		quad = (mx > 400) + (my > 300) * 2
		;set time for selected quadrant
		time(quad) = time(quad) + elapsed
		
		;graphical output
		Cls
		
		;draw grid
		Line 400, 0, 400, 600
		Line 0, 300, 800, 300
		
		;print out times
		For i = 0 To 3
			Text i Mod 2 * 400 + 200, i / 2 * 300 + 150, time#(i) / 1000.0
		Next
				
		;measure total time passed
		tot1# = ((time(0) + time(1) + time(2) + time(3)) / 1000.0)
		tot2# = (MilliSecs() - inittime#) / 1000.0

		;debug output		
		Text 0,  0, "You're in quadrant: " + quad
		Text 0, 20, "Total sum times:      " + tot1#
		Text 0, 40, "Total time millisecs: " + tot2#
		Flip

		;limit to 60 secs			
		If tot2# >= 3.0 Then 
			Err1# = tot2# - 3.0
			Err2# = tot2# - tot1#
			Text 0, 60, "Error1 (Total time - 3 sec.): " + Err1#
			Text 0, 80, "Error2 (Tot time - Tot sum):  " + Err2#
			Flip
			Exit
		EndIf
		
	Until KeyHit(1)
	
	Text 400, 290, "Press space to retry", True, True
	Text 400, 310, "Any other key to exit", True, True
	Flip
	
	Key = WaitKey()
	If Key = 32 Then Goto up
		
	End



The other strange thing is, as muk said, the error/delay is the same for 1 sec, 60 sec or more. I'm not sure to understand your solution muk. I need to flip continuously because of the graphics. How should I wait to get millisecs without flipping?

FiNegirO

Floats behave a bit strange:
x# = 0

For i = 1 To 1000
x# = x# + 0.0001
Next

Print x

WaitKey()
End

They are only as exact as they need to be. In the code above, it is precise until 0.0001 digits. All the stuff after that is just "rubbish". I believe it has something to do that inside the CPU floats are just integers as well.

Then in the graphical routine, it takes time to render each frame. So if you check the Millisecs() once a loop, it will not check every millisecond. Say the program renders at 30 FPS, then the loop repeats at 30FPS, so the "exit timer" checks at 30FPS, too. There is a deviation of one frame allways. The only thing you can do about this is check the exit timer more often.

Thanks for the clarification! But I don’t understand how could I check the exit timer more often while rendering the scene? Could you give me an example please?

FiNegirO

Well, you could for instance check the timer after each line of code.
x# = cos(p) * 10: CheckTimer()
y# = sin(p) * 10: CheckTimer()
oval x, y, 10, 10: CheckTimer()
for i = 0 to 10
  oval rand(x), rand(y), 10, 10: CheckTimer()
next

If you want to render in several steps, you could use a Type for all your objects.
For i.TObject = Each TObject
  ShowEntity i\mesh
  RenderWorld()
  HideEntity i\mesh
  CheckTimer()
Next

In fact, using threads does the same thing. Only then with two applications. Each application gets a certain amount of processor time.

There is a wait built in the "Flip" command. The VWait waits for the ScanLine() to be zero. It avoids nasty flickering of the image.
Graphics 800, 600, 0, 3
SetBuffer BackBuffer()

repeat

p = (p + 1) mod 2
clscolor 255 * p, 255 * p, 255 * p
cls

flip false

until keyhit(1)

end

During this wait, other application can perform their tasks. Same thing goes for Delay().
To get as close to the millisecs() accuracy, you should have:
* a tight loop
* check very often
start# = MilliSecs()
Print "START"
Repeat
time# = MilliSecs()
Until time >= start + 3000
Print "STOP"
Print time# - start#
WaitKey()
End


i guess the simplest way is like this, ie. don't flip the 15 millisecs before getting your time result, 20 is the margin of error.

Graphics3D 640,480,0,2

iniTime=MilliSecs()

While Not KeyHit(1)

 tempTime=MilliSecs()-iniTime
 If MilliSecs()>=iniTime+1000 ;1 second test
  If once=0
   once=1
   Latency#=(MilliSecs()-iniTime)/1000.0
   testTime=MilliSecs()-iniTime
   testFloat#=1000/1000.0
  EndIf
 EndIf

 Cls
 Text 0,0,"Latency="+Latency+" testFloat="+testFloat
 Text 0,12,"testTime="+testTime+" iniTime="+iniTime+" tempTime="+tempTime

 If MilliSecs()>=iniTime+1000-15 And MilliSecs()<=iniTime+1000+5
 Else
  Flip
 EndIf

Wend


ah, yes that should work perfectly!

Ok!
Timing is better (still not perfect) but what happens with the graphics?

Try this:

Graphics3D 640,480,0,2


camera=CreateCamera()
light=CreateLight()
RotateEntity light,90,0,0
cube=CreateCube()
PositionEntity cube,0,0,5


iniTime=MilliSecs()
OneSecondTime = iniTime

While Not KeyHit(1)

 tempTime=MilliSecs()-iniTime
 If MilliSecs()>=OneSecondTime+1000 ;1 second test
  Latency#=(MilliSecs()-OneSecondTime)/1000.0
  testTime=MilliSecs()-OneSecondTime
  OneSecondTime = MilliSecs()
  testFloat#=1000/1000.0
 EndIf

 UpdateWorld
 RenderWorld
 
 Text 0,0,"Latency="+Latency+" testFloat="+testFloat
 Text 0,12,"testTime="+testTime+" iniTime="+iniTime+" tempTime="+tempTime
 ;Text 50,50,"" + MilliSecs() Mod 1000

 TurnEntity cube,-.4,.2,.3

 If (MilliSecs() - OneSecondTime) Mod 1000 < 985 And (MilliSecs() - OneSecondTime) Mod 1000 > 5 
  Flip ;flip only if "far" from time checking
 EndIf

Wend


Could you see the gap in the render process? It's a big problem in a real time game.

Any idea to solve it?

Thanks a lot,
FiNegirO

the main problem there is you're updating the cube every loop so it's not syncing with the flip and causing that big jump. the solution is to group any graphics code in with the flip as well.

i changed your mod thingy as it gave some other kind of problem and i couldn't understand how to fix it.

Graphics3D 640,480,0,2
SetBuffer BackBuffer()

camera=CreateCamera()
light=CreateLight()
RotateEntity light,90,0,0
cube=CreateCube()
PositionEntity cube,0,0,5

iniTime=MilliSecs()
nextTime = iniTime

While Not KeyHit(1)

 tempTime=MilliSecs()-iniTime
 If MilliSecs()>=nextTime+1000 ;1 second test
  Latency#=(MilliSecs()-nextTime)/1000.0
  testTime=MilliSecs()-nextTime
  nextTime = MilliSecs()
  testFloat#=1000/1000.0
 EndIf

If Not MilliSecs()>=nextTime+1000-30 And MilliSecs()<=nextTime+1000+20

 UpdateWorld
 RenderWorld
 
 Text 0,0,"Latency="+Latency+" testFloat="+testFloat
 Text 0,12,"testTime="+testTime+" iniTime="+iniTime+" tempTime="+tempTime
 ;Text 50,50,"" + MilliSecs() Mod 1000

 ;If (MilliSecs()-nextTime) Mod 1000<985 And (MilliSecs()-nextTime) Mod 1000>5 
 TurnEntity cube,-.4,.2,.3

 Flip ;flip only if "far" from time checking
EndIf

Wend


Thanks muk! It worked for me. Now I get better timing without graphics gaps. Still there are some time errors. I'm trying to do time check more often, as bram32 said. I hope to get "almos perfect" results. I'll tell you.

cheers
FiNegirO

Hey, I was thinking .. maybe you could play a .WAV that is exactly 1 minute and use Channelplaying ?
edit: No, that would be the same actually.. Nevermind that. I was just thinking about the .wav playing as an independant process.