January 26, 2021, 11:49:43 AM

Author Topic: [bb] Billiards Style Collision Physics by Phish [ 1+ years ago ]  (Read 610 times)

Offline BlitzBot

  • Jr. Member
  • **
  • Posts: 1
Title : Billiards Style Collision Physics
Author : Phish
Posted : 1+ years ago

Description : 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@.... I think that realistic physics, even if pretty simple, can make a game feel a lot more immersive and interesting. The idea is that you should read my code, see how I use the collision response section, hack it up, and use it in your own game :-)

Code :
Code: BlitzBasic
  1. ;------------------------------------------
  2. ;2D COLLISION DEMO CODE
  3. ;------------------------------------------
  4. ; By Joseph 'Phish' Humfrey
  5. ; This type of collision response isn't
  6. ; just useful for pool and billiard style
  7. ; games, and in fact, I didn't write it for
  8. ; that reason at all. I wrote it because I
  9. ; needed to have collision for the space
  10. ; ships in my game 'Unity'. When their
  11. ; shields are up, they use this relatively
  12. ; simple method. It can be used for almost
  13. ; anything - player character bouncing off
  14. ; enemies in a platform game, to space
  15. ; ship collision, to a game which does
  16. ; actually involve balls. The basic
  17. ; algorithm which I used would work for
  18. ; both 2d and 3d, so if you would like to
  19. ; see it, email me at phish@aelius.com
  20. ;------------------------------------------
  21. ; The actual code which works out what
  22. ; happens after a collision is in the
  23. ; UpdateCirclePhysics() function.
  24. ;------------------------------------------
  25. ; Enjoy!
  26. ;------------------------------------------
  27. ;------------------------------------------
  28.  
  29. Graphics 800, 600, 16, 2
  30. SetBuffer BackBuffer()
  31. SeedRnd MilliSecs()
  32.  
  33.  
  34.  
  35. ;------------------------------------------
  36. ; MAIN DATA TYPE
  37. ;------------------------------------------
  38. ; This exact type isn't supposed to be used
  39. ; Instead, you should use some of the fields
  40. ; in your own type, or just use this one
  41. ; for reference, to see what each field does
  42. Type circle
  43.         Field x#, y#            ;position
  44.         Field dx#, dy#          ;x and y speeds
  45.        
  46.         Field radius#           ;radius of circle
  47.         Field mass#                     ;mass of circle
  48. End Type
  49. ;------------------------------------------
  50. ;------------------------------------------
  51.  
  52.  
  53.  
  54.  
  55.  
  56.  
  57. ;------------------------------------------
  58. ; SET UP BALLS INTO A POOL STYLE ARRANGEMENT
  59. ; FOR DEMO
  60. ;------------------------------------------
  61. .Setup
  62. ballTriangleSize=5
  63. For xloop = ballTriangleSize To 1 Step -1
  64.         For yloop = 1 To xloop
  65.                 c.circle = New circle
  66.                 cx = (5-xloop)*27 + 200
  67.                 cy = yloop*31-(xloop*31)/2.0 + 300
  68.                 cdx=0
  69.                 cdy=0
  70.                 c
  71. adius = 15
  72.                 cmass = 50
  73.         Next
  74. Next
  75.  
  76. ;Cue ball (smaller so you know which it is :)
  77. cue.circle = New circle
  78. cuex = 800
  79. cuey = 300 +20
  80. cuedx = -20
  81. cuedy = Rnd(4)-2
  82. cue
  83. adius = 14
  84. cuemass = 50
  85. ;------------------------------------------
  86. ;------------------------------------------
  87.  
  88.  
  89.  
  90.  
  91.  
  92.  
  93.  
  94. ;------------------------------------------
  95. ;MAIN LOOP
  96. ;------------------------------------------
  97. ; This is the main While..Wend game loop
  98. While Not KeyDown(1)
  99.  
  100.         Cls
  101.        
  102.         UpdateCirclePhysics()
  103.         RenderCircles()
  104.        
  105.         ;------------
  106.         ; Reset button
  107.         Text 10, 10, "Press a mouse button to reset."
  108.         Text 10, 25, "Press Esc to exit."
  109.         If GetMouse() Then
  110.                 For c.circle = Each circle
  111.                         Delete c
  112.                 Next
  113.                 Goto setup
  114.         End If
  115.         ;------------
  116.        
  117.         Flip
  118.        
  119. Wend
  120. ;------------------------------------------
  121. ;------------------------------------------
  122. End
  123.  
  124.  
  125.  
  126.  
  127.  
  128.  
  129.  
  130.  
  131.  
  132. ;------------------------------------------
  133. Function UpdateCirclePhysics()
  134. ;------------------------------------------
  135. ; This is the main physics function for the
  136. ; circles. It contains the very basic
  137. ; movement physics as well as the collision
  138. ; response code.
  139. ;------------------------------------------
  140.  
  141.         For c.circle = Each circle
  142.        
  143.                 ;update positions
  144.                 cx=cx+cdx
  145.                 cy=cy+cdy
  146.                
  147.                 ;gradually slow down
  148.                 cdx=cdx*0.99
  149.                 cdy=cdy*0.99
  150.                
  151.                 ;------------------------------------------
  152.                 ;COLLISION CHECKING
  153.                 ;------------------------------------------
  154.                 ; Check each circle in the loop against
  155.                 ; every other (c against c2)
  156.                 For c2.circle = Each circle
  157.                
  158.                         collisionDistance# = c
  159. adius+c2
  160. adius
  161.                         actualDistance# = Sqr((c2x-cx)^2+(c2y-cy)^2)
  162.                        
  163.                         ;Collided or not?
  164.                         If actualDistance<collisionDistance Then
  165.                                
  166.                                 collNormalAngle#=ATan2(c2y-cy, c2x-cx)
  167.  
  168.                                 ;Position exactly touching, no intersection
  169.                                 moveDist1#=(collisionDistance-actualDistance)*(c2mass/Float((cmass+c2mass)))
  170.                                 moveDist2#=(collisionDistance-actualDistance)*(cmass/Float((cmass+c2mass)))
  171.                                 cx=cx + moveDist1*Cos(collNormalAngle+180)
  172.                                 cy=cy + moveDist1*Sin(collNormalAngle+180)
  173.                                 c2x=c2x + moveDist2*Cos(collNormalAngle)
  174.                                 c2y=c2y + moveDist2*Sin(collNormalAngle)
  175.                                
  176.                                
  177.                                 ;------------------------------------------
  178.                                 ;COLLISION RESPONSE
  179.                                 ;------------------------------------------
  180.                                 ;n = vector connecting the centers of the circles.
  181.                                 ;we are finding the components of the normalised vector n
  182.                                 nX#=Cos(collNormalAngle)
  183.                                 nY#=Sin(collNormalAngle)
  184.                                
  185.                                 ;now find the length of the components of each movement vectors
  186.                                 ;along n, by using dot product.
  187.                                 a1# = cdx*nX  +  cdy*nY
  188.                                 a2# = c2dx*nX +  c2dy*nY
  189.                                
  190.                                 ;optimisedP = 2(a1 - a2)
  191.                                 ;             ----------
  192.                                 ;              m1 + m2
  193.                                 optimisedP# = (2.0 * (a1-a2)) / (cmass + c2mass)
  194.                                
  195.                                 ;now find out the resultant vectors
  196.                                 ;r1 = c1v - optimisedP * mass2 * n
  197.                                 cdx = cdx - (optimisedP*c2mass*nX)
  198.                                 cdy = cdy - (optimisedP*c2mass*nY)
  199.                                
  200.                                 ;r2 = c2v - optimisedP * mass1 * n
  201.                                 c2dx = c2dx + (optimisedP*cmass*nX)
  202.                                 c2dy = c2dy + (optimisedP*cmass*nY)
  203.  
  204.                         End If
  205.  
  206.                 Next
  207.                 ;------------------------------------------
  208.                 ;------------------------------------------
  209.                
  210.                
  211.                 ;Simple Bouncing off walls.
  212.                 If cx<c
  213. adius Then
  214.                         cx=c
  215. adius
  216.                         cdx=cdx*-0.9
  217.                 End If
  218.                 If cx>GraphicsWidth()-c
  219. adius Then
  220.                         cx=GraphicsWidth()-c
  221. adius
  222.                         cdx=cdx*-0.9
  223.                 End If
  224.                 If cy<c
  225. adius Then
  226.                         cy=c
  227. adius
  228.                         cdy=cdy*-0.9
  229.                 End If
  230.                 If cy>GraphicsHeight()-c
  231. adius Then
  232.                         cy=GraphicsHeight()-c
  233. adius
  234.                         cdy=cdy*-0.9
  235.                 End If
  236.                
  237.         Next
  238.  
  239. End Function
  240.  
  241.  
  242.  
  243.  
  244.  
  245. ;------------------------------------------
  246. Function RenderCircles()
  247. ;------------------------------------------
  248. ; Simple function draws all the circles
  249. ; on the screen.
  250. ;------------------------------------------
  251.  
  252.         For c.circle = Each circle
  253.                 If c
  254. adius=15 Then Color 200, 50, 50 Else Color 255, 255, 255
  255.                 Oval cx-c
  256. adius, cy-c
  257. adius, c
  258. adius*2, c
  259. adius*2
  260.         Next
  261.        
  262. End Function
  263. ;------------------------------------------
  264. ;------------------------------------------


