Posted: 11th Jul 2021 10:00
Below is my [size=large]2D Vector Library[/size] which you can #include in your own code. And attached is a usage example using seek and evade steering behaviours as described in Craig Reynold's research paper on on autonomous agents.
In the usage example, the 'target' sprite seeks a random waypoint. When it finds it, it will choose another waypoint and steer/accelerate towards that and so on...
The 'enemy' sprite will seek the player unless they get too close and then then will evade it instead.
Any enemies that collide with the target will be respawned in a random location and continue as before.


+ Code Snippet
type tVector
	x as float
	y as float
endtype



//**********************************
//	Set Vector Random heading
//**********************************
//Create A unit vector in a random direction
function SetVectorRandom(v ref as tVector)
	v.x = Random2()
	v.y = Random2()
	SetVectorNormalize(v)
endfunction



//**********************************
//		Set Vector
//**********************************
//Sets an existing vector directly
function SetVector(x#, y#)
	vr as tVector
	vr.x = x#
	vr.y = y#
endfunction vr


//**********************************
//	Set Vector from Heading and magnitude
//**********************************
//Sets an existing vector from heading and magnitude
function SetVectorHM(v ref as tVector, head#, mag#)
	v.x = cos(head#) * mag#
	v.y = sin(head#) * mag#
endfunction



//**********************************
//	Create Vector
//**********************************
//Creates a new vector by heading and magnitude
function CreateVectorHM(head#, mag#)
	v as tVector
	v.x = cos(head#) * mag#
	v.y = sin(head#) * mag#
endfunction v



//**********************************
//	Set Vector Heading
//**********************************
//Sets the vectors heading (direction)
function SetVectorHeading(v ref as tVector, head#)
	mag# = GetVectorLength(v)
	v.x = Cos(head#) * mag#
	v.y = Sin(head#) * mag#
endfunction


//**********************************
//	Rotate Vector
//**********************************
//Sets the vectors heading by rotating it by the given value
function RotateVector(v ref as tVector, a#)
	SetVectorHeading(v, GetVectorAngle(v) + a#)
endfunction


//**********************************
//	Set Vector Magnitude
//**********************************
//Sets the vectors magnitude (length)
function SetVectorMagnitude(v ref as tVector, mag#)
	SetVectorNormalize(v)
	SetVectorMultiply(v, mag#)
endfunction


//**********************************
//		Copy Vector
//**********************************
//Sets Vector 2 to be the same as Vector 1
function CopyVector(v1 as tVector, v2 ref as tVector)
	v2.x = v1.x
	v2.y = v1.y
endfunction

//Same thing - different name
function SetVectorCopy(v1 as tVector, v2 ref as tVector)
	v2.x = v1.x
	v2.y = v1.y
endfunction

//**********************************
//		Copy Vector
//**********************************
//Create a new vector that is a copy of an existing one
function GetVectorCopy(v as tVector)
	vr as tVector
	vr.x = v.x
	vr.y = v.y
endfunction vr



//**********************************
//		Normalise
//**********************************
//Directly normalise the passed vector
//Spelling of normalise has been "Americanised" as per coding standards :(
function SetVectorNormalize(v ref as tVector)
	l# = GetVectorLength(v)
	v.x = v.x / l#
	v.y = v.y / l#
endfunction

//Normalise v and return the result
function GetVectorNormalize(v as tVector)
	vr as tVector
	l# = GetVectorLength(v)
	vr.x = v.x / l#
	vr.y = v.y / l#
endfunction vr

// A direct copy of GetVectorNormalize() with a different name
function GetUnitVector(v as tVector)
	vr as tVector
	l# = GetVectorLength(v)
	vr.x = v.x / l#
	vr.y = v.y / l#
endfunction vr



//**********************************
//	Set Vector Zero
//**********************************
//Sets the vector to zero
function SetVectorZero(v ref as tVector)
	v.x = 0
	v.y = 0
endfunction



//**********************************
//	Addition
//**********************************
//Adds two vectors and puts the return value in v1
function SetVectorAdd(v1 ref as tVector, v2 ref as tVector)
	inc v1.x, v2.x 
	inc v1.y, v2.y
endfunction 
	
//Adds v1 to v2 and returns a vector
function GetVectorAdd(v1 as tVector, v2 as tVector)
	vr as tVector
	vr.x = v1.x + v2.x 
	vr.y = v1.y + v2.y
endfunction vr



//**********************************
//	Subtraction
//**********************************
//Subtracts two vectors and puts the return value in v1
function SetVectorSubtract(v1 ref as tVector, v2 ref as tVector)
	dec v1.x, v2.x 
	dec v1.y, v2.y
endfunction 
	
//Subtracts v2 from v1 and returns a vector
function GetVectorSubtract(v1 as tVector, v2 as tVector)
	vr as tVector
	vr.x = v1.x - v2.x 
	vr.y = v1.y - v2.y
endfunction vr
	


//**********************************
//	Multiplication
//**********************************
//Directly multiplies a vector value
function SetVectorMultiply(v ref as tVector, m#)
	v.x = v.x * m# 
	v.y = v.y * m#
endfunction 
	
//Multiplies v1 by m# and returns a vector
function GetVectorMultiply(v as tVector, m#)
	vr as tVector
	vr.x = v.x * m#
	vr.y = v.y * m#
endfunction vr


	
//**********************************
//	Division
//**********************************
//Divides a vector and puts the return value in v1
function SetVectorDivide(v ref as tVector, d#)
	v.x = v.x / d# 
	v.y = v.y / d#
endfunction 
	
//Divides v1 by m# and returns a vector
function GetVectorDivide(v as tVector, m#)
	vr as tVector
	vr.x = v.x / m#
	vr.y = v.y / m#
endfunction vr



//**********************************
//	Negate Vector
//**********************************
//Points the vector in the opposite direction
function SetVectorNegate(v ref as tVector)
	v.x = -v.x
	v.y = -v.y
endfunction

//Returns a vector that is in the opposite direction to that of v
function GetVectorNegate(v as tVector)
	vr as tVector
	vr.x = -v.x
	vr.y = -v.y
endfunction vr



//**********************************
//	Set Vector Limit
//**********************************
//Directly sets a limit to the passed in vector
function SetVectorLimit(v ref as tVector, limit#)
	mag# = GetVectorLength(v)
	if mag# <= limit# then exitfunction
	
	SetVectorNormalize(v)
	SetVectorMultiply(v, limit#)
endfunction



//**********************************   G E T T E R S   **********************************


//**********************************
//	GetVectorRandom
//**********************************
//Get a unit vector in a random direction
function GetVectorRandom()
	v as tVector
	v.x = random2()
	v.y = random2()
	SetVectorNormalize(v)
endfunction v 



//**********************************
//	Get Vector Distance
//**********************************
//Returns a float that is the distance between v1 and v2
function GetVectorDistance(v1 as tVector, v2 as tVector)
	vx# = v1.x - v2.x
	vy# = v1.y - v2.y
	d# = sqrt((vx# * vx#) + (vy# * vy#))	
endfunction d#


//**********************************
//	Get Vector Distance Sqr
//**********************************
//Returns a float that is the SQUARED distance between v1 and v2
function GetVectorDistanceSqr(v1 as tVector, v2 as tVector)
	vx# = v1.x - v2.x
	vy# = v1.y - v2.y
	d# = (vx# * vx#) + (vy# * vy#)
endfunction d#



//**********************************
//	Get Vector Length
//**********************************
function GetVectorLength(v as tVector)
	l# = sqrt( (v.x*v.x) + (v.y*v.y) )
endfunction l#


//**********************************
//	Get Vector Length Length
//**********************************
function GetVectorSquareLength(v as tVector)
endfunction (v.x*v.x) + (v.y*v.y)


//**********************************
//	Get Vector Magnitude
//**********************************
//Same as GetVectorLength() with a different name
function GetVectorMagnitude(v as tVector)
	l# = sqrt( (v.x*v.x) + (v.y*v.y) )
endfunction l#



//**********************************
//	Get Vector Midpoint
//**********************************
//Returns a vector that is the midpoint between v1 and v2
function GetVectorMidpoint(v1 as tVector, v2 as tVector)
	vr as tVector
	vr.x = (v1.x + v2.x)/2.0
	vr.y = (v1.y + v2.y)/2.0
endfunction vr



//**********************************
//	Get Vector Zero
//**********************************
//Creates a zeroed vector
function GetVectorZero()
	v as tVector
	v.x = 0
	v.y = 0
endfunction v



//**********************************
//	Get Angle as a scaler
//**********************************
//Gets the vectors angle as a scaler
function GetVectorAngle(v as tVector)
	angle# = WrapAngle(ATanFull(v.x, v.y) - 90)
endfunction angle#


//A copy of GetVectorAngle() with a different name
function GetVectorHeading(v as tVector)
	angle# = WrapAngle(ATanFull(v.x, v.y) - 90)
endfunction angle#



//**********************************
//	Dot Product
//**********************************
function GetDotProduct(v1 as tVector, v2 as tVector)
endfunction (v1.x * v2.x) + (v1.y * v2.y)

//Same as above but a different name
function GetVectorDotProduct(v1 as tVector, v2 as tVector)
endfunction (v1.x * v2.x) + (v1.y * v2.y)



//**********************************
//	Get Angle Between Two Vectors
//**********************************
function GetAngleBetween(v1 as tVector, v2 as tVector)
	m1# = GetVectorLength(v1)
	m2# = GetVectorLength(v2)
	dot# = GetDotProduct(v1, v2)
	a# = ACos(dot#/(m1#*m2#))
endfunction a#



//**********************************
//	Get Vector Projection
//**********************************
function GetVectorProjection(v1 as tVector, v2 as tVector)
	p as tVector
	mag# = GetVectorSquareLength(v2)
	dot# = GetVectorDotProduct(v1, v2)
	s# = dot# / mag#
	CopyVector(v2, p)
	SetVectorMultiply(p, s#)
endfunction p



//**********************************
//	Equals
//**********************************
//Returns 1 (TRUE) if the two passed vectors are equal
function GetVectorEquals(v1 as tVector, v2 as tVector)
	if v1.x = v2.x
		if v1.y = v2.y
			exitfunction 1
		endif
	endif
endfunction 0


//**********************************
// Set Vector Wrap Screen
//**********************************
// If a positional vector has move beyond the screen limints then
// Wrap it to the other side
function SetVectorWrapScreen(v REF as tVector)
	if v.x > GetScreenBoundsRight()
		v.x = GetScreenBoundsLeft() + (v.x - GetScreenBoundsRight())
	elseif v.x < GetScreenBoundsLeft()
		v.x = GetScreenBoundsRight() - (GetScreenBoundsLeft() - v.x)
	endif
	if v.y > GetScreenBoundsBottom()
		v.y = GetScreenBoundsTop() + (v.y - GetScreenBoundsBottom())
	elseif v.y < GetScreenBoundsTop()
		v.y = GetScreenBoundsBottom() - (GetScreenBoundsTop() - v.y)
	endif
endfunction



//**********************************
//	Wrap Angle
//**********************************
//Returns a value that does not exceed the range of 0 to 360.
function WrapAngle(angle as float)
	angle = fmod(angle, 360.0)
	if angle <0 then angle=angle+360
endfunction angle




/************************************************************************************
*																					*
									D E B U G   									*
*																					*
************************************************************************************/


//**********************************
//	  DEBUG - DRAW VECTOR
//**********************************
//Draw a line from pos in the direction and magnitude of v
//multi# is a multiplier that can extend/shrink the debug line
function DrawVector(pos as tVector, v as tVector, multi#, r, g, b)
	c = MakeColor(r, g, b)
	v2 as tVector
	CopyVector(v, v2)
	SetVectorMultiply(v2, multi#)
	DrawLine(pos.x, pos.y, pos.x + v2.x, pos.y+v2.y, c, c)
	DrawEllipse(pos.x + v2.x, pos.y+v2.y, 0.75, 0.75, c, c, 1)	
endfunction



//**********************************
//	DEBUG - DRAW VECTOR RANGE
//**********************************
//Draw a circle around a position vector 
function DrawVectorRange(v as tVector, range#, r, g, b)
	c = MakeColor(r, g, b)
	DrawEllipse(v.x, v.y, range#, range#, c, c, 0)
endfunction
Posted: 11th Jul 2021 16:45
Nice example, I like the flocking effect.

This is also handy for 3D if one swaps the y and z, for schools of fish and birds ETC at a set height.

Thanks for sharing.
Posted: 13th Jul 2021 8:59
For anyone that would like to learn more about vectors and how they work, I would very much recommend watching some of Daniel Shiffman's videos on YouTube. He is quite eccentric but incredibly informative. His website and link to his videos can be found here: https://thecodingtrain.com/learning/nature-of-code/
Posted: 14th Jul 2021 3:59
Glancing at his videos, I've always handled it differently. I maintain a normalized direction vector and keep velocity and acceleration as scalars, just seems easier to work with that way.
Posted: 14th Jul 2021 10:40
I know what you mean. I used to do the same but my inner pedant has to point out that velocity must be a vector because it has direction and magnitude. Speed could be a scaler. But like I say, I'm just being pedantic, if it works then go with whatever system you prefer.