My throttle/speed code, what is up with this...

Blitz3D Forums/Blitz3D Programming/My throttle/speed code, what is up with this...

OrcSlayer(Posted 2005) [#1]
I have a little code here that isn't working quite the way I think it should...

Note that this is a rough draft simply used for testing, but the basic principle with speed/throttle will be the same. The velocity application is just a quick move command rather than the more complicated one I'll actually end up using.

Note that all values except throttle are declared with a # (throttle only needs to be a whole number), and acceleration is 0.1 for this test.

If KeyDown(13) Then
				If s\Throttle < s\MaxSpeed Then
					s\Throttle = s\Throttle + 1
				EndIf
			ElseIf KeyDown(12) Then
				If s\Throttle > 0 Then
					s\Throttle = s\Throttle - 1
				EndIf
			EndIf
			
			If s\CurrentSpeed < s\Throttle Then
				s\CurrentSpeed = s\CurrentSpeed + s\Acceleration
			ElseIf s\CurrentSpeed > s\Throttle Then
				s\CurrentSpeed = s\CurrentSpeed - s\Acceleration
			EndIf
			
			MoveEntity(s\Entity,0,0,s\CurrentSpeed)


Basically, when I used something similar in an older Basic language I worked with, it did great, but here I have a problem...instead of landing correctly on the actual throttle value, it ends up some funky value like say, if the throttle is 5, it will be 4.9999 or something like that (hard to see, it jitters too much for an accurate reading on my test display). At 0, it ends up with some really strange value that is actually still moving, at considerable speed in fact (slightly less than 1 I'd guess, but certainly not stopped. Sometimes it goes negative as well moving the ship backwards).

Any way I can ensure the speed stops ON the throttle value, instead of a few decimal places away from it?

Sorry, I forgot the code box value, haven't used the forums in a while...


Ash_UK(Posted 2005) [#2]
Hmmm, have you tried using the INT command to convert the float into an integer?

eg. text 0,0,Int(s\Throttle)


Ash_UK(Posted 2005) [#3]
By the way, the code box commands for the forum are:

[ code ]
[ /code ]

or

[ codebox ]
[ /codebox ]


OrcSlayer(Posted 2005) [#4]
Well, that would work for rounding off the display, but in this case I need the actual speed to be rounded off when it reaches the set throttle, instead of "floating" near it. If I jumped the speed in integers, it would be crazy, yet as is it acts pretty funky and you can't really stop.

The way the code looks to me it should work fine, increasing or decreasing the speed until it matches the throttle, which should work as long as I use a simple value for acceleration. I have no idea why it's giving such odd values on my display, and why at throttle 0 my ship continues to coast along at a fair rate.

Perhaps I should create a "catcher" that detects if the speed is within a certain amount of the throttle setting, and snaps it to it. I was hoping there was an easier way though...

-OrcSlayer


Stevie G(Posted 2005) [#5]
Probably a result of floating point inaccuracy.

try this old bit of code ....

Graphics3D 640,480,16,1

Global plane = CreatePlane():EntityColor plane, 50,50,100:EntityAlpha plane, .75
texture = CreateTexture( 16,16)
SetBuffer TextureBuffer( texture )
Color 0,0,0:Rect 0,0,16,16,1
Color 50,50,200:Rect 0,0,16,16,0
SetBuffer BackBuffer()
ScaleTexture texture,20,20
EntityTexture plane, texture
FreeTexture texture

Global camera = CreateCamera():PositionEntity camera, 0, 50, -100
Global mirror = CreateMirror()

Global light = CreateLight():RotateEntity light , 15,-45,0

Type Car
	Field Model
	Field Wheel[3]
	Field WheelRotation#
	Field Wheelradius#
	Field TopSpeed#
	Field Drag#
	Field Acceleration#
	Field Brakes#
	Field Speed#
	Field Vx#, Vy#, Vz#
End Type

Global MyCar.car = CARcreate( 4, 3, 6, 2, 3 )

While Not KeyDown(1)

	CARupdate( MyCar )
	PointEntity camera, MyCar\model
	CameraZoom camera, EntityDistance( camera, MyCar\model ) / 100
	RenderWorld()
	Flip

Wend

;==========================================
;==========================================
;==========================================

Function CARupdate( c. car )
	
	;user input
	jx# = KeyDown(203) - KeyDown(205 )
	jb#  = KeyDown( 200)
	
	TurnEntity c\Model, 0, jx * 3.0,0
	For w = 0 To 1
		RotateEntity c\wheel[w], 0 , jx*45, 0
	Next
	
	c\Speed = LIMIT ( c\Speed + ( jb * c\Acceleration ) - ( (Not jb ) * c\Brakes ) , 0, c\TopSpeed )
	TFormVector 0,0,(1.0 - c\drag ), c\Model, 0
	c\Vx = ( c\Vx * c\drag ) + ( TFormedX() * c\Speed )
	c\Vz = ( c\Vz * c\drag ) + ( TFormedZ() * c\Speed )
	c\Vy = ( c\Vy * c\drag ) + ( TFormedY() * c\Speed ) 
	speed# = Sqr( c\Vx*c\Vx + c\Vy*c\Vy + c\Vz*c\Vz )

	TranslateEntity c\Model, c\Vx, c\vy , c\Vz
	
	c\WheelRotation = c\WheelRotation + Speed * 90.0 / ( Pi * c\WheelRadius )  
	For w = 0 To 3
		RotateEntity c\wheel[ w] , c\WheelRotation, EntityYaw( c\wheel[w] ) , 0
	Next

End Function

;==========================================
;==========================================
;==========================================

Function CARcreate.car( w#, h#, d# , tw# , tr# )

	c.car = New car
	c\Acceleration = .0075
	c\Brakes = .005 
	c\Drag = .95
	c\Speed = 0.0
	c\TopSpeed = 1.25
	c\Wheelradius = tw
		
	c\Model = CreateCube()
	FitMesh c\Model , -w,-h,-d, w*2,h*2,d*2
	UpdateNormals c\Model
	EntityColor c\Model,255,255,0
	
	Wheel = CreateCylinder( 5 )
	RotateMesh Wheel,0,0,90
	ScaleMesh Wheel , tw,tr,tr
	UpdateNormals Wheel
	EntityColor Wheel, 64,64,64

	For l= 0 To 3
		dx = ( l >0 And l<3 ) - ( l = 0 Or l = 3 )
		dz = ( l < 2 ) - ( l > 1 )
		c\wheel[l] = CopyEntity(wheel , c\Model )
		PositionEntity c\wheel[l], w*dx , -h , d*dz
	Next
	
	PositionEntity c\Model, 0 , tr+h, 0
	
	FreeEntity Wheel
	
	Return c
	
End Function

;==========================================
;==========================================
;==========================================

Function LIMIT#(n#,n1#,n2#)
	
	If n>n2 Then n=n2
	If n<n1 Then n=n1	
	Return n

End Function



OrcSlayer(Posted 2005) [#6]
Ah, I see...thanks for the info, I think I can fix it now...


OrcSlayer(Posted 2005) [#7]
Just a little follow up, I created a very simple "catch" which seems to work well...I simply added this after the above function...

If Int(s\CurrentSpeed) = s\Throttle Then
				s\CurrentSpeed = s\Throttle
			EndIf


Now the speed actually stops on the throttle value, and the sudden "jerk" into place is not actually noticable to me...


octothorpe(Posted 2005) [#8]
That'll jerk you around by up to .5. This would be smoother, jerking you around by only up to (.6 of) s\Acceleration, which really shouldn't be noticable at all because you'd only be eliminating the accrued floating point inaccuracies.

If Abs(s\CurrentSpeed - s\Throttle) < s\Acceleration * .6 Then
	s\CurrentSpeed = s\Throttle
Endif


Note that I used .6 instead of .5 just in case ((speed + accel) - speed > accel) due to floating point inaccuracies.

Or consider getting rid of floating point inaccuracies (as best as possible) every frame, regardless of how close to the target value you are:

s\CurrentSpeed = Int(s\CurrentSpeed / s\Acceleration) * s\Acceleration


Or the more generic:

s\CurrentSpeed = Int(s\CurrentSpeed * 10000) / 10000


An alternate approach which has always worked for me in the past (tested for accel=.1 target=8):

If speed < target Then
	speed = speed + min(Abs(target-speed), accel)
ElseIf speed > target Then
	speed = speed - min(Abs(target-speed), accel)
EndIf

Function min#(a#, b#)
	If a < b Then Return a Else Return b
End Function


If s\Acceleration is expected to change while driving, keep in mind what effect that'll have on your speed; imagine a situation where you've already accumulated speed with accel=.09 and then you set accel=.12. Consider that target may not be evenly divisible by accel. The first solution above will snap the speed to the target when it gets as close as it'll ever get to the target, meaning that the speed could jerk by up to (a little over) half of accel. The last solution is independant of changes to accel, but will sometimes take an extra frame to reach the target under normal circumstances as it adds the final .0000000001 (or whatever the accrued floating point inaccuracy is.)


OrcSlayer(Posted 2005) [#9]
octothorpe, excellent help! You are right, I soon realised how extreme the difference was with the int catch, and your methods aren't much more complex. I'm just not that familiar with the math functions you used...

Seems my throttle issue is fixed, thanks again!


octothorpe(Posted 2005) [#10]
Glad to help! :)

I'm likely going to be unable to give you better definitions than the language documentation, but if you want to know why I'm using the functions in a specific way, I'll be happy to explain: just ask.

I can imagine that my use of Abs() might have caused a little confusion. Abs(x-y) gives you the difference between x and y, regardless of which is greater. Abs(x-y) = Abs(y-x). In the first example, it was necessary for the logic. In the last example, I just thought it read better; without Abs(), the order of the subtraction matters:

If speed < target Then
	speed = speed + min(target-speed, accel)
ElseIf speed > target Then
	speed = speed - min(speed-target, accel)
EndIf


min() is just a convenient way of saying: change speed by either (a) accel or (b) the remainder of the way to the target speed, whichever is less/closest.