Comments :


Ching(Posted 1+ years ago)

 I could have scored much better in my advanced graphics module if I'd found this code a couple of years ago.This code is really good.


Matty(Posted 1+ years ago)

 The magic numbers in there for friction and elasticity of collision would be better replaced with parameters/fields that can be set as required.


BlitzSupport(Posted 1+ years ago)

 This is great stuff! Here are some quick-and-dirty BMX/Monkey ports...BlitzMax:
Code: [Select]

' By Joseph 'Phish' Humfrey

Graphics 800, 600

SeedRnd MilliSecs()



'------------------------------------------
' Main DATA Type
'------------------------------------------
' This exact Type isn't supposed to be used
' Instead, you should use some of the fields
' in your own Type, Or just use this one
' For reference, To see what each Field does
Type circle
Field x#, y# 'position
Field dx#, dy# 'x And y speeds

Field radius# 'radius of circle
Field mass# 'mass of circle
End Type

Global Circles:TList = CreateList ()

'------------------------------------------
'------------------------------------------






'------------------------------------------
' SET UP BALLS INTO A POOL STYLE ARRANGEMENT
' For DEMO
'------------------------------------------
Function Setup ()

ballTriangleSize=5

For xloop = ballTriangleSize To 1 Step -1
For yloop = 1 To xloop
c:circle = New circle
c.x = (5-xloop)*27 + 200
c.y = yloop*31-(xloop*31)/2.0 + 300
c.dx=0
c.dy=0
c.radius = 15
c.mass = 50
ListAddLast Circles, c
Next
Next

