Hey all
Here's a function I just worked on...
It is acurate, FAST, rotated rectangle/box collision.
Here's the actuall, uncommented function:
Function _rot_box(boxx#,boxz#,xlength,zlength,boxrot#,px#,pz#,buffer#)
collision = 0
centrea# = ATan(Abs(xlength / 2) / Abs(zlength / 2))
theta# = WrapValue(ATan((px# - boxx#) / (pz# - boxz#)) - boxrot#)
If theta# >= 180
theta# = theta# - 180
EndIf
If theta# >= centrea# And theta# <= (180 - centrea#)
If theta# < 90 Then theta# = 90 - theta# Else theta# = theta# - 90
dist# = (xlength / 2) / Cos(theta#)
Else
If theta# < centrea# Or (theta# > (180 - centrea#) And theta# <= 180)
If theta# > 90 Then theta# = 180 - theta#
dist# = (zlength / 2) / Cos(theta#)
Else
collision = 2
ExitFunction collision
EndIf
EndIf
dist# = dist# + buffer#
If Sqrt( Abs(px# - boxx#)^2 + Abs(pz# - boxz#)^2) < dist# Then collision = 1
EndFunction collision
And here is a Fully commented, working Example:
Sync On : Sync Rate 0
AutoCam Off
`Makes the "rectangle". Just a simple stretched cube that is in Wireframe
Make Object Cube 1,40
Position Object 1,0,-20,0
Scale Object 1,50,100,100
Set Object 1,0,0,1
objwidth = 20
objheight = 40
`Positions the camera above the rectangle to easily see it.
Position Camera Object Position X(1),200,Object Position Z(1)
Point Camera Object Position X(1),0,Object Position Z(1)
Randomize Timer()
`Makes 200 spheres and randomly positions them around the Rectangle
For x = 2 To 202
Make Object Sphere x,1
_random(x,objwidth,objheight)
Color Object x,RGB(255,0,0)
Next x
Do
For x = 2 To 202
`Loops through each sphere, passing it's current X and Z coords as the
`points to check for collision
coll = _rot_box(0,0,objwidth,objheight,Object Angle Y(1),Object Position X(x),Object Position Z(x),0.0)
`If no collision, then keep on moving the object
If coll = 0
Point Object x,0,0,0
Move Object x,1
EndIf
`If there was an error, then report it.
If coll = 2
Text 0,100,"Error"
EndIf
Next x
`Simple camera and object controls.
If RightKey() = 1 Then YRotate Object 1,WrapValue(Object Angle Y(1) + 1)
If LeftKey() = 1 Then YRotate Object 1,WrapValue(Object Angle Y(1) - 1)
If UpKey() = 1 Then Move Camera 5
If DownKey() = 1 Then Move Camera -5
`Just re-randomizes the positions of the spheres.
If InKey$() = "r"
For x = 2 To 202
_random(x,objwidth,objheight)
temp = temp * -1
Next x
EndIf
Text 0,20,"Press 'r' To reset positions of balls"
Sync
Loop
Function _random(x,objwidth,objheight)
`The Repeat... Until loops just makes sure no sphere's get placed
`inside the rectangle.
Repeat
objx = NewXValue(0,Rnd(360),Rnd(100) + 50)
objy = 0
objz = NewZValue(0,Rnd(360),Rnd(100) + 50)
Until _rot_box(0,0,objwidth,objheight,Object Angle Y(1),objx,objz,0.0) = 0
Position Object x,objx,objy,objz
Point Object x,0,0,0
EndFunction
Function _rot_box(boxx#,boxz#,xlength,zlength,boxrot#,px#,pz#,buffer#)
remstart
The method I'm using simply takes all the points into account,
converts them into a non-rotated box, then distance checks them all
boxx:
The position of the centre of the box on the X axis
boxz:
The position of the centre of the box on the Z axis
xlength
_____/____
| |
_ ___________
| | |
| | |
zlength < | |
| | |
|_ |___________|
boxrot:
The angle that box is rotated to currently ( 1 to 360, where 0 = 360 )
px:
The position of the External/Internal point on the X axis
pz:
The position of the External/Internal point on the Z axis
buffer:
This is the distance from the edge of the box that you want to check from.
ie, if you wanted to check the collision for 2 units outside the nox's
actuall dimensions, then set the buffer to 2.
The Function returns;
0 for no collision;
1 for collision;
2 for function error ( usually due to incorrect input values ).
remend
collision = 0
`Gets the angle between the centre of the xlength, and the diagonal from the centre to the
`corner of the rectangle
centrea# = ATan(Abs(xlength / 2) / Abs(zlength / 2))
`This takes the angle between the external/internal point, and the centre of the box
`and the xaxis and converts it to a point between the same lines, but on a
`non-rotated box.
theta# = WrapValue(ATan((px# - boxx#) / (pz# - boxz#)) - boxrot#)
`Simply makes the following calculation a little easier by restricting the
`angle to one side of the rectangle
If theta# >= 180
theta# = theta# - 180
EndIf
`This part is a little difficult to explain without Diagrams.
`Basically, the first IF function gets the collision distance for objects who's
`angle is between the lines going from the centre to the corners of the rectangle.
`The second if function gets the collision distance for object's that are in
`the remaining angles ( on the one side of the rectangle )
`The last IF is for if there is some sort of error somewhere
If theta# >= centrea# And theta# <= (180 - centrea#)
If theta# < 90 Then theta# = 90 - theta# Else theta# = theta# - 90
dist# = (xlength / 2) / Cos(theta#)
Else
If theta# < centrea# Or (theta# > (180 - centrea#) And theta# <= 180)
If theta# > 90 Then theta# = 180 - theta#
dist# = (zlength / 2) / Cos(theta#)
Else
collision = 2
ExitFunction collision
EndIf
EndIf
`Just extends/retracts the collision distance to comply with the buffer value
dist# = dist# + buffer#
`This is a simple distance check using Pythagorus Theorum.
`If the points tested are closer to the centre of the rectangle than
`the collision distance, then those points are colliding with the rectangle
If Sqrt( Abs(px# - boxx#)^2 + Abs(pz# - boxz#)^2) < dist# Then collision = 1
EndFunction collision
And here is the same example with a bit of debug info:
Sync On : Sync Rate 0
AutoCam Off
`Makes the "rectangle". Just a simple stretched cube that is in Wireframe
Make Object Cube 1,40
Position Object 1,0,-20,0
Scale Object 1,50,100,100
Set Object 1,0,0,1
objwidth = 20
objheight = 40
`Positions the camera above the rectangle to easily see it.
Position Camera Object Position X(1),200,Object Position Z(1)
Point Camera Object Position X(1),0,Object Position Z(1)
Randomize Timer()
`Makes 200 spheres and randomly positions them around the Rectangle
For x = 2 To 202
Make Object Sphere x,1
_random(x,objwidth,objheight)
Color Object x,RGB(255,0,0)
Next x
called = 0
Do
fps = Screen FPS()
For x = 2 To 202
`Loops through each sphere, passing it's current X and Z coords as the
`points to check for collision
st = Timer()
coll = _rot_box(0,0,objwidth,objheight,Object Angle Y(1),Object Position X(x),Object Position Z(x),0.0)
et = Timer() - st
time = time + et
Inc called
`If no collision, then keep on moving the object
If coll = 0
Point Object x,0,0,0
Move Object x,1
EndIf
`If there was an error, then report it.
If coll = 2
Text 0,100,"Error"
EndIf
Next x
`Simple camera and object controls.
If RightKey() = 1 Then YRotate Object 1,WrapValue(Object Angle Y(1) + 1)
If LeftKey() = 1 Then YRotate Object 1,WrapValue(Object Angle Y(1) - 1)
If UpKey() = 1 Then Move Camera 5
If DownKey() = 1 Then Move Camera -5
`Just re-randomizes the positions of the spheres.
If InKey$() = "r"
For x = 2 To 202
_random(x,objwidth,objheight)
temp = temp * -1
Next x
EndIf
Text 0,0,"Screen FPS() = " + Str$(fps)
Text 0,20,"Press 'r' To reset positions of balls"
Text 0,60,"Current Rotation = " + Str$(Object Angle Y(1))
Text 0,Screen Height() - 20,"Average Function call time = " + Str$(time / called) + "milliseconds"
Sync
Loop
Function _random(x,objwidth,objheight)
`The Repeat... Until loops just makes sure no sphere's get placed
`inside the rectangle.
Repeat
objx = NewXValue(0,Rnd(360),Rnd(100) + 50)
objy = 0
objz = NewZValue(0,Rnd(360),Rnd(100) + 50)
Until _rot_box(0,0,objwidth,objheight,Object Angle Y(1),objx,objz,0.0) = 0
Position Object x,objx,objy,objz
Point Object x,0,0,0
EndFunction
Function _rot_box(boxx#,boxz#,xlength,zlength,boxrot#,px#,pz#,buffer#)
remstart
The method I'm using simply takes all the points into account,
converts them into a non-rotated box, then distance checks them all
boxx:
The position of the centre of the box on the X axis
boxz:
The position of the centre of the box on the Z axis
xlength
_____/____
| |
_ ___________
| | |
| | |
zlength < | |
| | |
|_ |___________|
boxrot:
The angle that box is rotated to currently ( 1 to 360, where 0 = 360 )
px:
The position of the External/Internal point on the X axis
pz:
The position of the External/Internal point on the Z axis
buffer:
This is the distance from the edge of the box that you want to check from.
ie, if you wanted to check the collision for 2 units outside the nox's
actuall dimensions, then set the buffer to 2.
The Function returns;
0 for no collision;
1 for collision;
2 for function error ( usually due to incorrect input values ).
remend
collision = 0
`Gets the angle between the centre of the xlength, and the diagonal from the centre to the
`corner of the rectangle
centrea# = ATan(Abs(xlength / 2) / Abs(zlength / 2))
`This takes the angle between the external/internal point, and the centre of the box
`and the xaxis and converts it to a point between the same lines, but on a
`non-rotated box.
theta# = WrapValue(ATan((px# - boxx#) / (pz# - boxz#)) - boxrot#)
`Simply makes the following calculation a little easier by restricting the
`angle to one side of the rectangle
If theta# >= 180
theta# = theta# - 180
EndIf
`This part is a little difficult to explain without Diagrams.
`Basically, the first IF function gets the collision distance for objects who's
`angle is between the lines going from the centre to the corners of the rectangle.
`The second if function gets the collision distance for object's that are in
`the remaining angles ( on the one side of the rectangle )
`The last IF is for if there is some sort of error somewhere
If theta# >= centrea# And theta# <= (180 - centrea#)
If theta# < 90 Then theta# = 90 - theta# Else theta# = theta# - 90
dist# = (xlength / 2) / Cos(theta#)
Else
If theta# < centrea# Or (theta# > (180 - centrea#) And theta# <= 180)
If theta# > 90 Then theta# = 180 - theta#
dist# = (zlength / 2) / Cos(theta#)
Else
collision = 2
ExitFunction collision
EndIf
EndIf
`Just extends/retracts the collision distance to comply with the buffer value
dist# = dist# + buffer#
`This is a simple distance check using Pythagorus Theorum.
`If the points tested are closer to the centre of the rectangle than
`the collision distance, then those points are colliding with the rectangle
If Sqrt( Abs(px# - boxx#)^2 + Abs(pz# - boxz#)^2) < dist# Then collision = 1
EndFunction collision
I hope this helps some people out ( I know it sure helped me
)
Any comments and or suggestions would be extremely welcome
Thanks.
Jess.
Team EOD :: Programmer/Logical Engineer/All-Round Nice Guy