Beziers, following them..

BlitzMax Forums/BlitzMax Beginners Area/Beziers, following them..

So I have my aliens following beziers but since the bezier's points are not evenly distributed the aliens cant seem to maintain the right speed.

A friend of mine who knows visual basic told me to find the angle that points towards the next point in the bezier and have the alien fly towards the next point at one pixel per update.

I asked him "how do I find the angle?"

he gives me this VB Function (which I cant use in BMAX)
Public Function getAngle(ByVal x1 As Single, ByVal y1 As Single, ByVal x2 As Single, ByVal y2 As Single) As Single
    
    On Error Resume Next
    
    Dim xLength As Single
    Dim yLength As Single
    
    xLength = x1 - x2: yLength = y1 - y2
    
    'look for stupid stuff
    If xLength = 0 Then
        If yLength > 0 Then getAngle = 0: Exit Function
        If yLength < 0 Then getAngle = 180: Exit Function
        If yLength = 0 Then Exit Function
    End If
    
    If yLength = 0 Then
        If xLength < 0 Then getAngle = 90: Exit Function
        If xLength > 0 Then getAngle = 270: Exit Function
    End If
    
    getAngle = Atn(yLength / xLength)
    getAngle = getAngle * DEG
    
    If xLength < 0 Then
        getAngle = getAngle + 90
    ElseIf xLength > 0 Then
        getAngle = getAngle + 270
    End If
    
End Function


Then he says:
x = x + cos(theta) * x - sin(theta)  * distance
y = y + cos(theta) * y + sin(theta) * distance


could anyone please explain this to me?

First, how can I find the angle in blitzmax? How can I make the alien find the angle that directs it towards the next point? Then how do I get it to follow that point and make sure it goes the same speed?

Small codesnippet I found in these forums earlier today
Function calcAngle2D:Float( x1:float, y1:float, x2:float, y2:float )
	Local angle:Float=ATan2( ( y2 - y1 ), ( x2 - x1 ) ) '+ 180
	Return angle
End Function


whats the x2 and the y2? the next spot.. hmm I wonder how I would determine what that is exactly.

x1,y1 could be the last point or the alien position. x2,y2 is the next point.

Just to give a quick example:
angle = calcAngle2D( enemy.x,enemy.y,  nextpoint.x,nextpoint.y)
enemy.x :+ cos( angle )
enemy.y :+ sin( angle )


hmm they're starting out in the middle of the screen now instead of the corner?

Ok I fixed the problem..

but to make them all go the same speed

enemy.x:+ Cos( enemy.Angle ) * enemy.x- Sin (enemy.Angle) * (1 - enemy.t)
enemy.Y:+ Cos( enemy.Angle ) * enemy.Y+ Sin (enemy.Angle) * (1 - enemy.t)


Right?

no that didnt work...

How can I make the speed right?

this may help.. sorry if its a little complex. This is the test app i used to write my animation controller. If you read through and strip it right back it should be clear what is going on.

Strict


'------------------------------------------------
Type tAnimationKey

	Field Value				:Float
	Field Time				:Int
	
	' these optional handles are used to control the direction of the curve
	' as it approaches and leaves the key point. 
	' Should be reletave to the Value Field
	Field HandleInHeight	:Float
	Field HandleOutHeight	:Float


EndType
'------------------------------------------------