'Cue ball (smaller so you know which it is :)
cue:circle = New circle
cue.x = 800
cue.y = 300 +20
cue.dx = -20
cue.dy = Rnd(4)-2
cue.radius = 14
cue.mass = 50
ListAddLast Circles, cue

End Function

'------------------------------------------
'------------------------------------------



Setup



'------------------------------------------
'Main LOOP
'------------------------------------------
' This is the Main While..Wend game loop
While Not KeyDown(KEY_ESCAPE)

Cls

UpdateCirclePhysics()
RenderCircles()

'------------
' Reset button
DrawText "Press a mouse button to reset.", 10, 10
DrawText "Press Esc to exit.", 10, 25
If MouseHit(1) Then
For c:circle = EachIn Circles
ClearList Circles
Next
Setup
End If
'------------

Flip

Wend
'------------------------------------------
'------------------------------------------
End









'------------------------------------------
Function UpdateCirclePhysics()
'------------------------------------------
' This is the Main physics Function For the
' circles. It contains the very basic
' movement physics as well as the collision
' response code.
'------------------------------------------

For c:circle = EachIn Circles

'update positions
c.x=c.x+c.dx
c.y=c.y+c.dy

'gradually slow down
c.dx=c.dx*0.99
c.dy=c.dy*0.99

