vector movement question, movement don't stop
Monkey Forums/Monkey Beginners/vector movement question, movement don't stop
| ||
| 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 |
| ||
| 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 |
| ||
| 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 ;) |
| ||
| 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. |
| ||
| 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. |
| ||
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 |
| ||
| 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. |
| ||
| 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. |
| ||
| 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. |
| ||
| 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 |
| ||
| 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 !! |
| ||
| @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
|
| ||
| 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. |