'------------------------------------------------
Type tAnimationController

	Field KeyArray	:tAnimationKey[]
	Field Value		:Float Ptr
	Field Time		:Int 		= 0
	Field Linear 	:Int 		= False
	Field Looping	:Int		= True
	
	'
	' add an animation key to the key array
	Method AddKey (_Time:Int=0,_Value:Float=0,_HandleInHeight:Float=0,_HandleOutHeight:Float=0)

		Local ak:tAnimationKey = New tAnimationKey
		ak.Value = _Value
		ak.Time = _Time
		ak.HandleInHeight= _HandleInHeight
		ak.HandleOutHeight= _HandleOutHeight

		Local i:Int = Self.KeyArray.Length
		Self.KeyArray = Self.KeyArray[..i+1]
		Self.KeyArray[i] = ak
	
	EndMethod
	
	'
	' called every frame And updated the pointer To the animating property
	Method Update(_delta:Int)
		
		Local i			:Int = 0
		Local key		:Int = 0
		Local curvePos	:Float
		
		Self.Time :+ _delta
		
		'Just loop now
		If Self.Time > Self.KeyArray[Self.KeyArray.Length - 1].Time Then 
			If Self.Looping Then Time = Self.KeyArray[0].Time
		Else
			' destroy me now
		EndIf
		
		' find the key pair for the current time
		For i = 0 To Self.KeyArray.Length - 2 	
			If (Self.Time > Self.KeyArray[i].Time) And (Self.Time <= Self.KeyArray[i+1].Time)
				key = i
			EndIf
		Next 
		
		' find where we are betwene the two keys.	
		curvePos = (1.0/(Self.KeyArray[key+1].Time - Self.KeyArray[key].Time)) * (Self.Time - Self.KeyArray[key].Time)  
		
		' find the final value
		If Self.Linear Then		
			Self.Value[0] = ((Self.KeyArray[key + 1].Value - Self.KeyArray[key].Value) * curvePos) + Self.KeyArray[key].Value
		Else
			Self.Value[0] = Self.Curve (Self.KeyArray[Key].Value, Self.KeyArray[Key].HandleOutHeight,..
										Self.KeyArray[Key+1].Value, Self.KeyArray[Key+1].HandleInHeight,..
										curvePos) 	
		EndIf 'linear

	EndMethod	
	
	' method creates a bezier curve and finds our value based on _mu
	' direct port of c from net
	Method Curve:Float(_P1:Float,_HandleInHeight:Float,_P4:Float,_HandleOutHeight:Float,_mu:Float)
	
		Local point	:Float
		Local p2	:Float
		Local p3	:Float
		Local mum1 	:Float
		Local mum13	:Float
		Local mu3	:Float
		
		If _mu > 1 Then Return _p1
		If _mu < 0 Then Return _p1
		
		p2 = _p1 + _HandleInHeight
		p3 = _p4 + _HandleOutHeight
		
		mum1 = (1 - _mu)
		mum13 = mum1 * mum1 * mum1
		mu3 = _mu * _mu * _mu
		
		point = mum13*_p1 + 3*_mu*mum1*mum1*p2 + 3*_mu*_mu*mum1*p3 + mu3*_p4
		
		Return(point)

	EndMethod
	
	' this method is used to visualise the curve 
	Method Render(_TimeScale:Float=0.1, _ValueScale:Float=1, _r:Int=255, _g:Int=255, _b:Int=255, _lable:String="")	

		SetColor (_r,_g,_b)
		DrawText (_lable,KeyArray[KeyArray.length-1].Time*_TimeScale,KeyArray[KeyArray.length-1].Value*_ValueScale)
		For Local i = 0 To KeyArray.length - 2 		
			SetColor (_r-50,_g-50,_b-50)
			DrawRect (KeyArray[i].Time*_TimeScale-2, KeyArray[i].Value*_ValueScale-2,5,5)
			DrawRect (KeyArray[i+1].Time*_TimeScale-2, KeyArray[i+1].Value*_ValueScale-2,5,5) 'hack
			If Self.Linear Then DrawLine (KeyArray[i].Time*_TimeScale, KeyArray[i].Value*_ValueScale, KeyArray[i+1].Time*_TimeScale, KeyArray[i+1].Value*_ValueScale)
			SetColor (_r,_g,_b)
			Local TimeAcc:Float = KeyArray[i+1].Time - KeyArray[i].Time
			If Not Self.Linear Then
				For Local j:Float = 0.01 To 1 Step 0.02	
					Local ypos:Float = Curve (KeyArray[i].Value,KeyArray[i].HandleOutHeight,KeyArray[i+1].Value,KeyArray[i+1].HandleInHeight,j)
					Local xpos:Float = KeyArray[i].Time + (TimeAcc)* j 
					Plot(xpos*_TimeScale,ypos*_ValueScale)
				Next 
			EndIf
		Next
	
	EndMethod
	
End Type
'------------------------------------------------



Type tMovingBox

	Field x:Float = 0
	Field y:Float = 200
	
	Method Render()
		
		SetColor (50,200,50)
		DrawRect (x,y,15,15)
	
	EndMethod
		
EndType


Graphics 640,480

Global TimeNow			:Int
Global DeltaTime		:Int
Global Accumulator		:Int
Global LastFrameTime	:Int
Global TickRate			:Int 	= 20


Global mb:tMovingBox = New tMovingBox


' create an animation controller
Global ac1:tAnimationController = New tAnimationController

' add 4 keys
ac1.AddKey  (0000,200,0,0)
ac1.AddKey  (2100,300,0,0)
ac1.AddKey  (3000,300,0,0)
ac1.AddKey  (4000,200,0,0)


' bind the animation controller to the boxes y property
ac1.Value = Varptr mb.y