'------------------------------------------
'COLLISION CHECKING
'------------------------------------------
' Check each circle in the loop against
' every other (c against c2)
For c2:circle = EachIn Circles

collisionDistance# = c.radius+c2.radius
actualDistance# = Sqr((c2.x-c.x)^2+(c2.y-c.y)^2)

'Collided Or Not?
If actualDistance<collisionDistance Then

collNormalAngle#=ATan2(c2.y-c.y, c2.x-c.x)

'Position exactly touching, no intersection
moveDist1#=(collisionDistance-actualDistance)*(c2.mass/Float((c.mass+c2.mass)))
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 circles.
'we are finding the components of the normalised vector n
nX#=Cos(collNormalAngle)
nY#=Sin(collNormalAngle)

'now find the length of the components of each movement vectors
'along n, by using dot product.
a1# = c.dx*nX  +  c.dy*nY
a2# = c2.dx*nX +  c2.dy*nY

'optimisedP = 2(a1 - a2)
'             ----------
'              m1 + m2
optimisedP# = (2.0 * (a1-a2)) / (c.mass + c2.mass)

'now find out the resultant vectors
'r1 = c1.v - optimisedP * mass2 * n
c.dx = c.dx - (optimisedP*c2.mass*nX)
c.dy = c.dy - (optimisedP*c2.mass*nY)

'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 Then
c.x=c.radius
c.dx=c.dx*-0.9
End If
If c.x>GraphicsWidth()-c.radius Then
c.x=GraphicsWidth()-c.radius
c.dx=c.dx*-0.9
End If
If c.y<c.radius Then
c.y=c.radius
c.dy=c.dy*-0.9
End If
If c.y>GraphicsHeight()-c.radius Then
c.y=GraphicsHeight()-c.radius
c.dy=c.dy*-0.9
End If

Next

End Function





'------------------------------------------
Function RenderCircles()
'------------------------------------------
' Simple Function draws all the circles
' on the screen.
'------------------------------------------

For c:circle = EachIn Circles
If c.radius=15 Then SetColor 200, 50, 50 Else SetColor 255, 255, 255
DrawOval c.x-c.radius, c.y-c.radius, c.radius*2, c.radius*2
Next

End Function
Monkey:
Code: [Select]

' By Joseph 'Phish' Humfrey

Import mojo

Global Circles:List <circle>

Class Pool Extends App

Method OnCreate ()
SetUpdateRate 60
Circles = New List <circle>
Seed = Millisecs ()
Setup
End

Method OnUpdate ()

UpdateCirclePhysics

If MouseHit ()

For Local c:circle = Eachin Circles
Circles.Clear
Next

Setup

Endif

End

Method OnRender ()

Cls

RenderCircles()

DrawText "Press a mouse button to reset.", 10, 10
DrawText "Press Esc to exit.", 10, 25

End

End




'------------------------------------------
' Main DATA Class
'------------------------------------------
' This exact Class isn't supposed to be used
' Instead, you should use some of the fields
' in your own Class, Or just use this one
' For reference, To see what each Field does
Class circle
Field x#, y# 'position
Field dx#, dy# 'x And y speeds

Field radius# 'radius of circle
Field mass# 'mass of circle
End


