vector movement question, movement don't stop

Monkey Forums/Monkey Beginners/vector movement question, movement don't stop

GC-Martijn(Posted 2015) [#1]
H!

Its a long time ago that I used Vectors, I hope that this is the correct way of using them.
The final goal is to move a object from A to Mouse Click X,Y and then stop.

I draw the image like this at the moment.
DrawImage img, Position.X-(img.Width()/2), Position.Y-(img.Height()/2), frame


But where i'm stuck is that it has to move to my mouse x,y click.
It does, but its stuttering at the end, because at the that point its still adding direction.
Because I can't detect when its at the endpoint.

Destination = New Vector(90,90) 'simulate a mouse click'
Direction = Destination.Subtract(Position)

Print Direction.Length

 ' never <= 0 
If Direction.Length<=0 Or Position.DistanceTo(Destination)<=0
    Print "Stop"
Else
    Direction.Normalize()
    Direction.Multiply(dt.delta)	' delta fix
    Position.Add(Direction)
End


Output
56.56854248046875
55.608543395996094
52.848545074462891
52.848545074462891
52.548545837402344
51.528545379638672
50.568546295166016
49.608547210693359
48.528549194335938
47.568550109863281
46.608551025390625
45.588550567626953
44.568550109863281
43.608551025390625
42.528553009033203
41.568553924560547
40.548553466796875
39.528553009033203
38.448554992675781
37.548557281494141
36.528556823730469
35.568553924560547
34.548549652099609
33.588542938232422
32.568538665771484
31.548532485961914
30.528528213500977
29.568523406982422
28.548519134521484
27.528514862060547
26.568510055541992
25.548505783081055
24.528499603271484
23.568496704101562
22.548490524291992
21.58848762512207
20.568483352661133
19.548477172851562
18.588474273681641
17.56846809387207
16.608463287353516
15.528469085693359
14.568465232849121
13.548460006713867
12.528454780578613
11.568450927734375
10.548445701599121
9.5884418487548828
8.5684366226196289
7.5484318733215332
6.5284266471862793
5.568422794342041
4.5484180450439453
3.5284128189086914
2.568408727645874
1.5484037399291992
0.58839964866638184
0.43160530924797058
0.52839875221252441
0.49160623550415039
0.52839875221252441
0.49160623550415039
0.4683978259563446
0.55160719156265259
0.4683978259563446
0.55160719156265259
0.40839689970016479
0.61160808801651001
0.40839689970016479
0.49160623550415039
0.58838886022567749
0.43161609768867493
0.52838796377182007
0.43161609768867493
0.64837902784347534
0.31162503361701965
0.70837992429733276
0.25162410736083984
0.76838088035583496
0.25162410736083984
0.76838088035583496
0.19162318110466003
0.82838177680969238
0.19162318110466003
0.76838088035583496
0.25162410736083984
0.76838088035583496
0.19162318110466003
0.82838177680969238
0.19162318110466003
0.76838088035583496
0.25162410736083984
0.70837992429733276
0.31162503361701965
0.70837992429733276
0.25162410736083984
0.76838088035583496
0.25162410736083984
0.76838088035583496
0.19162318110466003
0.82838177680969238
0.19162318110466003
0.76838088035583496
0.25162410736083984
0.70837992429733276
0.31162503361701965
0.70837992429733276
0.31162503361701965
0.64837902784347534
0.43161609768867493
0.46838703751564026
0.55161798000335693
0.46838703751564026
0.55161798000335693
0.40838611125946045
0.61161887645721436
0.34838518500328064
0.67161983251571655
0.34838518500328064
0.67161983251571655
0.28838425874710083
0.73162072896957397
0.28838425874710083
0.67161983251571655
0.34838518500328064
0.67161983251571655
0.28838425874710083
0.73162072896957397
0.28838425874710083
0.67161983251571655
0.34838518500328064


Duke87(Posted 2015) [#2]
Maybe you shlould try something like <= 1.3 (or even better: smaller than your delta-timing-step value).
I guess it's because, the chance the distance will be exactly 0.0 or even less is like 0,0000000000000000000000001%.
Because that would mean, that your
" Direction.Multiply(dt.delta) ' delta fix
Position.Add(Direction)" will be exactly(!!!) the same value, than your Distance is.
It will add your vector*delta-step, so if the distance is like 0.5 (from left) it will add something like (1.1Pxl), so now your object is like 0.6Pxl on the right side, now it will change the vector and add again something like 1.1Pxl and move again to the left, now its again something like 0.5 Pxl on the left and so on and so on.

Edit:
Maybe try Position.DistanceTo(Destination)<= dt.delta
or something like this


GC-Martijn(Posted 2015) [#3]
Thanks for the info Duke87
I did copy past to check If Position.DistanceTo(Destination)<=dt.delta to check if that was the answer, but it's not :(

Going to try other things now ;)


Duke87(Posted 2015) [#4]
try <= 1.5.
I'm not sure what dt.delta is. the value that i mean that has to be checked, is the delta step- value. the Pixels you shift your Object per Frame.


Paul - Taiphoz(Posted 2015) [#5]
dt.delta would be a delta facotor if your using delta time, can you post your full source or a working example ?

also If Direction.Length<=0 will not be met because the output your showing is trending toward not reaching zero you can do either..

if Direction.Length<1 which I assume would be 1 or there abouts.

or

if Int(Direction.Length<=0) then . << Typecast the float to an int to get rid of the unneeded decimals.


Jesse(Posted 2015) [#6]
See if that Helps:
Import mojo

Function Main()
	New Game
End Function

Class Game Extends App

	Field Direction:Vector
	Field Position:Vector
	Field Destination:Vector
	Field v1:Vector
	Field v2:Vector
	
	Method OnCreate()
		Destination = New Vector(250,250) 'simulate a mouse click'
		Position = New Vector(90,90)
		
		SetUpdateRate 30
		
	End Method
	
	Method OnUpdate()
		Direction = New Vector(Destination).Subtract(Position)
		v1 = New Vector(Direction)		
		Direction.Normalize()
		Direction.Multiply(1.5)	' delta fix
		Position.Add(Direction)
		v2 = New Vector(Destination).Subtract(Position)
		If v2.DotProduct(v1)<= 0.0 Error "Found collision" 
	End Method
	
	Method OnRender()
		Cls()
		DrawCircle Position.x,Position.y,10
		DrawRect Destination.x-4,Destination.y-4,8,8
	End Method
	
End Class

Class Vector
	Field x:Float
	Field y:Float
	
	Method New(x:Float,y:Float)
		Self.x = x
		Self.y = y
	End Method
	
	
	Method New(v:Vector)
		Self.x = v.x
		Self.y = v.y
	End Method
	
	Method Add:Vector(x:Float,y:Float)
		Self.x += x
		Self.y += y
		Return Self
	End Method
	
	Method Add:Vector(v:Vector)
		Self.x += v.x
		Self.y += v.y
		Return Self
	End Method
	
	Method Subtract:Vector(v:Vector)
		Self.x -= v.x
		Self.y -= v.y
		Return Self
	End Method
	
	Method Multiply:Vector(value:Float)
		Self.x *= value
		Self.y *= value
		Return Self
	End Method

	Method Multiply:Vector(v:Vector)
		Self.x *= v.x
		Self.y *= v.y
		Return Self
	End Method
	
	Method Divide:Vector(value:Float)
		Self.x /= value
		Self.y /= value
		Return Self
	End Method
	
	Method DotProduct:Float(v:Vector)
		Return Self.x * v.x + Self.y * v.y
	End Method

	Method Normalize:Vector()
		Divide(Magnitude())
		Return Self
	End Method
		
	Method MagnitudeSquare:Float()
		Return Self.x * Self.x + Self.y * Self.y
	End Method
	
	Method Magnitude:Float()
		Return Sqrt(MagnitudeSquare())
	End Method
	
End Class



ImmutableOctet(SKNG)(Posted 2015) [#7]
Just for the record, Taiphoz, your cast was casting the boolean result of the expression, simple oversight. Also, static casting tends to be inefficient for that sort of thing, you should be using 'Ceil' and 'Floor' to approximate the position. I should also mention that Jesse's example, though potentially working, is inefficient. And when it comes to math, performance needs to be optimal. In addition to that, making new objects every update is a nightmare on the Android targets. It's best to keep this to enumerators (Even that can be a problem at times; some of my own code caches enumerators). Just because you're dealing with fields, that doesn't mean 'New' will function differently; they're not scope-allocated, they're references.

Also, Jesse, that example works, but his problem is the inconsistent position afterward. Comment out your 'Error' line, and you'll see the problem. Other than that, solid example.

Okay, so as I said above, and to some extent what Taiphoz said, use 'Ceil' and 'Floor'. Basically, taking Jesse's example, add this 'If' statement around his 'OnUpdate' routine:


Note that 'Ceil' and 'Floor' can sometimes be problematic for exact comparison, so you might still need to approximate (Distance check using a "delta" value. Something like: ((X-Y) < 0.5)).

There, now it won't even bother if the destination is close enough. Alternatively to this setup, you could get a more gradual interpolation effect by simply dividing/multiplying the position-delta (Remove the use of 'Normalize', and try 0.05 or similar; you can change this to change the "speed"). Normalizing the vector may be your intended effect, though. I personally like the multiplication approach; looks nicer to me, takes fewer operations to perform. Also keep in mind what I said above, please make new vectors as infrequently as possible. The standard C++ garbage collector Monkey comes with is good at dealing with this kind of thing, but Android (And XNA) can be problematic.


Jesse(Posted 2015) [#8]

I should also mention that Jesse's example, though potentially working, is inefficient


my Intentions was not to show efficiency. I save that for the advanced programmer. I was more into illustrating the proper way of checking for collision with vectors.
this should solve both problems :P
Import mojo

Function Main()
	New Game
End Function

Class Game Extends App

	Field Direction:Vector
	Field Position:Vector
	Field Destination:Vector
	Field v1:Vector
	Field v2:Vector
	Field dt:Float = 7.2
	
	Method OnCreate()
		Position = New Vector(90,90)
		Direction =New Vector
		Destination = New Vector
		v1 = New Vector
		v2 = New Vector
		SetUpdateRate 30
		
	End Method
	
	Method OnUpdate()
		Destination.Set(MouseX(),MouseY()) 'simulate a mouse click'
		Direction.Set(Destination).Subtract(Position)
		v1.Set(Direction)		
		Direction.Normalize().Multiply(dt)	' delta fix
		Position.Add(Direction)
		v2.Set(Destination).Subtract(Position)
		If v2.DotProduct(v1)<= 0.0 
			Position.Set(Destination)
			'Error "Found collision" 
		Endif 
	End Method
	
	Method OnRender()
		Cls()
		DrawCircle Position.x,Position.y,10
		DrawRect Destination.x-4,Destination.y-4,8,8
	End Method
	
End Class

Class Vector
	Field x:Float
	Field y:Float
	
	Method New(x:Float,y:Float)
		Self.x = x
		Self.y = y
	End Method
	
	Method Set:Vector(v:Vector)
		Self.x = v.x
		Self.y = v.y
		Return Self
	End Method
	
	Method Set:Vector(x:Float,y:Float)
		Self.x = x
		Self.y = y
		Return Self
	End Method
	
	Method New(v:Vector)
		Self.x = v.x
		Self.y = v.y
	End Method
	
	Method Add:Vector(x:Float,y:Float)
		Self.x += x
		Self.y += y
		Return Self
	End Method
	
	Method Add:Vector(v:Vector)
		Self.x += v.x
		Self.y += v.y
		Return Self
	End Method
	
	Method Subtract:Vector(v:Vector)
		Self.x -= v.x
		Self.y -= v.y
		Return Self
	End Method
	
	Method Multiply:Vector(value:Float)
		Self.x *= value
		Self.y *= value
		Return Self
	End Method
	
	Method Multiply:Vector(v:Vector)
		Self.x *= v.x
		Self.y *= v.y
		Return Self
	End Method
	
	Method Divide:Vector(value:Float)
		If value <> 0
			Self.x /= value
			Self.y /= value
		Else
			Self.x = 0
			Self.y = 0
		Endif
		Return Self
	End Method
	
	Method DotProduct:Float(v:Vector)
		Return Self.x * v.x + Self.y * v.y
	End Method

	Method Normalize:Vector()
		Divide(Magnitude())
		Return Self
	End Method
		
	Method MagnitudeSquare:Float()
		Return Self.x * Self.x + Self.y * Self.y
	End Method
	
	Method Magnitude:Float()
		Return Sqrt(MagnitudeSquare())
	End Method
	
End Class


Interesting how many programmers use vectors but not many know how to use Dot Product.


ImmutableOctet(SKNG)(Posted 2015) [#9]
Funny enough, I didn't even think to use dot-product like this. I normally just use it to detect the difference between the user's acceleration and the velocity, and "compensate" based on how much it deviates. I'll get around to refactoring that module eventually. If it wasn't obvious already, I'm definitely going to use your strategy in my own projects, thanks Jesse.


GC-Martijn(Posted 2015) [#10]
I contacted the owner yesterday and the solution was very simple.
I use this his vector class : http://www.monkey-x.com/Community/posts.php?topic=568

And I only needed to use the Method ReduceLength()

So my code is now exactly stopping at 0.0
With only one line.

Destination = New Vector(90,90) 'simulate a mouse click'
Direction = Destination.Subtract(Position)

Direction.ReduceLength(Speed*dt.delta) '' with this line it becomes 0.0 at the end.
If Direction.Length<=0 Or Position.DistanceTo(Destination)<=0
Print "Yes it did Stop loop"
Else
Direction.Normalize()
Direction.Multiply(dt.delta) ' delta fix
Position.Add(Direction)
End

' will try to make a small working example


Pakz(Posted 2015) [#11]
I seriously need to be studying more on what you can do with classes :) Vectors I still barely understand. I converted 5 examples from blitz basic to monkey and have been reading through that to learn to understand what is happening and what does it. The vector class here on this thread and the one linked on the code forum has methods that makes me scratch my head even more.

I am still so used to procedural programming. The example source code on this thread and in the code forum is something I wil need to read through more.

Great thread this though !!


GC-Martijn(Posted 2015) [#12]
@Jesse I forgot to say that your code is working, testing both solutions now.

The full working code with some things cut-out is now this.
fully working moving a sprite from A to B using mouse click.


Import mojo
Import classes.vector

Function Main:Int()
	New MyApp
	Return 0
End

Global dt:DeltaTimer

Class MyApp Extends App
	Global FPS:Int = 60
	
	Field scene:Scene = New Scene()

	Method OnCreate:Int()
		dt = New DeltaTimer(FPS)
		SetUpdateRate(FPS)
		
	'	Local sprite:Sprite = New Sprite()
	
		scene.Load()

    	sprite1.Load("atlas-thing.json")
    	sprite1.Move(50,50)
 

		Return 0
	End
	
	
	Method OnRender:Int()
		Cls(255, 255, 255)

		scene.DebugDraw()

		SetColor(255, 255, 255)
		
		sprite1.Draw()

		DrawText "Use <- and -> plus ENTER to change game update rate (currently " + FPS + " fps)", 0, 60
		Return 0
	End
	
	
	Method OnUpdate:Int()
		dt.UpdateDelta

		If KeyHit (KEY_LEFT)
			FPS = FPS - 10
			If FPS < 10 Then FPS = 10
			SetUpdateRate FPS
		Endif
		If KeyHit (KEY_RIGHT)
			FPS = FPS + 10
			SetUpdateRate FPS
		Endif
		If KeyDown (KEY_ENTER)
			FPS = 60
			SetUpdateRate FPS
		Endif

		If MouseHit( MOUSE_LEFT )
			sprite1.MoveTo(MouseX(),MouseY())
		End

		
		sprite1.Update()
		Return 0
	End

	Method OnResume:Int()
		dt.currentticks = Millisecs()
		dt.lastticks = dt.currentticks
		Return 0
	End	
End


Class Sprite
	Field img:Image
	
	' movement
	Field Speed:Float			= 1
	Field Direction:Vector 
	Field Position:Vector
	Field Destination:Vector 

	' frame animation
	Field frames:Int = 0
	Field frame:Int
	Field frameTimer:Int
	Field frameEnd:Int
	Field frameDuration:Int[]

	Method Draw:Void()
		DrawImage img, Position.X-(img.Width()/2), Position.Y-(img.Height()/2), frame
	End

	Method Update:Void()

		' frame animation
		If frames > 0
			If dt.lastticks > frameTimer + frameDuration[frame]
				frame+=1
				If frame > frameEnd
					frame = 0
				End
				frameTimer = Millisecs()
			End	
		End
    	

	    Direction.Set(Destination).Subtract(Position)
		Direction.ReduceLength(Speed*dt.delta)
		If Direction.Length>0
			Direction.Normalize()
			Direction.Multiply(Speed*dt.delta)	' delta fix
			Position.Add(Direction)
		End
	End

	Method MoveTo:Void(_mx:Float, _my:Float)
		Destination.Set(_mx,_my)
	End

	Method Move:Void(_mx:Float, _my:Float)
		Position = New Vector(_mx,_my)
		Destination = New Vector(_mx,_my)
		Direction = New Vector
	End

End


Class DeltaTimer	
	Field targetfps:Float = 60
	Field currentticks:Float
	Field lastticks:Float
	Field frametime:Float
	Field delta:Float
	
	Method New (fps:Float)
		targetfps = fps
		lastticks = Millisecs()
	End
	
	Method UpdateDelta:Void()
		currentticks = Millisecs()
		frametime = currentticks - lastticks
		delta = frametime / (1000.0 / targetfps)
		lastticks = currentticks
	End
End



Jesse(Posted 2015) [#13]
Good to know it was useful. I wish more programmers learn to use the Dot product. it is very useful. Many things can be resolve with it and many times will eliminate square roots and exponential math. I mainly use it for collision, distances and relationship to other vectors.