Ive been slowly working out the maths involved in getting 2D sliding collision working and I think Ive solved the main bulk of it.
Here's a fairly large code snippet including a few helpfull functions from a math library ive been building that calculates a normal and the proper sliding collision position when a line intersects the normal (the sliding collision position is stored in bounce.x2# and bounce.y2#).
code:
`2D Collision Demonstration
`Demonstrates:
`- Point of intersection
`- Line's normal angle
`- Sliding collision position
`- Distance to intersection
`- Incidence angle
`app settings
SYNC ON:SYNC RATE 0
BACKDROP ON:COLOR BACKDROP RGB(000,000,000)
INK RGB(255,255,255),0
SET TEXT FONT "Courier New":SET TEXT SIZE 15
`setup vectors (only for distance checks)
math_setup()
`normal data
TYPE lineT
x1#,y1#
x2#,y2#
angle#
projX1#,projY1#
projX2#,projY2#
projDist#
ENDTYPE
`ray data
TYPE rayT
x1#,y1#
x2#,y2#
angle#
ENDTYPE
`sliding collision data
TYPE bounceT
x1#,y1#
x2#,y2#
returnDist#
ENDTYPE
TYPE yesnoT
t$
ENDTYPE
GLOBAL normal AS lineT
GLOBAL ray AS rayT
GLOBAL bounce AS bounceT
GLOBAL intX#
GLOBAL intY#
DIM yesno(1) AS yesnoT
normal.x1# = 100
normal.y1# = SCREEN WIDTH() / 2
normal.x2# = 400
normal.y2# = SCREEN WIDTH() / 2
normal.projDist# = 100
ray.x1# = 70
ray.y1# = 200
yesno(0).t$ = "No"
yesno(1).t$ = "Yes"
`main loop
DO
`allow the user to move the incident ray around
INC ray.x1#, (RIGHTKEY()-LEFTKEY())*0.1
INC ray.y1#, (DOWNKEY()-UPKEY())*0.1
`allow the user to "rotate" the normal
INC normal.y1#, (KEYSTATE(31)-KEYSTATE(17))*0.1
DEC normal.y2#, (KEYSTATE(31)-KEYSTATE(17))*0.1
`store the incident ray's endpoints
ray.x2# = MOUSEX()
ray.y2# = MOUSEY()
`data calculations:
`checkif an intersection has occured
intersection = math_intersectLines(normal.x1#,normal.y1#,normal.x2#,normal.y2#,ray.x1#,ray.y1#,ray.x2#,ray.y2#)
`get the distance to the intersection
distance# = math_getDist2D(ray.x1#,ray.y1#,intX#,intY#)
`get the length of the incident ray
length# = math_getDist2D(ray.x1#,ray.y1#,ray.x2#,ray.y2#)
`get the angle of the incident ray
ray.angle# = math_getAngle(ray.x1#,ray.y1#,ray.x2#,ray.y2#)
`get the normal's angle
normal.angle# = math_getangle(normal.x1#,normal.y1#,normal.x2#,normal.y2#) + 90
`calculate a line segment pointing in the normal's direction
normal.projX1# = (normal.x2# + normal.x1#) / 2
normal.projY1# = (normal.y2# + normal.y1#) / 2
normal.projX2# = NEWXVALUE(normal.projX1#,normal.angle#,normal.projDist#)
normal.projY2# = NEWZVALUE(normal.projY1#,normal.angle#,normal.projDist#)
`if an intersection occured
IF intersection
`calculate sliding data
bounce.x1# = ray.x2#
bounce.y1# = ray.y2#
bounce.returnDist# = math_distanceToLine(ray.x2#,ray.y2#,normal.x1#,normal.y1#,normal.x2#,normal.y2#)
bounce.x2# = NEWXVALUE(ray.x2#,normal.angle#,bounce.returnDist#)
bounce.y2# = NEWZVALUE(ray.y2#,normal.angle#,bounce.returnDist#)
LINE bounce.x1#,bounce.y1#,bounce.x2#,bounce.y2#
CIRCLE bounce.x2#,bounce.y2#,5
ENDIF
`display
LINE normal.x1#,normal.y1#,normal.x2#,normal.y2#
LINE ray.x1#,ray.y1#,ray.x2#,ray.y2#
LINE normal.projX1#,normal.projY1#,normal.projX2#,normal.projY2#
`text
TEXT 0,0,"Frames Per Second: "+STR$(SCREEN FPS())
TEXT 0,30,"Intersection: " + yesno(intersection).t$
TEXT 0,45,"Distance to Intersection: "+STR$(distance#)
TEXT 0,60,"Angle of incidence: "+STR$(ray.angle#)
TEXT 0,75,"Angle of normal: "+STR$(normal.angle#)
TEXT 0,90,"Incident Ray length: "+STR$(length#)
`display
CIRCLE intX#,intY#,5
CENTER TEXT intX#,intY# - 15,"("+STR$(INT(intX#))+","+STR$(INT(intY#))+")"
CENTER TEXT bounce.x2#,bounce.y2# - 15,"("+STR$(INT(bounce.x2#))+","+STR$(INT(bounce.y2#))+")"
`refresh
SYNC
`repeat
LOOP
`math library
`intersect lines
`checks if 2 line segments are intersecting
`returns 1 on intersection, 0 on no intersection
FUNCTION math_intersectLines(x1#,y1#,x2#,y2#,x3#,y3#,x4#,y4#)
denominator# = ((y4# - y3#)*(x2# - x1#)) - ((x4# - x3#)*(y2# - y1#))
`If denominator = 0, then the lines are parallel
IF denominator# <> 0
numerator# = ((x4# - x3#)*(y1# - y3#)) - ((y4# - y3#)*(x1# - x3#))
uA# = numerator# / denominator#
numerator# = ((x2# - x1#)*(y1# - y3#)) - ((y2# - y1#)*(x1# - x3#))
uB# = numerator# / denominator#
`Calculate the point of intersection and store it
`Remove these 2 lines if you're sure you wont need the
`x/y coordinate of intersection.
intX# = x1# + (uA#*(x2# - x1#))
intY# = y1# + (uB#*(y2# - y1#))
`If uA and uB > 0 and < 1 then an intersection occured
IF (uA# > 0) AND (uA# < 1) AND (uB# > 0) AND (uB# < 1)
collision = 1
ELSE
collision = 0
ENDIF
ELSE
collision = 0
ENDIF
ENDFUNCTION collision
`distance to line (by phaelex of the tgc forums)
`finds the shortest distance from point px#,py# to the line segment
FUNCTION math_distanceToLine(px#,py#,lax#,lay#,lbx#,lby#)
n# = (px#-lax#)*(lbx#-lax#) + (py#-lay#)*(lby#-lay#)
x# = lbx# - lax#
y# = lby# - lay#
d# = x#*x# + y#*y#
u# = n# / d#
dist# = 0.0
IF u# >= 0.0 AND u# <= 1.0
ix# = lax# + u#*(lbx#-lax#)
iy# = lay# + u#*(lby#-lay#)
dist# = (ix#-px#)^2 + (iy#-py#)^2
ELSE
IF u# < 0 then dist# = (lax#-px#)^2 + (lay#-py#)^2
IF u# > 1 then dist# = (lbx#-px#)^2 + (lby#-py#)^2
ENDIF
dist# = SQRT(dist#)
ENDFUNCTION dist#
`setup math library vectors
FUNCTION math_setup()
`setup required vectors for distance calculations
null = MAKE VECTOR2(2)
#CONSTANT vecDist2D = 2
ENDFUNCTION
`get distance (2D)
`gets distance between two 2D points using vector lengths
FUNCTION math_getDist2D(X1#,Y1#,X2#,Y2#)
SET VECTOR2 vecDist2D,X1#-X2#,Y1#-Y2#
dist# = LENGTH VECTOR2(vecDist2D)
ENDFUNCTION dist#
`get angle
`gets angle between two 2D points
FUNCTION math_getAngle(X1#,Y1#,X2#,Y2#)
angle# = ATANFULL(X2#-X1#,Y2#-Y1#)
ENDFUNCTION angle#
My next step is to set up more than 1 normal and get the proper sliding position after the incident ray has hit both normals (simulating concave corners).
If anyones wondering, I plan to implement this into my Gibble Blops game, Ive already built a map editor that allows for the creation of maps by drawing lines, afterwards I load all of the lines into the game and draw them. I plan on then looping through each line checking for intersection/sliding collision using a line segment cast from the player's old position to their new position. If all goes well and I get this working solidly Ill add it to the stickied "Massive Collision Resource" post.
(Use W/S to change the normal, and the mouse + arrow keys to change the incident ray in the code snippet).
<edit>
And
here's a demo of what Ive gotten so far. Its working exactly as planned right now, the only problem is at 1 specific angle the sphere goes through the wall a bit and then gets repositioned, I know why, I just dont know if I can figure out how to fix it or not
.
</edit>
- RUC'