'------------------------------------------
' SET UP BALLS INTO A POOL STYLE ARRANGEMENT
' For DEMO
'------------------------------------------
Function Setup ()

Local ballTriangleSize=5

For Local xloop = ballTriangleSize To 1 Step -1
For Local yloop = 1 To xloop
Local c:circle = New circle
c.x = (5-xloop)*27 + 200
c.y = yloop*31-(xloop*31)/2.0 + 300
c.dx=0
c.dy=0
c.radius = 15
c.mass = 50
Circles.AddLast c
Next
Next

'Cue ball (smaller so you know which it is :)
Local cue:circle = New circle
cue.x = 800
cue.y = 300 +20
cue.dx = -20
cue.dy = Rnd(4)-2
cue.radius = 14
cue.mass = 50
Circles.AddLast cue

End

Function Main ()
New Pool
End









'------------------------------------------
Function UpdateCirclePhysics()
'------------------------------------------
' This is the Main physics Function For the
' circles. It contains the very basic
' movement physics as well as the collision
' response code.
'------------------------------------------

For Local c:circle = Eachin Circles

'update positions
c.x=c.x+c.dx
c.y=c.y+c.dy

'gradually slow down
c.dx=c.dx*0.99
c.dy=c.dy*0.99

'------------------------------------------
'COLLISION CHECKING
'------------------------------------------
' Check each circle in the loop against
' every other (c against c2)
For Local c2:circle = Eachin Circles

Local collisionDistance# = c.radius+c2.radius
Local actualDistance# = Sqrt(Pow(c2.x-c.x, 2)+Pow(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 circles.
'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
'r1 = c1.v - optimisedP * mass2 * n
c.dx = c.dx - (optimisedP*c2.mass*nX)
c.dy = c.dy - (optimisedP*c2.mass*nY)

'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 Then
c.x=c.radius
c.dx=c.dx*-0.9
End If
If c.x>DeviceWidth()-c.radius Then
c.x=DeviceWidth()-c.radius
c.dx=c.dx*-0.9
End If
If c.y<c.radius Then
c.y=c.radius
c.dy=c.dy*-0.9
End If
If c.y>DeviceHeight()-c.radius Then
c.y=DeviceHeight()-c.radius
c.dy=c.dy*-0.9
End If

Next

End





'------------------------------------------
Function RenderCircles()
'------------------------------------------
' Simple Function draws all the circles
' on the screen.
'------------------------------------------

For Local c:circle = Eachin Circles
If c.radius=15 Then SetColor 200, 50, 50 Else SetColor 255, 255, 255
DrawOval c.x-c.radius, c.y-c.radius, c.radius*2, c.radius*2
Next

End



Matty(Posted 1+ years ago)

 In the monkey example my browser freezes when compiled either for html5 or flash as soon as the user clicks the mouse after the balls have moved.  I'm using Chrome, and Monkey 45c.  Otherwise looks good.


BlitzSupport(Posted 1+ years ago)

 Bit odd -- I'm on Chrome and it's fine here. I also ran it without problems on GLFW and my Android phone.


Jesse(Posted 1+ years ago)

 works fine here in Safari and Chrome using monkey 53.I have been using this code for a few years although not for pool. Actually this code was what motivated me to do more research on vector physics and start learning all about vectors.it's good for fairly simple simulation. I wasn't able to use it as it moves balls apart when they over lap(causes a collision inaccuracy) instead of finding the exact point of collision and it fails completely if the speed of a ball exceed its diameter. I needed something that works dynamically.I find it useful for other things but was not able to use it for my pool game where I needed to find the exact point of collision.I am not nocking it down as it has it's uses and the collision response is really good but not for acceptable pool physics, at least for me it wasn't.


Hardcoal(Posted 1+ years ago)

 great submission


Captain Wicker (crazy hillbilly)(Posted 1+ years ago)

 lovely! :) [/i]

 

SimplePortal 2.3.6 © 2008-2014, SimplePortal