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. |