sin,cos movement help

BlitzMax Forums/BlitzMax Beginners Area/sin,cos movement help

coffeedotbean(Posted 2010) [#1]
Hi all

Any one good wih the whole Sin,Cos stuff? I have been trying for the last hour to get the below code to work, the Red circle should go the location of the mouse when the mouse button is pressed, in a nice smooth movement with gentle acceleration and decceleration. I feel I am almost there but after a while of searching these forums I cannot quite get it to work.

Any tips?

Strict

Graphics 800 , 600 , 0 , 32
SetBlend ALPHABLEND

Global L_enemy:TList = CreateList()
Type T_enemy
	Field x:Float
	Field y:Float
	Field size:Float
	Field angle:Float
	Field speed_x:Float
	Field speed_y:Float
	Field acceleration_x:Float
	Field acceleration_y:Float
	Field target_x:Float
	Field target_y:Float
	
	'// 
	Function Add(x:Float , y:Float , size:Float)
		Local a:T_enemy = New T_enemy
			a.x		 = x
			a.y		 = y
			a.size	 = size
		ListAddLast(L_enemy,a)
	End Function
	
	'// 
	Function Update()
		
		Local MaxSpeed:Float = 4
		Local Acceleration:Float = 0.1
		
		For Local a:T_enemy = EachIn l_enemy
			a.angle = ATan( (a.target_x - a.x) / (a.target_y - a.y) )
			' smooth acceleration?
			If a.target_x > a.x Then a.speed_x:+ Acceleration Else a.speed_x:- Acceleration
			If a.target_y > a.y Then a.speed_y:+ Acceleration Else a.speed_y:- Acceleration
			If a.speed_x > MaxSpeed Then a.speed_x = MaxSpeed
			If a.speed_y < -MaxSpeed Then a.speed_y = -MaxSpeed
			If a.speed_x > MaxSpeed Then a.speed_x = MaxSpeed
			If a.speed_y < - MaxSpeed Then a.speed_y = - MaxSpeed
			
			a.acceleration_x = a.speed_x * Cos(a.angle)
			a.acceleration_y = a.speed_y * Sin(a.angle)
			a.x:+ a.acceleration_x
			a.y:+ a.acceleration_y
			a.Draw()
			DrawText "angle: " + a.angle , 10 , 10
			DrawText "speed x: " + a.speed_x , 10 , 30
			DrawText "speed y: " + a.speed_y , 10 , 50
		Next
	End Function
	
	'// 
	Method Draw()
		SetColor 255 , 0 , 0 ; SetAlpha 0.5
			DrawOval x-(size/2) , y-(size/2) , size , size
		SetAlpha 1.0 ; SetColor 255 , 255 , 255
	End Method
	
	'//
	Function ChangeFocus(target_x:Float , target_y:Float)
		For Local a:T_enemy = EachIn l_enemy
			a.target_x = target_x
			a.target_y = target_y
		Next
	End function

End Type

'// There will only ever be one enemy.
T_enemy.Add(GraphicsWidth()/2,GraphicsHeight()/2,250)
T_enemy.ChangeFocus(0 , 0)

'//
Repeat ; Cls ; Delay 3
	
	T_enemy.Update
	If MouseHit(1) = True Then T_enemy.ChangeFocus( MouseX() , MouseY() )
	
Flip ; Until KeyDown(KEY_ESCAPE) ; End




Warpy(Posted 2010) [#2]
You're mixing up cos/sin and component speeds. Speed is a vector, which you can think of as either an x-component and a y-component, OR as an angle and a magnitude. To get this clear in your head, get a bit of graph paper and draw a line at an angle. Measure the difference between the start and end of the line in horizontally and vertically. Those are the x- and y-components. Now measure the angle the line makes with the horizontal, and the length of the line (the magnitude).

Where sin and cos come in is this: the x-component is the magnitude * cos(angle), and the y-component is the magnitude * sin(angle).

And y/x = (m*sin(angle)) / (m*cos(angle)) = sin(angle)/cos(angle) = tan(angle).

Finally, using Pythagoras' Theorem, we can get the magnitude from the x- and y-components by m^2 = x^2 + y^2

In fact, you don't really need to use sin or cos at all to do what you want to do. You can do it all using the components.
Local dx:Float, dy:Float, d:Float
dx = a.target_x - a.x   '(dx,dy) is a vector from the enemy to its target
dy = a.target_y - a.y

d = Sqr( dx*dx + dy*dy ) 'get distance from enemy to target using pythagoras

dx = dx/d  'divide the vector by its length to get a vector in the same direction but of magnitude 1. We can then multiply this vector by the amount we want to make it the right size
dy = Acceleration * dy/d

a.acceleration_x = Acceleration * dx  'Multiply by Acceleration to make the amount the enemy will accelerate by this frame
a.acceleration_y = Acceleration * dy

a.speed_x = a.speed_x + dx  'add the acceleration to the enemy's speed
a.speed_y = a.speed_y + dy

'now the enemy might be travelling faster than is allowed, so we might have to scale its speed down, in the same way as before
Local v:Float
v = Sqr( a.speed_x * a.speed_x + a.speed_y * a.speed_y )

If v>MaxSpeed
  a.speed_x = MaxSpeed * a.speed_x / v
  a.speed_y = MaxSpeed * a.speed_y / v
EndIf


Hope that helps.


coffeedotbean(Posted 2010) [#3]
thanks Warpy I wont pretend I understand it just yet, but when I get home ill run the code and play about till I do.


_Skully(Posted 2010) [#4]
Using Sin/Cos/ATAN2

local dx:float,dy:float, d:float, a:float
a=ATAN2(a.target_y-a.y,a.target_x-a.x)   ' angle between destination and target
dx=cos(a)
dy=sin(a)


Of course, Warpy's method is faster... I just added this because its another way of doing it.


explosive(Posted 2010) [#5]
warpy's method is not necessarily faster.

I really prefer using sin and cos for movements. And to make execution really, really fast you can store the sin an cos values of important, let's say 1440, angles in an array.

Now the processor only has to computer a simple floating-point multiplication.