2d balls smacking together

BlitzMax Forums/BlitzMax Programming/2d balls smacking together

I'm making a little mini game...

In this mini game balls bounce around the screen and collide with eachother. There is no gravity so they continue on.

I am little confused by what exactly needs to happen to make the rebounding accurate..

What do you do exactly? Add their vectors to eachother?

If ball 1 is going in the direction (1.5,1.5) and ball 2 in a collision path going in the direction (-0.75,-0.75) what happens to their vectors when they collide?

Slow the speed and invert the directions?

It could extend to checking collision angles and appropriating the next direction (I don't know exactly how to do that though).

... Thats where vectors come in... I'm pretty sure theres a way to calculate the new speed/direction(vector) if you have the size of both circle and both vectors



I did find this... But I'm not sure what to make of it.


V2' = sqrt((cos θ * V2)^2 + (sin β * V1)^2))
V1' = sqrt((cos θ * V1)^2 + (sin β * V2)^2))

Where:
θ is the angle between the force vector of ball1 and the V1 direction vector, and
β is the angle between the force vector of ball2 and the V2 direction vector.



I'm going to run a couple tests and see if I can get this working in bmax with my vector module. Whats this force vector?

I went through this tutorial when I was trying to learn vector math:
http://tonypa.pri.ee/vectors/tut01.html
it is in flash but easy to understand. I myself don't know flash but was able to understand the concept really easy.
I created a little demo based on some stuff I learned from there.you can download it from my signature link below. It is in the demo Section
called 2d ball physics&source

That helps alot Jesse!

so I still am trying to figure this out... can someone help me with this below:

Ball collision(both have equal mass) procedure:



1. Constantly measure the abs distance between the centers of the balls
2. If abs distance < sum of radii then a collision has occured
3. the normal of the collision is perpendicular to the line that connects the two balls
?4. Ball #1's new vector is a result somehow of v1b and v2a
?5. Ball #2's new vector is a result somehow of v1a and v2b

see if this helps you
ob is ball 1 and ob2 is ball 2 they are both vectors
newv=bounce(ob2, ob)
ob.vx=newv.vx1 'new direction of ball 1
ob.vy=newv.vy1 
ob2.vx=newv.vx2 'new direction of ball 2 
ob2.vy=newv.vy2

Function bounce:vector2d(v2:vector2d, v:vector2d)
	Local proj11:vector2d=projectVector(v, v.dx, v.dy)		'projection of v1 on v
	Local proj12:vector2d=projectVector(v, v.lx, v.ly)		'projection of v1 on v normal
	Local proj21:vector2d=projectVector(v2, v.dx, v.dy)		'projection of v2 on v
	Local proj22:vector2d=projectVector(v2, v.lx, v.ly)		'projection of v2 on v normal

	Local P#= v.m * proj11.vx + v2.m * proj21.vx;
	Local Vn#=proj11.vx-proj21.vx;
	Local v2fx#=(P+Vn*v.m)/(v.m+v2.m);	
	Local v1fx#=v2fx-Vn;

	P#=v.m*proj11.vy+v2.m*proj21.vy;
        Vn#=proj11.vy-proj21.vy;
	Local v2fy#=(P+Vn*v.m)/(v.m+v2.m);
	Local v1fy#=v2fy-Vn;

	Local proj:vector2d  = New vector2d
	'add the projections For v1
	proj.vx1=proj12.vx+v1fx;
	proj.vy1=proj12.vy+v1fy;
	'add the projections For v2
	proj.vx2=proj22.vx+v2fx;
	proj.vy2=proj22.vy+v2fy;
	Return proj
End Function
    
  ' project vector v1 on unit-sized vector dx/dy
Function projectVector:vector2d (v:vector2d, dx#, dy#)
   ' find dot product
   Local dp# = v.vx*dx+v.vy*dy;
   Local proj:vector2d = New vector2d
   ' projection components
   proj.vx = dp*dx;
   proj.vy = dp*dy;
   Return proj;
End Function


I found this in my files maybe this can help you:
SuperStrict
'------------------------------------------
'2D COLLISION DEMO 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@...
'------------------------------------------
' The actual code which works out what
' happens after a collision is in the
' UpdateCirclePhysics() Function.
'------------------------------------------
' Enjoy!
'------------------------------------------
'------------------------------------------

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 list:TList = New TList

'------------------------------------------
' SET UP BALLS INTO A POOL STYLE ARRANGEMENT
' For DEMO
'------------------------------------------

Function setup()
	Local ballTriangleSize%=5
	For Local xloop:Int = ballTriangleSize To 1 Step -1
		For Local yloop:Int = 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
			list.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
	list.addlast(cue)
End Function
'------------------------------------------
'------------------------------------------







'------------------------------------------
'MAIN LOOP
'------------------------------------------
' This is the main While..Wend game loop
setup()
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 MouseDown(1)  Then
		For Local c:circle = EachIn list
			list.remove(c)
		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 Local c:circle = EachIn list
	
		'update positions
		c.x=c.x+c.dx
		c.y=c.y+c.dy
		
		'gradually slow down
		c.dx=c.dx*0.991 '0 stop. 1 constant. >1 increase 
		c.dy=c.dy*0.991
		
		'------------------------------------------
		'COLLISION CHECKING
		'------------------------------------------
		' Check each circle in the loop against
		' every other (c against c2)
		For Local c2:circle = EachIn list
		
			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 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>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 Local c:circle = EachIn list
		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
'------------------------------------------
'------------------------------------------


Yeah I finally got it after re-reading the resource you linked...

Thanks for all the help guys... Seems the hardest part of this minigame is done now!

Odd... The balls seem to get on a straight x/y path alot when they are hit by another ball... Which the chances are really low that this could happen?

Does anyone know why the balls behave like this looking at the source below?


AppTitle = "Ball Bounce Test"
Graphics 400,500

Global BallList:TList = CreateList()

Type TPoint
	Field x:Float, y:Float
End Type

Type EnemyBall
	Field Vec:TVec2
	Field point:TPoint
	Field radius:Int
	Field mass:Int
	
	Method New()
		point = New TPoint
		radius = 10
		chance:Float = Rnd(-3,3)
		Vec = New TVec2.Init(2,2)
		Vec.Multiply(chance,chance-1.0203)
		point.x = Rand(20, 380)
		point.y = Rand(20, 480)
		mass = 30
		ListAddLast(BallList,Self)
	End Method
	
	Function UpdateAll()
		For Local i:EnemyBall = EachIn BallList
			i.Update()
		Next
	End Function
	
	Method Update()
		'update positions
		point.x:+Vec.x
		point.y:+Vec.y
		
		If point.x<radius Then
			point.x=radius
			vec.x=vec.x*-1.0
		End If
		If point.x>GraphicsWidth()-radius Then
			point.x=GraphicsWidth()-radius
			vec.x=vec.x*-1.0
		End If
		If point.y<radius Then
			point.y=radius
			vec.y=vec.y*-1.0
		End If
		If point.y>GraphicsHeight()-radius Then
			point.y=GraphicsHeight()-radius
			vec.y=vec.y*-1.0
		End If
		For Local ball2:Enemyball = EachIn BallList
		
			Local collisionDistance# = radius+ball2.radius
			Local actualDistance# = Sqr((ball2.point.x-point.x)^2+(ball2.point.y-point.y)^2)
			
			If actualDistance<collisionDistance Then
				Local collNormalAngle#=ATan2(ball2.point.y-point.y, ball2.point.x-point.x)
				
				'Position exactly touching, no intersection
				Local moveDist1#=(collisionDistance-actualDistance)*(ball2.mass/Float((mass+ball2.mass)))
				Local moveDist2#=(collisionDistance-actualDistance)*(mass/Float((ball2.mass+mass)))
				point.x=point.x + moveDist1*Cos(collNormalAngle+180)
				point.y=point.y + moveDist1*Sin(collNormalAngle+180)
				ball2.point.x=ball2.point.x + moveDist2*Cos(collNormalAngle)
				ball2.point.y=ball2.point.y + moveDist2*Sin(collNormalAngle)
				
				
				Local n:TVec2 = New TVec2.Init(Cos(collNormalAngle),Sin(collNormalAngle))

				Local a1# = vec.DotProduct(n)
				Local a2# = ball2.vec.DotProduct(n)

				Local optimisedP# = (2.0 * (a1-a2)) / (mass + ball2.mass)
				
				vec.x = vec.x - (optimisedP*ball2.mass*n.x)
				vec.y = vec.y - (optimisedP*ball2.mass*n.y)
				
				ball2.vec.x = ball2.vec.x + (optimisedP*mass*n.x)
				ball2.vec.y = ball2.vec.y + (optimisedP*mass*n.y)
			
			End If
		Next
	End Method
	
	Function RenderAll()
		For Local i:EnemyBall = EachIn BallList
			i.Render()
		Next
	End Function
	Method Render()
		DrawOval point.x-radius,point.y-radius,radius*2,radius*2
	End Method
End Type

For Local i:Int = 0 To 3
	Local ball:Enemyball = New Enemyball
Next

While Not KeyHit(KEY_ESCAPE)
	Cls
	EnemyBall.UpdateAll()
	EnemyBall.RenderAll()
	
	Flip
Wend
End


change rand to rnd.