Enemy motion path

BlitzMax Forums/BlitzMax Programming/Enemy motion path

Qweeg(Posted 2005) [#1]
Hi,

I am just playing about with a side scrolling shooter and am working on the motion paths of the enemies. I can get them to travel in a wave motion from Right to Left using COSINE, and I can get them to track towards the player ship based on its position on the screen.

But how would I go about doing a more complex motion path? One of the things I wanted to do was have an enemy appear on the right of the screen, travel straight across for a short distance, then do a complete circle back in roughly in the middle of the screen, and finish by heading toward the player ship.

Any tips would be much appreciated


smilertoo(Posted 2005) [#2]
didnt someone just release a path spline editor?


Diablo(Posted 2005) [#3]
This i belive


Perturbatio(Posted 2005) [#4]
One way to create paths is to record them.

You could write a simple path creator that allows you to draw the path with the mouse.

Alternatively, you could used a list that contains instructions on how to move the object and simply process it sequentially.

i.e.
Move 200,200 'move in a straight line to 200,200
Circle 250 ' move in a circle with a radius of 250 (using current position as the starting point.
Move 900,300 ' move in a straight line to 900,300
exit 'move to the nearest edge and then remove the enemy


Qweeg(Posted 2005) [#5]
Great - thanks for the quick responses guys. This path spline editor looks to be just the ticket! - I should pay more attention to what gets posted on the site ;)

And Perturbatio, thanks for your tips too.


Matt McFarland(Posted 2005) [#6]
Check this out.. Made by SCOUSE (not me)
Type vec2d
	Field x#,Y#
	
	Method set(xx#,yy#)
		x=xx
		Y=yy
	End Method
	
	Method add(v:vec2d)
		x:+ v.x
		Y:+ v.Y
	End Method

	Method sub(v:vec2d)
		x:- v.x
		Y:- v.Y
	End Method
		
	Method mulS(f#)
		x:* f
		Y:* f
	End Method

	Method negate()
		x= -x
		Y= -Y
	End Method	
	
	Method Copy(s:vec2d)
		x=s.x
		Y=s.Y
	End Method
	
	Method mag:Float(v:vec2d)
		Local xx#,yy#
		xx=v.x - x
		yy=v.Y - Y
		Return sqr(xx*xx + yy*yy)
	End Method
	
End Type


Type cSpline
	Global cSplineList:TList
	
	Field pnt:vec2d[]
	Field segLen:Float[]		'the length of each segment
	Field segPerc:Float[] 	'What percentage of the overall length, this segment takes up
	Field totalLength:Float
	Field outMulti:Float
	Field inMulti:Float
	Field link:TLink
	
	Method New()
		If not cSplineList Then cSplineList = CreateList()
		link = ListAddLast(cSplineList,Self)
	End Method
		
	Function Create:cSpline(pntCount = 4)
		If pntCount<4 Then pntCount = 4
		
		Local c:cSpline = New cSpline
		
		c.pnt = New vec2d[pntCount]
		For Local i:Int = 0 To pntCount-1
			c.pnt[i] = New vec2d
		Next
		
		'segments are always the pointcount - 3
		' 1....2----3----4----5----6....7  <7 points, 4 segments
		c.segLen = New Float[pntCount-3]
		c.segPerc = New Float[pntCount-3]
		
		c.outMulti = 0.5
		c.inMulti = 0.5

		Return c
	End Function

	'Adds a point to this curve. Default index of
	'-1 will add the point to the end
	Method AddPoint(x:Float, Y:Float, index:Int = -1)
		If index > pnt.length Then index = -1

		If index = -1
			pnt = pnt[..pnt.length+1]
			pnt[pnt.length-1] = New vec2d
			pnt[pnt.length-1].set(x,Y)
			
			segPerc = segPerc[..segPerc.length+1]
			segLen = segLen[..segLen.length+1]
		Else

		End If
		UpdateCurve()
	End Method
	
	'Makes a cSpline appear closed 
	Method Close(mergeEnds:Byte = False)
		If pnt.length < 6 Return

		If mergeEnds 'average the start and end points
			Local v:vec2d = New vec2d
			v.Copy(pnt[pnt.length-2])
			v.sub(pnt[1])
			v.mulS(.5)
			
			pnt[1].x:+ v.x
			pnt[1].Y:+ v.Y
			
			pnt[pnt.length-2].x:- v.x
			pnt[pnt.length-2].Y:- v.Y
		End If

		pnt[pnt.length-2].Copy(pnt[1])
		pnt[pnt.length-1].Copy(pnt[2])		
		pnt[0].Copy(pnt[pnt.length-3])	
		UpdateCurve()
	End Method
	
	Method DrawCurve(steps:Int = 100)
		SetScale 1,1
		SetLineWidth 1
		Local i:Int,j:Int
		Local s:Float
		Local t:Float = 1.0 / steps
		Local Loop:Float
		Local sX:Float = 0, sY:Float = 0
		Local lX:Float = 0, lY:Float = 0
		
		lX = pnt[1].x
		lY = pnt[1].Y
		
		For i = 0 To pnt.length-4
			While Loop <= 1.0
				For j = 0 To 3
					sX:+ interp(j,Loop) * pnt[i+j].x
					sY:+ interp(j,Loop) * pnt[i+j].Y
				Next
				DrawLine lX,lY,sX,sY
				lX = sX
				lY = sY
				sX = 0
				sY = 0
				Loop:+ t
			Wend
			Loop = 0
		Next
		
	End Method


	Method UpdateCurve(steps:Int=500)

		Local i:Int,j:Int
		Local cLen:Float = 0
		Local t:Float = 1.0 / steps
		Local delta:Float = 0
		Local lastX#=0,lastY#=0
		Local tX#=0,tY#=0
		Local sX#=0,sY#=0
		
		totalLength = 0
				
		For i = 0 To pnt.length-4
			lastX = pnt[i+1].x
			lastY = pnt[i+1].Y
		
			While delta < 1.0
			
				'get the position at point 'delta'
				For j = 0 To 3
					sX:+ interp(j,delta) * pnt[i+j].x
					sY:+ interp(j,delta) * pnt[i+j].Y
				Next
				
				tX = sX - lastX
				tY = sY - lastY
				
				'Add the length of this step
				cLen:+ sqr(tX*tX + tY*tY)
				
				'Record last position
				lastX = sX
				lastY = sY
				
				sX = 0
				sY = 0
				delta:+ t
			Wend
			segLen[i] = cLen
			totalLength:+ cLen
			cLen = 0
			delta = 0
		Next

		'Update segment percentages
		For i = 0 To segLen.length-1
			segPerc[i] = segLen[i] / totalLength
		Next
	End Method

	Method GetHeading:Float(Pos:Float)
		Local ahead:Float
		Local ax:Float,ay:Float
		Local bx:Float,by:Float
		Local angle:Float
		
		If Pos < .99
			ahead = Pos + .01
		Else
			ahead = Pos - .01
		End If
		
		Local a:vec2d = GetPosition(Pos)
		Local b:vec2d = GetPosition(ahead)

		a.sub(b)
		angle = ATan2(-a.x,a.Y)
		
		If Pos < .99
			Return angle
		Else
			Return angle+180
		End If
	End Method
		

	Method PlotCurve(steps:Int = 100)
		SetColor 0,255,0
		SetScale 1,1
		Local p:vec2d
		Local d:Float = 0
		Local stp:Float = 1.0 / steps
		
		While d <= 1.0
			p = GetPosition(d)
			DrawOval p.x-1,p.Y-1,2,2
			d:+ stp
		Wend
		SetColor 255,255,255
		
		
	End Method
	
	Method GetPosition:vec2d(tPerc:Float)
		If tPerc>1 Then tPerc = 1
		If tPerc<0 Then tPerc = 0
		
		Local p:vec2d = New vec2d
		Local i:Int,j:Int
		Local tempTotal:Float = 0
		Local tempPerc:Float = 0
				
		'find which segment the target percentage is in
		For i = 0 To segPerc.length-1
			tempTotal:+ segPerc[i]
			
			If tPerc = tempTotal
				p.x = pnt[i+1].x
				p.Y = pnt[i+1].Y
				Return p
			Else If tPerc < tempTotal
				Exit
			End If
			tempPerc:+ segPerc[i]
		Next

		' i = the segment in which the target percentage falls into

		'transform the target 'global' percentage, into a local segment percentage
		Local getPerc# = (tPerc - tempPerc) * (1.0 / segPerc[i])

		For j = 0 To 3
			p.x:+ interp(j,getPerc) * pnt[i+j].x
			p.Y:+ interp(j,getPerc) * pnt[i+j].Y
		Next
	
		posSeg = i+1
		Return p
	End Method

  Method interp:Float(i:Int, t:Float)
		Select i
			Case 0
				Return ( (-t+2) * t - 1) * t * .5
			Case 1
				Return (( (3*t-5) *t) * t + 2) * .5
			Case 2
				Return ( (-3*t+4) * t + 1) * t * .5
			Case 3
				Return ( (t-1) * t^2) * .5
			Default
				Return 0
		End Select
	End Method
		
End Type



'----------------------------------------------------------------------------------- MAIN PROGRAM

Global cpm#=.5,pm#=.5

'Create a curve with 7 points (first and last are always ghost control points only!)
Local c:cSpline = cSpline.Create(7)
c.pnt[0].set(100,200)
c.pnt[1].set(200,400)
c.pnt[2].set(300,200)
c.pnt[3].set(400,400)
c.pnt[4].set(500,200)
c.pnt[5].set(600,400)
c.pnt[6].set(700,200)

c.UpdateCurve()

Graphics 800,600,0

'make a sprite
Local player:TImage = CreateImage(32,32,1,DYNAMICIMAGE|MASKEDIMAGE)
SetImageHandle player,16,16
DrawOval 0,0,32,32
SetLineWidth 3
SetColor 255,0,0
DrawLine 16,0,10,6
DrawLine 16,0,22,6
DrawLine 10,6,22,6
GrabImage player,0,0,0
SetColor 255,255,255



Local curPoint=0
Local Pos:vec2d = New vec2d
Local playerPos# = 0
Local time%
Local doPlot:Byte = False, doCurve:Byte = True
Local Last:vec2d = New vec2d

Last.Copy(c.pnt[1])
Global posSeg:Int = 0

While not KeyHit(KEY_ESCAPE)
	
	Cls
	If KeyDown(KEY_1) curPoint = 0
	If KeyDown(KEY_2) curPoint = 1
	If KeyDown(KEY_3) curPoint = 2
	If KeyDown(KEY_4) curPoint = 3
	If KeyDown(KEY_5) curPoint = 4
	If KeyDown(KEY_6) curPoint = 5
	If KeyDown(KEY_7) curPoint = 6
	If KeyDown(KEY_8) curPoint = 7
	If KeyDown(KEY_9) curPoint = 8
	If KeyDown(KEY_0) curPoint = 9


	'curve multiplier adjustment
	If KeyDown(KEY_LEFT) c.outMulti:+ .01
	If KeyDown(KEY_RIGHT) c.outMulti:- .01				

	If KeyDown(KEY_UP) c.inMulti:- .01
	If KeyDown(KEY_DOWN) c.inMulti:+ .01	
	
	If KeyHit(KEY_SPACE) Then c.Close(True)

	If KeyHit(KEY_P) Then doPlot = not doPlot
	If KeyHit(KEY_C) Then doCurve = not doCurve
	
	If MouseDown(1)
		c.pnt[curPoint].set(MouseX(),MouseY())
		c.UpdateCurve()
	End If
		
	If MouseHit(2)
		c.AddPoint(MouseX(),MouseY())
	End If
	
		
	Local n:Int
	For n = 0 To c.pnt.length-1
	
		If n=curPoint
			SetScale 6,6
			SetColor 100,100,100
			DrawOval c.pnt[n].x - 7,c.pnt[n].Y - 7,3,3
		End If

		If n=0 or n=c.pnt.length-1
			SetColor 0,255,0
		Else
			SetColor 255,0,0
		End If
		
		SetScale 3,3
		DrawOval c.pnt[n].x - 2,c.pnt[n].Y - 2,2,2
		SetScale 1,1
		SetColor 255,255,255
		DrawText "p"+(n+1),c.pnt[n].x,c.pnt[n].Y+2
	Next
	
	SetColor 255,255,255
	SetScale 1,1
		

	
	
	DrawText "Press 1,2,3,4,5,6 or 7 to select point, 'p' & 'c' toggle plot/curve drawing",0,0
	DrawText "Current move point: "+(curPoint+1),0,20
	DrawText "Approx length of curve: "+c.totalLength,0,40

	'For Local i:Int = 0 To c.segPerc.length-1
	'	DrawText "Segment percentage "+(i+1)+" = : "+c.segPerc[i],0,80+(i*14)
	'Next
	
	Pos = c.GetPosition(playerPos)
	
	DrawText Int(100*playerpos)+"%",Pos.x,Pos.Y+20
	
	SetRotation c.GetHeading(playerpos)
	DrawImage player,Pos.x,Pos.Y
	
	SetRotation 0

	If doCurve Then c.DrawCurve(10) '10 lines per segment	
	If doPlot Then c.PlotCurve(50)

	
	
	Flip
	FlushMem
	
	playerPos:+ .005'.0025
	If playerPos > 1.0 Then playerPos = 0

Wend


copy paste all that and you'll have the coolest spline ever.
only problem is I cannot comprehend his code (its seems to be way too complex)

any chance someone can make a much simpler version of the "cspline?" so I can disect it and add this to my editor?