' uncomment these to see the effect of the bezier handles
' dont forget to comment the other addKey calls. 
'ac1.AddKey  (0000,200,0,200)
'ac1.AddKey  (2100,300,-200,0)
'ac1.AddKey  (3000,300,0,0)
'ac1.AddKey  (4000,200,300,0)





Global ac2:tAnimationController = New tAnimationController

ac2.AddKey  (0,0)
ac2.AddKey  (4000,400)

ac2.Linear = True ' ac2 is linear
ac2.Value = Varptr mb.x 


While Not KeyHit (KEY_ESCAPE)

	TimeNow = MilliSecs()
	DeltaTime = TimeNow - LastFrameTime
	Accumulator :+ DeltaTime 	
	
	' clamp needed for start up
	If Accumulator > 5 * TickRate  Then Accumulator = 5 * TickRate
	
	While Accumulator > TickRate
	' this a fixed step 	
		Accumulator :- TickRate  
		
		Cls
			
		ac1.Update(TickRate)
		ac2.Update(TickRate)
		
		ac1.Render (0.1, 1, 150, 0, 150, "ac1: Y: " + String(ac1.Value[0]))
		ac2.Render(0.1, 1, 150, 150, 0, "ac2: X: "+ String(ac2.Value[0]))
		mb.Render()
		
		Flip
			
	Wend 	
	
Wend


Here's something that (more or less) travels at a constant rate along a bezier:
Graphics 800,800,0

Local ax=50,ay=50,bx=400,by=50,cx=350,cy=750,dx=750,dy=750
t1#=0
t2#=0
speed#=0 'Speed of clever speed changing version
x1#=ax
y1#=ay
x2#=ax
y2#=ay
While Not KeyHit(KEY_ESCAPE)
	SetColor 100,100,100
	bezier(ax,ay,bx,by,cx,cy,dx,dy) 'draw the curve
	
	ox1#=x1
	oy1#=y1
	ox2#=x2
	oy2#=y2
	
	'CLEVER SPEED CHANGING VERSION
	t1:+speed
	If t1>1 Then t1=0
	a#=t1
	b#=1-t1
	x1#=ax*b*b*b + 3*bx*b*b*a + 3*cx*b*a*a + dx*a*a*a
	y1#=ay*b*b*b + 3*by*b*b*a + 3*cy*b*a*a + dy*a*a*a
	SetColor 255,255,0
	DrawRect x1-3,y1-3,7,7

	'Calculate 'speed' of curve at this point. 
	vx#=-3*ax*b*b + 3*bx*b*(b-2*a) + 3*cx*a*(2*b-a) + dx*3*a*a
	vy#=-3*ay*b*b + 3*by*b*(b-2*a) + 3*cy*a*(2*b-a) + dy*3*a*a
	d#=Sqr(vx*vx+vy*vy)

	'Watch out, magic ahead!

	speed#=1/d 'Make the ball moves less along the curve the 'faster' the curve is at this point, so the ball's *actual* cartesian velocity should stay roughly constant

	'You missed the magic! Go back!

	'Check what the actual speed is
	movedist1#=Sqr((ox1-x1)^2+(oy1-y1)^2)
	DrawText movedist1,0,0

	'Boring old version, t increments at a constant rate
	t2:+.001
	If t2>1 Then t2=0
	a#=t2
	b#=1-t2
	x2#=ax*b*b*b + 3*bx*b*b*a + 3*cx*b*a*a + dx*a*a*a
	y2#=ay*b*b*b + 3*by*b*b*a + 3*cy*b*a*a + dy*a*a*a
	SetColor 0,0,255
	DrawRect x2-3,y2-3,7,7

	'Check what the actual speed is
	movedist2#=Sqr((ox2-x2)^2+(oy2-y2)^2)
	DrawText movedist2,0,15

	Flip
	Cls
Wend

'DON'T BE DECEIVED! This is just a plain old bezier-drawing function, to show the curve the balls are moving along
Function bezier(ax#,ay#,bx#,by#,cx#,cy#,dx#,dy#)
	DrawLine ax,ay,bx,by
	DrawLine cx,cy,dx,dy
	ox#=ax
	oy#=ay
	For t#=0 To 1 Step .01
		a#=t
		b#=1-t
		x#=ax*b*b*b + 3*bx*b*b*a + 3*cx*b*a*a + dx*a*a*a
		y#=ay*b*b*b + 3*by*b*b*a + 3*cy*b*a*a + dy*a*a*a
		DrawRect x-1,y-1,3,3
		DrawLine ox,oy,x,y
		ox=x
		oy=y
	Next
End Function


Warpy strikes again.

This would be the third time you've helped me considerable ;)