Circle to Circle Collision

BlitzMax Forums/BlitzMax Programming/Circle to Circle Collision

Heres a simple 2D Snooker/Pool example I converted to Max ...

The main Circle<>Circle collision checking is done via the UpdateBallPhysics() function.
You might want to play around with the FRICTION, BALL_MASS, and BALL_RADIUS constants too.


Rem
 Title: Billiards Style Collision Physics
 Description: 2D circle collision response which
 can be used where 2 objects collide in 2 dimensions

 2D COLLISION CODE

 By Joseph 'Phish' Humfrey
 This Type of collision response isn't just useful For pool and billiard style
 games, And in fact, I didn't write it for that reason at all. I wrote it because I
 needed To have collision for the space ships in my game 'Unity'.
 When their shields are up, they use this relatively simple method.
 It can be used For almost anything - player character bouncing off enemies in
 a platform game, to space ship collision, to a game which does actually involve
 balls. The basic algorithm which I used would work for both 2D and 3D, so If you
 would like to see it, email me at phish@...
End Rem

Const sw=800 , sh=600

Const FRICTION#		=	0.982
Const BALL_MASS#		=	160.0
Const BALL_RADIUS#	=	15.0

SetGraphicsDriver GLMax2DDriver()
Graphics sw,sh,0,60
SetClsColor 16,172,78

SeedRnd MilliSecs()

Global dx#=-1.0 , dy#=0 , power#=0.0

Global ballList:TList=New TList
Type ballType
	Field x#, y#     ' position
	Field dx#, dy#   ' x and y speeds
	Field radius#    ' radius of ball
	Field mass#      ' mass of ball
	Field r%,g%,b%   ' ball color
End Type

SetupTriangle
Global cueball:ballType=New ballType
SetupCueBall


' #############################################################

' MAIN LOOP
Repeat
	Global mx%,my%
	
	SetClsColor 60,140,80
	Cls
	UpdateBallPhysics
	RenderBalls

	' shoot
	If MouseDown(1)
		power=power+0.3+(power*0.04)
		If power>50.0 power=50.0
	Else
		If cueball.dx=0 And cueball.dy=0 ' ball stopped
			FlushMouse
			ang=angle(cueball.x,cueball.y,mx,my)
			cueball.dx=Sin(ang)*power ;	cueball.dy=-Cos(ang)*power
			power=0
		EndIf
	EndIf

	' reset
	If MouseHit(2)
		ballList.Clear
		SetupTriangle
		SetupCueBall
	End If

	' show shoot line when balls are stationary
	' (currently, only checks for cue ball stopped)
	If cueball.dx=0 And cueball.dy=0
		SetColor 180,180,180
		SetAlpha 0.4 ; SetBlend ALPHABLEND
		mx%=MouseX() ; my%=MouseY()
		dx=(mx-cueball.x)/20 ; dy=(my-cueball.y)/20
		DrawLine cueball.x,cueball.y,mx,my
		DrawOval mx-cueball.radius, my-cueball.radius, cueball.radius*2, cueball.radius*2
		DrawOval mx-cueball.radius+1, my-cueball.radius+1, cueball.radius*2-2, cueball.radius*2-2
		SetAlpha 1.0
	EndIf

	' draw power bar
	SetColor 0,0,0 ; SetAlpha 0.7
	DrawRect sw*0.3-1,sh-26,50*4+2,18
	SetColor 170,251,206
	DrawRect sw*0.3,sh-25,power*4,16

	SetAlpha 1.0 ; SetBlend MASKBLEND
	SetColor 200,200,200
	DrawText "[LMB] Shoot   [RMB] Reset    [Esc] Exit",10,10
	
	Flip
Until KeyHit(KEY_ESCAPE)

End

' #############################################################


Function SetupTriangle()
	Const ballTriangleSize%=5
	For Local xloop% = ballTriangleSize To 1 Step -1
		For Local yloop% = 1 To xloop
			Local c:ballType = New ballType
			ballList.AddLast c
			c.radius = BALL_RADIUS
			c.mass = BALL_MASS
			c.x = (5-xloop)*27 + 200
			c.y = (yloop*31) - (xloop*31)/2.0 + (sh/2)
			c.r=Rand(255) ; c.g=Rand(255) ; c.b=Rand(255)
		Next
	Next
