Vector class:keef.vectors
Strict
' A vector class with a lot of handy functionality for game-programming!
' In simplicy a vector is an Arrow. It has an length
' and a direction. You can use these properties to
' make the vector model a Position, a Velocity, a
' Line, a Distance and many other things that can be
' measured using a length and a direction or anything
' that uses two float pair-values!
' Wikipedia: A vector is what is needed to "carry" the
' point A to the point B; the Latin word vector means "one who carries"
Class Vector
Public
Field X:Float
Field Y:Float
Method New(x:Float=0,y:Float=0)
X = x
Y = y
End
' S E T
'----------------------------------------------
Method Set:Vector( vector:Vector )
X = vector.X
Y = vector.Y
Return Self
End
Method Set:Vector(x:Float,y:Float )
X = x
Y = y
Return Self
End
' A D D
'----------------------------------------------
Method Add:Vector( vector:Vector )
X += vector.X
Y += vector.Y
Return Self
End
' S U B T R A C T
'----------------------------------------------
Method Subtract:Vector( vector:Vector )
X = X - vector.X
Y = Y - vector.Y
Return Self
End
' D O T P R O D U C T
'---------------------------------------------------
' Dot Product which is X*X2 + Y*Y2
'
'---------------------------------------------------
Method Dot:Float( Vector:Vector )
Return ( X * Vector.X + Y * Vector.Y)
End
' M U L T I P L Y V E C T O R
'----------------------------------------------
Method Multiply:Vector( Value:Float )
X*=Value
Y*=Value
Return Self
End
' M I R R O R
'----------------------------------------------------------
' if the mirrorImage vector is a unit-vector this will
' make this vector flip 180 degrees around it
' So that this vector now points in the exact opppisite
' direction To the mirrorImage vector
' Returns Self, which will be opposite to mirrorImage.
Method Mirror:Vector( mirrorImage:Vector )
Local Dotprod# = -X * mirrorImage.X - Y * mirrorImage.Y
X=X+2 * mirrorImage.X * Dotprod
Y=Y+2 * mirrorImage.Y * Dotprod
Return Self
End
' Set the vector to a direction and a length x,y
' returns Self, which is now pointin the directio
' provided, with the length provided
Method MakeField:Vector( direction:Float, length:Float )
X = Cos( -direction )*length
Y = Sin( -direction )*length
Return Self
End
'----------------------------------------------
' Create a copy --> depending on situation you might
' want to use the VectorPool to do this instead for
' when extrenme performance is required
'-----------------------------------------------
Method Copy:Vector() Property
Local vector:Vector = New Vector
vector.X = X
vector.Y = Y
Return vector
End
' C R E A T E L E F T N O R M A L
'----------------------------------------------
' Make this an Perpendicular Vector
' As if you would rotate it 90 degrees Counter Clockwise
' return ( Y, -X )
Method LeftNormal:Vector( )
Local tempX:Float = Y
Y = -X
X = tempX
Return Self
End
' C R E A T E R I G H T N O R M A L
'----------------------------------------------
' Make this a Perpendicular Vector
' Same as rotating it 90 degrees ClockWise
' return ( -Y, X )
Method RightNormal:Vector( )
Local tempY:Float = Y
Y = X
X = -tempY
Return Self
End
' N O R M A L I Z E
'----------------------------------------------
' Purpose: Sets Vector length To ONE but keeps it's direction
' Returns Self as a UnitVector
Method Normalize:Vector()
Local Length:Float = Self.Length()
If Length = 0
Return Self ' Don't divide by zero,
'we do not normalize a zero-vector
'since this vector's direction is in
' mathematical terms all directions!
Endif
Set( X / Length, Y / Length ) 'Make length = 1
Return Self
End
' G E T L E N G T H
'-----------------------------
' Get current Length of vector, uses a single sqr operation
Method Length:Float() Property
Return Sqrt( X*X + Y*Y )'
End
' S E T L E N G T H
'-------------------------------------
Method Length:Void( length:Float ) Property
'If we want to set vector to zero
If length = 0
X=0
Y=0
Return
Endif
If X = 0 And Y = 0
X = length
Endif
Normalize()
Multiply( length )
End
Method ReduceLength:Vector( amount:Float )
Local currentAngle:Float = Direction
Local currentLength:Float = Length
Local newLength:Float = currentLength - amount
If newLength > 0
MakeField( currentAngle, currentLength - amount )
Else
Set 0,0
Endif
Return Self
End
' G E T D I R E C T I O N
'----------------------------------------------------
' Calculates the current direction in degrees
' in the 0 To < 360 range
Method Direction:Float() Property
Local angle:Float = ATan2(-Y , X)
If angle < 0
angle =angle+ 360
Endif
'If angle = 360 Then angle = 0
Return angle
End
' S E T D I R E C T I O N
'----------------------------------------------
'
' Set the angle of this vector without changing the length,
' has no effect if vector length is 0
' uses a single sqr operation
Method Direction:Void( direction:Float ) Property
MakeField( direction, Length )
End Method
' D I S T A N C E
'----------------------------------------------
' The Distance between the two points
' This is NOT related to the vectors Length#
Method DistanceTo:Float(Other:Vector)
Return DistanceTo(Other.X, Other.Y)
End
Method DistanceTo:Float(x:Float, y:Float)
Local dx:Float = x - X
Local dy:Float = y - Y
Return Sqrt( dx*dx + dy*dy )
End
' G E T A N G L E B E T W E E N
'----------------------------------------------
' If you have two vectors that start at the same position
' the angle-distance between two vectors Result is from 0
' to 180 degrees, because two vectors can not be more than 180
' degrees apart, check AngleClockwise & AngleAntiClockwise
' to get a 0-360 result instead
' even tough it is counted as they are on the same position,
' that is not a requirement at all for this to be correct
Method AngleTo:Float( target:Vector )
Local dot:Float = Self.Dot( target )
Local combinedLength:Float = Self.Length()*target.Length()
If combinedLength = 0 Then Return 0
Local quaski:Float = dot/combinedLength
Local angle:Float = ACos( quaski )
Return angle
End
' If you have two vectors so they both start at the same position
' returns the angle from this vector to target vector (in degrees)
' if you where to go Clockwise to it, result is 0 to 360
Method AngleToClockwise:Float( target:Vector )
Local angle:Float = ATan2(-Y , X) - ATan2(-target.Y , target.X)
If angle < 0 Then angle += 360
If angle >= 360 Then angle -= 360
Return angle
End
' If you have two vectors so they both start at the same position
' returns the angle fromt this vector to target vector (in degrees)
' if you where to go AntiClockwise to it, result is 0 to 360
Method AngleToAntiClockwise:Float( target:Vector )
Local angle:Float = AngleToClockwise(target)-360
Return -angle
End
' V E C T O R B E T W E E N
'----------------------------------------------
' Change the vector into a vector from Position1 to Position2
' Return Self, as a vector that goes from first
' parameter's position to the second
Method MakeBetween:Vector( PositionFrom:Vector , PositionToo:Vector)
If PositionFrom = Null Or PositionToo = Null Then Return Self
X = ( PositionToo.X - PositionFrom.X )
Y = ( PositionToo.Y - PositionFrom.Y )
Return Self
End
End
CODE + EXAMPLE
Strict
Import mojo
Import keef.vectors
Function distance:Float(x1:Int, y1:Int, x2:Int, y2:Int)
Local x:Float=Sqrt(((x1 - x2)*(x1 - x2)) + ((y1 - y2)*(y1 - y2)) )
Return x
End Function
Global shapes:List<shape>
Global square1:poly
Global square2:poly
Class collisions Extends App
Method OnUpdate:Int()
'Local s:shape=shapes.First()
If KeyDown(KEY_LEFT)
square1.posx=square1.posx-1
Endif
If KeyDown(KEY_RIGHT)
square1.posx=square1.posx+1
Endif
If KeyDown(KEY_UP)
square1.posy=square1.posy-1
Endif
If KeyDown(KEY_DOWN)
square1.posy=square1.posy+1
Endif
'square1 s2.angle
If square1.collide(square2)
square1.collided=True
'square1 "yeah"
Endif
square1.angle=square1.angle+1
square2.angle=square2.angle+1
Return 0
End Method
Method OnRender:Int()
Cls(0,0,0)
'For Local s:shape=Eachin shapes
square1.render()
square2.render()
'Next
Return 0
End Method
Method OnCreate:Int()
SetUpdateRate(30)
'shapes=New List<shape>
'shapes.AddLast(New point(5,5))
square1=New poly(150,250,3)
'shapes.AddLast(p)
'square1.angle=0
square1.add_point(New point(-25,-25))
'square1=New point(250,200)
'square1.add_point(New point(25,-25))
square1.add_point(New point(25,25))
square1.add_point(New point(-25,25))
square2=New poly(250,250,4)
'shapes.AddLast(p)
'p.angle=0
square2.add_point(New point(-25,-25))
square2.add_point(New point(25,-25))
square2.add_point(New point(25,25))
square2.add_point(New point(-25,25))
'p.add_point(New point(0,25))
Return 0
End Method
End Class
Function Main:Int()
New collisions
Return 0
End Function
Class point Extends shape
Field rotatedx:Float
Field rotatedy:Float
Method New(x1:Int,y1:Int)
Self.posx=x1
Self.posy=y1
End Method
Method rotate_around:Void(angle:Float,cx:Float,cy:Float)
Local s:Float = Sin(angle)
Local c:Float = Cos(angle)
Local px:Float=Self.posx
Local py:Float=Self.posy
Local xnew:Float = px * c - py * s
Local ynew:Float = px * s + py * c
Self.rotatedx = xnew + cx
Self.rotatedy = ynew + cy
End Method
Method collide:Bool(shape2:shape)
Local result:Bool
Local other_number:Int
If poly(shape2)
Local p:poly=poly(shape2)
Local angles:Int[p.num_points]
Local backwards_angles:Int[p.num_points]
Local angle_to_middle:Int[p.num_points]
Local tolerance_angle:Int[p.num_points]
Local point_num:Int
For Local iter:Int=0 To p.num_points-1
Local t:Vector=New Vector(p.points[iter].rotatedx,p.points[iter].rotatedy)
Local t2:Vector=New Vector(p.points[p.next_point(iter)].rotatedx,p.points[p.next_point(iter)].rotatedy)
Local t3:Vector=New Vector(0,0)
t3.MakeBetween(t,t2)
angles[iter]=t3.Direction
Local t22:Vector=New Vector(p.points[p.prev_point(iter)].rotatedx,p.points[p.prev_point(iter)].rotatedy)
Local t33:Vector=New Vector(0,0)
t33.MakeBetween(t,t22)
backwards_angles[iter]=t33.Direction
Local t222:Vector=New Vector(posx,posy)
Local t333:Vector=New Vector(0,0)
t333.MakeBetween(t,t222)
angle_to_middle[iter]=t333.Direction
tolerance_angle[iter]=Abs(angles[iter]-backwards_angles[iter])
Next
For Local iter:Int=0 To p.num_points-1
p.success[iter]=False
Next
result=True
For Local iter:Int=0 To p.num_points-1
Local t:Vector=New Vector(p.points[iter].rotatedx,p.points[iter].rotatedy)
Local t2:Vector=New Vector(Self.rotatedx,Self.rotatedy)
Local t3:Vector=New Vector(0,0)
t3.MakeBetween(t,t2)
If (t3.Direction<angles[iter] And t3.Direction>backwards_angles[iter]) Or (tolerance_angle[iter]>179)
p.success[iter]=True
Endif
Next
For Local iter:Int=0 To p.num_points-1
If p.success[iter]=False
result=False
Endif
Next
Endif
Return result
End Method
Method render:Void()
If collided=True
SetColor(255,0,0)
Endif
DrawText(posx+" "+posy,posx,posy)
DrawRect(posx,posy,2,2)
SetColor(255,255,255)
collided=False
End Method
End Class
Class shape
Field posx:Float
Field posy:Float
Field angle:Int
Field width:Int
Field height:Int
Field collided:Bool
Method collide:Bool(shape2:shape) Abstract
Method render:Void()
End Method
End Class
Class poly Extends shape
Field success:Bool[]
Field num_points:Int
Field point_index:Int
Field points:point[]
Method add_point:Void(p:point)
points[point_index]=p
point_index=point_index+1
End Method
Method render:Void()
For Local iter:=0 To num_points-1
Self.points[iter].rotate_around(Self.angle,Self.posx,Self.posy)
Self.points[next_point(iter)].rotate_around(Self.angle,Self.posx,Self.posy)
If success[iter]=True
SetColor(255,0,0)
Endif
DrawText(iter,Self.points[iter].rotatedx,Self.points[iter].rotatedy)
DrawLine(Self.points[iter].rotatedx,Self.points[iter].rotatedy,Self.points[next_point(iter)].rotatedx,Self.points[next_point(iter)].rotatedy)
SetColor(255,255,255)
Next
DrawRect(Self.posx,Self.posy,3,3)
End Method
Method next_point:Int(a:Int)
If a<num_points-1
Return a+1
Else
Return 0
Endif
End Method
Method prev_point:Int(a:Int)
If a>0
Return a-1
Else
Return num_points-1
Endif
End Method
Method New(x:Int,y:Int,ps:Int)
Self.posx=x
Self.posy=y
Self.angle=0
num_points=ps
points=New point[ps]
success=New Bool[ps]
For Local iter:Int=0 To ps-1
success[iter]=False
Next
End Method
Method collide:Bool(shape2:shape)
Local hit_this:Bool
If circle(shape2)
Local circ:circle=circle(shape2)
For Local iter:=0 To num_points-1
If distance(circ.posx,circ.posy,points[iter].rotatedx,points[iter].rotatedy)<circ.width
hit_this=True
Endif
Next
Endif
If poly(shape2)
Local pol:poly=poly(shape2)
For Local iter:=0 To num_points-1
If Self.points[iter].collide(shape2)=True
hit_this=True
Return true
Endif
Next
Endif
Return hit_this
End Method
End Class
Class circle Extends shape
Method New(x:Int,y:Int,wid:Int)
Self.posx=x
Self.posy=y
Self.width=wid
End Method
End Class
Use the arrow keys to move the triangle around. The triangle is called 'square1' in the code.
|