PONG Engine - Issue with bounce!?

BlitzMax Forums/BlitzMax Programming/PONG Engine - Issue with bounce!?

Trader3564(Posted 2015) [#1]
Hi,

I have an issue with this pong game (full source). First i just want the bounching balls done, then i take it from there. But its bugging even now.

This code is a port from this flash-actionshipt engine http://www.emanueleferonato.com/2007/08/19/managing-ball-vs-ball-collision-with-flash/

I think its very strange BlitzMax throws this unusual behaviour; After some time balls clutch together and drift to the upperleft corner. Try 30 balls for example.

Const TableWidth = 64;
Const TableHeight = 48;
Const TabelPixel = 10;

Graphics TableWidth * TabelPixel, TableHeight * TabelPixel

Type Tball
	Field _x:Double 
	Field _y:Double 
	Field xspeed:Double 
	Field yspeed:Double 
	Field mass:Int 
	Field collision:Int
End Type

Local number_of_balls = 5;
Local speed_limit = 10;

Local balls:Tball[number_of_balls]
Local ball:Tball
Local x:Int

'Create balls
For x=0 To number_of_balls -1
	ball:Tball = New Tball
	ball._x = Rand((TableWidth * TabelPixel)-50)+25
	ball._y = Rand((TableHeight * TabelPixel)-50)+25
	ball.collision = 0;
	ball.mass = 1;
	ball.xspeed = Rand(4.0) -2.0;
	ball.yspeed = Rand(4.0) -2.0;
	balls[x] = ball
Next

Function Bounce(ball:Tball, ball2:Tball)
	Local dx:Double = ball._x - ball2._x;
	Local dy:Double = ball._y - ball2._y;
	Local collisionision_angle:Double = ATan2(dy, dx);
	Local magnitude_1:Double = Sqr(ball.xspeed * ball.xspeed + ball.yspeed * ball.yspeed);
	Local magnitude_2:Double = Sqr(ball2.xspeed * ball2.xspeed + ball2.yspeed * ball2.yspeed);
	Local direction_1:Double = ATan2(ball.yspeed, ball.xspeed);
	Local direction_2:Double = ATan2(ball2.yspeed, ball2.xspeed);
	Local new_xspeed_1:Double = magnitude_1 * Cos(direction_1 -collisionision_angle);
	Local new_yspeed_1:Double = magnitude_1 * Sin(direction_1 -collisionision_angle);
	Local new_xspeed_2:Double = magnitude_2 * Cos(direction_2 -collisionision_angle);
	Local new_yspeed_2:Double = magnitude_2 * Sin(direction_2 -collisionision_angle);
	Local final_xspeed_1:Double = ((ball.mass -ball2.mass) * new_xspeed_1 + (ball2.mass + ball2.mass) * new_xspeed_2)/(ball.mass + ball2.mass);
	Local final_xspeed_2:Double = ((ball.mass + ball.mass) * new_xspeed_1 + (ball2.mass -ball.mass) * new_xspeed_2)/(ball.mass + ball2.mass);
	Local final_yspeed_1:Double = ((ball.mass -ball2.mass) * new_yspeed_1 + (ball2.mass + ball2.mass) * new_yspeed_2)/(ball.mass + ball2.mass);
	Local final_yspeed_2:Double = ((ball.mass + ball.mass) * new_yspeed_1 + (ball2.mass -ball.mass) * new_yspeed_2)/(ball.mass + ball2.mass);
	'Local final_xspeed_1:Double = new_xspeed_1;
	'Local final_xspeed_2:Double = new_xspeed_2;
	'Local final_yspeed_1:Double = new_yspeed_1;
	'Local final_yspeed_2:Double = new_yspeed_2;
	ball.xspeed = Cos(collisionision_angle) * final_xspeed_1 + Cos(collisionision_angle + Pi/2) * final_yspeed_1;
	ball.yspeed = Sin(collisionision_angle) * final_xspeed_1 + Sin(collisionision_angle + Pi/2) * final_yspeed_1;
	ball2.xspeed = Cos(collisionision_angle) * final_xspeed_2 + Cos(collisionision_angle + Pi/2) * final_yspeed_2;
	ball2.yspeed = Sin(collisionision_angle) * final_xspeed_2 + Sin(collisionision_angle + Pi/2) * final_yspeed_2;
End Function

Repeat
	'left pad
	'ball:Tball = balls[0]
	'ball.xspeed = 1
	'ball.yspeed = 0
	'ball._x = 50
	'ball._y = MouseY()
	
	'right pad
	'ball:Tball = balls[1]
	'ball.xspeed = 0
	'ball.yspeed = 0
	'ball._x = (TableWidth * TabelPixel) - 50
	'ball._y = MouseY()
	
	'pong
	For x=0 To number_of_balls -1
		ball:Tball = balls[x]
		
		If(ball._x<15) Then
			ball._x = 15;
			ball.xspeed = ball.xspeed *-1;
		Else If (ball._x>(TableWidth * TabelPixel) - 15) Then
			ball._x = (TableWidth * TabelPixel) - 15;
			ball.xspeed = ball.xspeed *-1;
		End If
		If (ball._y<15) Then
			ball._y = 15;
			ball.yspeed = ball.yspeed *-1;
		Else If (ball._y>(TableHeight * TabelPixel) - 15) Then
			ball._y = (TableHeight * TabelPixel) - 15
			ball.yspeed = ball.yspeed *-1;
		End If
		If (ball.xspeed>speed_limit) Then
			ball.xspeed = speed_limit;
		End If
		If (ball.xspeed<speed_limit*-1) Then
			ball.xspeed = speed_limit*-1;
		End If
		If (ball.yspeed>speed_limit) Then
			ball.yspeed = speed_limit;
		End If
		If (ball.yspeed<speed_limit*-1) Then
			ball.yspeed = speed_limit*-1;
		End If
		
		ball._x = ball._x + ball.xspeed;
		ball._y = ball._y + ball.yspeed;
	Next
	
	For x=0 To number_of_balls -1
		For y=x+1 To number_of_balls -1
			Local distance_x:Double = Abs(balls[x]._x -balls[y]._x);
			Local distance_y:Double = Abs(balls[x]._y -balls[y]._y);
			Local distance:Double  = Sqr(distance_x * distance_x + distance_y * distance_y);
			
			If (distance<=30 And (balls[x].collision = 0 Or balls[y].collision = 0)) Then
				balls[x].collision = 1;
				balls[y].collision = 1;
				Bounce(balls[x], balls[y]);
			Else If (distance>30) Then
				balls[x].collision = 0;
				balls[y].collision = 0;
			End If
		Next
	Next
	
	'SetColor 0,200,0
	'DrawOval balls[0]._x -15, balls[0]._y -15, 30, 30
	'SetColor 255,255,255
	'DrawOval balls[0]._x -13, balls[0]._y -13, 26, 26
	
	'SetColor 0,0,200
	'DrawOval balls[1]._x -15, balls[1]._y -15, 30, 30
	'SetColor 255,255,255
	'DrawOval balls[1]._x -13, balls[1]._y -13, 26, 26
	
	For x=0 To number_of_balls -1
		SetColor 200,0,0
		DrawOval balls[x]._x -15, balls[x]._y -15, 30, 30
		SetColor 255,255,255
		DrawOval balls[x]._x -13, balls[x]._y -13, 26, 26
	Next
	
	Flip
	Cls
Until KeyHit(KEY_ESCAPE) 'ESC



Trader3564(Posted 2015) [#2]
Ugh n.m. fixed it with re-calc the pos after the Bounce update

	'Fix distance Bug
	Local distance_x:Double = Abs(ball._x - ball2._x);
	Local distance_y:Double = Abs(ball._y - ball2._y);
	Local distance:Double = Sqr(distance_x * distance_x + distance_y * distance_y);
	ball2._x = ball2._x - Cos(collisionision_angle)*(30-distance);
	ball2._y = ball2._y - Sin(collisionision_angle)*(30-distance);



Brucey(Posted 2015) [#3]
Changing this:
	ball.xspeed = Cos(collisionision_angle) * final_xspeed_1 + Cos(collisionision_angle + Pi/2) * final_yspeed_1;
	ball.yspeed = Sin(collisionision_angle) * final_xspeed_1 + Sin(collisionision_angle + Pi/2) * final_yspeed_1;
	ball2.xspeed = Cos(collisionision_angle) * final_xspeed_2 + Cos(collisionision_angle + Pi/2) * final_yspeed_2;
	ball2.yspeed = Sin(collisionision_angle) * final_xspeed_2 + Sin(collisionision_angle + Pi/2) * final_yspeed_2;

to this, seemed to fix it for me :
	ball.xspeed = Cos(collisionision_angle ) * final_xspeed_1 + Cos(collisionision_angle  + 90 ) * final_yspeed_1;
	ball.yspeed = Sin(collisionision_angle ) * final_xspeed_1 + Sin(collisionision_angle + 90 ) * final_yspeed_1;
	ball2.xspeed = Cos(collisionision_angle ) * final_xspeed_2 + Cos(collisionision_angle + 90 ) * final_yspeed_2;
	ball2.yspeed = Sin(collisionision_angle ) * final_xspeed_2 + Sin(collisionision_angle + 90 ) * final_yspeed_2;


Angles in BlitzMax are in degrees, not radians.


Trader3564(Posted 2015) [#4]
Ah, cool! Yes, yours works. Got it.


Trader3564(Posted 2015) [#5]
Alright, i have it setup to work like pong. However im not happy with the movement. What i would like is to increase the speed? But more importantly, that the pads dont glitch.

I think its because im mis-using pads as balls :_) im terrible at math. Maybe someone would like to take a shot at it?

I guess what needs to be done is work with the angle instead of the x,y velocity. That way you can change the angle instead of re-doing the velocity math.
And then adjust the Bounce to work with the angle insead of the velocity.

Full source below (cut & run)

Const TableWidth = 14;
Const TableHeight = 7;
Const TabelPixel = 70;

Graphics TableWidth * TabelPixel, TableHeight * TabelPixel

Type Tball
	Field _x:Double 
	Field _y:Double 
	Field xspeed:Double 
	Field yspeed:Double 
	Field mass:Int 
	Field collision:Int
End Type

Local number_of_balls = 3;
Local speed_limit = 10;

Local balls:Tball[number_of_balls]
Local ball:Tball
Local x:Int

'Create balls
For x=0 To number_of_balls -1
	ball:Tball = New Tball
	ball._x = Rand((TableWidth * TabelPixel)-50)+25
	ball._y = Rand((TableHeight * TabelPixel)-50)+25
	ball.collision = 0;
	ball.mass = 1;
	ball.xspeed = 4*((Rand(0,1)*2)-1)
	ball.yspeed = 2*((Rand(0,1)*2)-1)
	balls[x] = ball
Next

Function Bounce(ball:Tball, ball2:Tball)
	Local dx:Double = ball._x - ball2._x;
	Local dy:Double = ball._y - ball2._y;
	Local collisionision_angle:Double = ATan2(dy, dx);
	Local magnitude_1:Double = Sqr(ball.xspeed * ball.xspeed + ball.yspeed * ball.yspeed);
	Local magnitude_2:Double = Sqr(ball2.xspeed * ball2.xspeed + ball2.yspeed * ball2.yspeed);
	Local direction_1:Double = ATan2(ball.yspeed, ball.xspeed);
	Local direction_2:Double = ATan2(ball2.yspeed, ball2.xspeed);
	Local new_xspeed_1:Double = magnitude_1 * Cos(direction_1 -collisionision_angle);
	Local new_yspeed_1:Double = magnitude_1 * Sin(direction_1 -collisionision_angle);
	Local new_xspeed_2:Double = magnitude_2 * Cos(direction_2 -collisionision_angle);
	Local new_yspeed_2:Double = magnitude_2 * Sin(direction_2 -collisionision_angle);
	Local final_xspeed_1:Double = ((ball.mass -ball2.mass) * new_xspeed_1 + (ball2.mass + ball2.mass) * new_xspeed_2)/(ball.mass + ball2.mass);
	Local final_xspeed_2:Double = ((ball.mass + ball.mass) * new_xspeed_1 + (ball2.mass -ball.mass) * new_xspeed_2)/(ball.mass + ball2.mass);
	'Local final_yspeed_1:Double = ((ball.mass -ball2.mass) * new_yspeed_1 + (ball2.mass + ball2.mass) * new_yspeed_2)/(ball.mass + ball2.mass);
	'Local final_yspeed_2:Double = ((ball.mass + ball.mass) * new_yspeed_1 + (ball2.mass -ball.mass) * new_yspeed_2)/(ball.mass + ball2.mass);
	'Local final_xspeed_1:Double = new_xspeed_1;
	'Local final_xspeed_2:Double = new_xspeed_2;
	Local final_yspeed_1:Double = new_yspeed_1;
	Local final_yspeed_2:Double = new_yspeed_2;
	
	'Radians
	'ball.xspeed = Cos(collisionision_angle) * final_xspeed_1 + Cos(collisionision_angle + Pi/2) * final_yspeed_1;
	'ball.yspeed = Sin(collisionision_angle) * final_xspeed_1 + Sin(collisionision_angle + Pi/2) * final_yspeed_1;
	'ball2.xspeed = Cos(collisionision_angle) * final_xspeed_2 + Cos(collisionision_angle + Pi/2) * final_yspeed_2;
	'ball2.yspeed = Sin(collisionision_angle) * final_xspeed_2 + Sin(collisionision_angle + Pi/2) * final_yspeed_2;
	
	'Degrees
	ball.xspeed = Cos(collisionision_angle ) * final_xspeed_1 + Cos(collisionision_angle  + 90 ) * final_yspeed_1;
	ball.yspeed = Sin(collisionision_angle ) * final_xspeed_1 + Sin(collisionision_angle + 90 ) * final_yspeed_1;
	ball2.xspeed = Cos(collisionision_angle ) * final_xspeed_2 + Cos(collisionision_angle + 90 ) * final_yspeed_2;
	ball2.yspeed = Sin(collisionision_angle ) * final_xspeed_2 + Sin(collisionision_angle + 90 ) * final_yspeed_2;
End Function

'Ball index: 0 = Left pad, 1 = Right pad, 2 = ball

Local ox,oy,nx,ny
Repeat
	'Detect Up or Down movement
	ox = nx
	oy = ny
	nx = MouseX()
	ny = MouseY()
	If ny>oy Then
		sy = 1
	Else
		sy = -1
	End If
	
	'Pads are fixed balls. Advantage: Circular  bounce radius (e.g. walls do just flat reflection)
	
	'left pad
	ball:Tball = balls[0]
	ball.xspeed = Abs(balls[2].xspeed)
	ball.yspeed = Abs(balls[2].yspeed)*sy
	ball._x = 50
	ball._y = MouseY()
	
	'right pad
	ball:Tball = balls[1]
	ball.xspeed = Abs(balls[2].xspeed)*-1
	ball.yspeed = Abs(balls[2].yspeed)*sy
	ball._x = (TableWidth * TabelPixel) - 50
	ball._y = MouseY()
	
	'pong
	For x=2 To number_of_balls -1
		ball:Tball = balls[x]
		
		If(ball._x<15) Then
			ball._x = 15;
			ball.xspeed = ball.xspeed *-1;
		Else If (ball._x>(TableWidth * TabelPixel) - 15) Then
			ball._x = (TableWidth * TabelPixel) - 15;
			ball.xspeed = ball.xspeed *-1;
		End If
		If (ball._y<15) Then
			ball._y = 15;
			ball.yspeed = ball.yspeed *-1;
		Else If (ball._y>(TableHeight * TabelPixel) - 15) Then
			ball._y = (TableHeight * TabelPixel) - 15
			ball.yspeed = ball.yspeed *-1;
		End If
		If (ball.xspeed>speed_limit) Then
			ball.xspeed = speed_limit;
		End If
		If (ball.xspeed<speed_limit*-1) Then
			ball.xspeed = speed_limit*-1;
		End If
		If (ball.yspeed>speed_limit) Then
			ball.yspeed = speed_limit;
		End If
		If (ball.yspeed<speed_limit*-1) Then
			ball.yspeed = speed_limit*-1;
		End If
		
		ball._x = ball._x + ball.xspeed;
		ball._y = ball._y + ball.yspeed;
	Next
	
	For x=0 To number_of_balls -2 'Pads check collision with..
		'For y=x+1 To number_of_balls -1
			Int y=2 '..the ball
			
			Local distance_x:Double = Abs(balls[x]._x -balls[y]._x);
			Local distance_y:Double = Abs(balls[x]._y -balls[y]._y);
			Local distance:Double  = Sqr(distance_x * distance_x + distance_y * distance_y);
			
			If (distance<=30 And (balls[x].collision = 0 Or balls[y].collision = 0)) Then
				balls[x].collision = 1;
				balls[y].collision = 1;
				
				'Fix Position (prevent clutching) 'Not needed anymore?
				'Local dx:Double = balls[x]._x - balls[y]._x;
				'Local dy:Double = balls[x]._y - balls[y]._y;
				'Local collisionision_angle:Double = ATan2(dy, dx);
				'balls[y]._x = balls[y]._x - Cos(collisionision_angle)*(30-distance);
				'balls[y]._y = balls[y]._y - Sin(collisionision_angle)*(30-distance);
				
				'Do Bounce
				Bounce(balls[x], balls[y]);
				
				'Boost Speed - Doesnt work well :(
				balls[y].xspeed = balls[y].xspeed * 1.1;
				
			Else If (distance>30) Then
				balls[x].collision = 0;
				balls[y].collision = 0;
			End If
		'Next
	Next
	
	SetColor 0,200,0
	DrawOval balls[0]._x -15, balls[0]._y -15, 30, 30
	SetColor 255,255,255
	DrawOval balls[0]._x -13, balls[0]._y -13, 26, 26
	
	SetColor 0,0,200
	DrawOval balls[1]._x -15, balls[1]._y -15, 30, 30
	SetColor 255,255,255
	DrawOval balls[1]._x -13, balls[1]._y -13, 26, 26
	
	For x=2 To number_of_balls -1
		SetColor 200,0,0
		DrawOval balls[x]._x -15, balls[x]._y -15, 30, 30
		SetColor 255,255,255
		DrawOval balls[x]._x -13, balls[x]._y -13, 26, 26
	Next
	
	Flip
	Cls
Until KeyHit(KEY_ESCAPE) 'ESC