End Function

Function SetupCueBall()
	cueball.x = sw-BALL_RADIUS-8
	cueball.y = sh/2
	cueball.dx=0.0 ; cueball.dy=0.0
	cueball.radius = BALL_RADIUS
	cueball.mass = BALL_MASS
	cueball.r=255 ; cueball.g=255 ; cueball.b=255
	ballList.Addlast cueball
End Function

Function UpdateBallPhysics()
' This is the main physics function for the
' balls. It contains the very basic movement
' physics as well as the collision response code
	For Local c:ballType = EachIn ballList
		' update positions
		c.x=c.x+c.dx
		c.y=c.y+c.dy
		' gradually slow down
		c.dx=c.dx*FRICTION
		c.dy=c.dy*FRICTION
		' stop completely when below a certain speed
		If Abs(c.dx)<0.068 Then c.dx=0
		If Abs(c.dy)<0.068 Then c.dy=0
		' COLLISION CHECKING
		' Check each ball in the loop against
		' every other (c against c2)
		For Local c2:ballType = EachIn ballList
			Local collisionDistance# = c.radius+c2.radius
			Local actualDistance# = Sqr((c2.x-c.x)^2+(c2.y-c.y)^2)
			' collided or not?
			If actualDistance<collisionDistance Then
				Local collNormalAngle#=ATan2(c2.y-c.y, c2.x-c.x)
				' position exactly touching, no intersection
				Local moveDist1#=(collisionDistance-actualDistance)*(c2.mass/Float((c.mass+c2.mass)))
				Local moveDist2#=(collisionDistance-actualDistance)*(c.mass/Float((c.mass+c2.mass)))
				c.x=c.x + moveDist1*Cos(collNormalAngle+180)
				c.y=c.y + moveDist1*Sin(collNormalAngle+180)
				c2.x=c2.x + moveDist2*Cos(collNormalAngle)
				c2.y=c2.y + moveDist2*Sin(collNormalAngle)
				' COLLISION RESPONSE
				' n = vector connecting the centers of the balls.
				' we are finding the components of the normalised vector n
				Local nX#=Cos(collNormalAngle)
				Local nY#=Sin(collNormalAngle)
				' now find the length of the components of each movement vectors
				' along n, by using dot product.
				Local a1# = c.dx*nX  +  c.dy*nY
				Local a2# = c2.dx*nX +  c2.dy*nY
				' optimisedP = 2(a1 - a2)
				'             ----------
				'              m1 + m2
				Local optimisedP# = (2.0 * (a1-a2)) / (c.mass + c2.mass)
				' now find out the resultant vectors
				'' Local r1% = c1.v - optimisedP * mass2 * n
				c.dx = c.dx - (optimisedP*c2.mass*nX)
				c.dy = c.dy - (optimisedP*c2.mass*nY)
				'' Local r2% = c2.v - optimisedP * mass1 * n
				c2.dx = c2.dx + (optimisedP*c.mass*nX)
				c2.dy = c2.dy + (optimisedP*c.mass*nY)
			End If
		Next
		' Simple bouncing off walls.
		If c.x<c.radius
			c.x=c.radius
			c.dx=c.dx*-0.9
		End If
		If c.x>GraphicsWidth()-c.radius
			c.x=GraphicsWidth()-c.radius
			c.dx=c.dx*-0.9
		End If
		If c.y<c.radius
			c.y=c.radius
			c.dy=c.dy*-0.9
		End If
		If c.y>GraphicsHeight()-c.radius
			c.y=GraphicsHeight()-c.radius
			c.dy=c.dy*-0.9
		End If
	Next
End Function

Function RenderBalls()
	For Local c:ballType = EachIn ballList
		SetColor c.r,c.g,c.b
		DrawOval c.x-c.radius, c.y-c.radius, c.radius*2, c.radius*2
	Next
End Function

Function dist%(x1#, y1#, x2#, y2#)
	Local xd% = Abs(x1# - x2#) , yd% = Abs(y1# - y2#)
	Return Sqr(xd * xd + yd * yd)
End Function

Function angle%(x1#,y1#,x2#,y2#)
	Local angle%=ATan2(x2-x1,y2-y1)
	Return 180-angle
End Function