2d ball to line collision

Monkey Forums/Monkey Code/2d ball to line collision

Jesse(Posted 2012) [#1]
a simple example of a ball to line collision including gravity.
[monkeycode]
Strict

Import mojo


Function Main:Int()
New Game
Return 1
End Function

Class Point
Field x:Float
Field y:Float

Method New(x:Float,y:Float)
Self.x = x
Self.y = y
End Method
End Class


Class Game Extends App

Field velocity:Point
Field gravity:Float = .05
Field damping:Float = 0.999
Field segments:Int = 30
Field ground:Ground[]
Field peakHeights:Float[]
Field vertices:Float[]
Field ball:Ball
Field width:float
Field height:float



Method OnCreate:Int()

ground = New Ground[segments]
peakHeights = New Float[segments+1]
vertices = New Float[segments*2+2]
ball = New Ball(50, 50, 10)
velocity = New Point(.5, 0)
width = DeviceWidth()
height = DeviceHeight()

' Calculate ground peak heights
For Local i:Int = 0 Until peakHeights.Length()
peakHeights[i] = Rnd(height-50, height-100)
Next
#rem
/* Float value required For segment width (segs)
calculations so the ground spans the entire
display window, regardless of segment number. */
#End rem
Local segs:Float = segments
ground[0] = New Ground(0,480,0,peakHeights[0])
For Local i:Int = 1 Until segments
ground[i] = New Ground(width/segs*i, peakHeights[i],
width/segs*(i+1), peakHeights[i+1])
Next
ground[segments-1].x2 = 640
ground[segments-1].y2 = 480
SetUpdateRate 60

Return 1
End method

Method OnUpdate:Int()
' Move ball
ball.x += velocity.x
velocity.y += gravity
ball.y += velocity.y

' Collision detection
checkWallCollision()
For Local i:Int = 0 Until segments
checkGroundCollision(ground[i])
Next

Return 1
End Method

Method OnRender:Int()
Cls
SetColor 0,105,255
DrawRect(0, 0, width, height)
SetColor 255,0,255

For Local i:Int =0 Until segments
vertices[i*2] = ground[i].x1
vertices[i*2+1] = ground[i].y1
Next
vertices[segments*2] = ground[segments-1].x2
vertices[segments*2+1] = ground[segments-1].y2
DrawPoly(vertices)
SetColor 255,100,50
DrawCircle(ball.x, ball.y, ball.r)
Return 1
End Method


Method checkWallCollision:Void()
If (ball.x > width-ball.r)
ball.x = width-ball.r
velocity.x *= -1
velocity.x *= damping
Else If (ball.x < ball.r)
ball.x = ball.r
velocity.x *= -1
velocity.x *= damping
Endif
End Method


Method checkGroundCollision:Void(groundSegment:Ground)

' Get difference between ball and ground
Local deltaX:Float = ball.x - groundSegment.x
Local deltaY:Float = ball.y - groundSegment.y

' Precalculate trig values
Local cosine:Float = Cos(groundSegment.rot)
Local sine:Float = Sin(groundSegment.rot)
#rem
/* Rotate ground And velocity To allow
orthogonal collision calculations */
#End rem
Local groundXTemp:Float = cosine * deltaX + sine * deltaY
Local groundYTemp:Float = cosine * deltaY - sine * deltaX
Local velocityXTemp:Float = cosine * velocity.x + sine * velocity.y
Local velocityYTemp:Float = cosine * velocity.y - sine * velocity.x
#Rem
/* Ground collision - check For surface
collision And also that ball is within
left/rights bounds of ground segment */
#End rem
If (groundYTemp > -ball.r And
ball.x > groundSegment.x1 And
ball.x < groundSegment.x2 )
' keep ball from going into ground
groundYTemp = -ball.r
' bounce and slow down ball
velocityYTemp *= -1.0
velocityYTemp *= damping
End If

' Reset ground, velocity and ball
deltaX = cosine * groundXTemp - sine * groundYTemp
deltaY = cosine * groundYTemp + sine * groundXTemp
velocity.x = cosine * velocityXTemp - sine * velocityYTemp
velocity.y = cosine * velocityYTemp + sine * velocityXTemp
ball.x = groundSegment.x + deltaX
ball.y = groundSegment.y + deltaY
End Method
End Class

Class Ground
Field x1:Float
Field y1:Float
Field x2:Float
Field y2:float
Field x:Float
Field y:Float
Field len:Float
Field rot:Float

' Constructor
Method New(x1:Float, y1:Float, x2:Float, y2:Float)
Self.x1 = x1
Self.y1 = y1
Self.x2 = x2
Self.y2 = y2
x = (x1+x2)/2
y = (y1+y2)/2
len = dist(x1, y1, x2, y2)
rot = ATan2((y2-y1), (x2-x1))
End Method

Method dist:float(x1:Float,y1:Float,x2:Float,y2:Float)
Return Sqrt((x2-x1)*(x2-x1) +(y2-y1)*(y2-y1))
End Method

End Class

Class Ball
Field x:Float
Field y:Float
Field r:Float

Method New(x:Float, y:Float, r:Float)
Self.x = x
Self.y = y
Self.r = r
End Method

End Class

[/monkeycode]


Raz(Posted 2012) [#2]
Really useful for me thanks Jesse :)


Raz(Posted 2012) [#3]
Actually Jesse, maybe you can help.

is this code able to check collisions against lines of all angles?

For example, I added 4 additional Ground instances to form a square (I renamed the Ground class to Line)

tL.lines2 = New Line[4]
tL.lines2[0] = New Line(500.0, 200.0, 550.0, 200.0)
tL.lines2[1] = New Line(550.0, 200.0, 550.0, 250.0)
tL.lines2[2] = New Line(500.0, 250.0, 550.0, 250.0)
tL.lines2[3] = New Line(500.0, 200.0, 500.0, 250.0)


and when the left vertical line is hit (lines2[3]), the ball pops up on top of the square, however when lines2[0] is hit (horizontal line on top) the ball behaves as expected.

Any tips? :)

Thanks


Floyd(Posted 2012) [#4]
Algorithms with polygons usually make assumptions about orientation. Maybe that's the problem here.

In the original code the line segments are oriented consistently. As you travel from (x1,y1) to (x2,y2) the sky is on the left, ground on the right. Your lines 0 and 1 have this property, but lines 2 and 3 are the reverse.

As you define lines 1,2,3 the start point of each line should be the end point of the previous line.

If that doesn't work then post your code so we can see what is happening.


Raz(Posted 2012) [#5]
Hey Floyd, thanks for the reply. Yeah I thought this might be it, hence the dodgy order (originally it was in a more logical order for drawing a square)

I know it's not pretty, but here is the code I've been messing with




Floyd(Posted 2012) [#6]
The idea seems to be to rotate everything (line, ball, velocity vector) around the center of line so the line becomes horizontal. The collision checking and bounce are calculated for this easier configuration and the results are rotated back to the original orientation.

There's no obvious reason it wouldn't work for arbitrary angles, but something is going wrong.


Raz(Posted 2012) [#7]
It almost looks like the repositioning code just handles vertical stuff, I'll have a play


Jesse(Posted 2012) [#8]

It almost looks like the repositioning code just handles vertical stuff



yes it is. After the line and ball are rotated for movement calculations, the line is checked to see if the ball went below the line. If it did, the ball is moved above the line and then everything is rotated back to it's original orientations along with the correct bounce mechanism.

I learned to do that while I was researching the vector math. I had it in my hard Drive so I decided to post it here. I have not had much use for it really.

anyway I decided that I am going to create a vector object engine that works with a single ball and a line(s). I will post it as soon I am done with it. it shouldn't take me more than a day or two. it will have elastic and dynamic collision. stay tuned.


Raz(Posted 2012) [#9]
Cheers Jesse :)

I found what I believe to be your vector code examples on blitzbasic.com as well and I've been messing around converting them for Monkey.

I think it's about time I learnt about trigonometry properly seeing as I will probably end up using it in one way or another in just about everything I create!


Samah(Posted 2012) [#10]
Diddy has a 2D vector module if you want to take a look at it.


Jesse(Posted 2012) [#11]
@raz
Just be careful if you go through the tutorials posted by toypa they are good but have steps that I guess he didn't either know how to do or decided not to share(?) so he did some work around that had me puzzled. he did something somewhat similar to this circle to line demo above. I had to do a lot more searching around vector tutorials before I figured it out.


Raz(Posted 2012) [#12]
I'll have to have a look Samah, does it include reflection and point of intersection stuff?

Jesse: By chance I had been looking at these tutorials myself and I wasn't able to get my head around them!

With your blitzmax code though, from what I can tell, all angles worked fine :)