Recent

Author Topic: Simple ball bounce  (Read 11416 times)

lazboy10

  • New member
  • *
  • Posts: 7
Simple ball bounce
« on: January 31, 2022, 05:39:08 pm »
Hi!
So my problem is that i have been trying to get a ball to hit another ball and then bounce in the correct angle.
i have working code for the actual collision part, but for the life of me i just cant get the other ball to bounce correctly in the right angle.

My math skills are terrible so im hoping someone could help me with some working code... what i need is the bounce code only, i don't need any velocity or optimized code. just the basic bounce part.

here is my collision code that works:
Code: Pascal  [Select][+][-]
  1. function TestCollision: boolean;
  2. var
  3.   CentreAX, CentreAY, CentreBX, CentreBY, vDx, vDy, fDist: Single;
  4. begin
  5.   {red ball}
  6.   CentreAX := (Balls[1].Position.X + 24) / 2;
  7.   CentreAY := (Balls[1].Position.Y + 24) / 2;
  8.   {white ball}
  9.   CentreBX := (Balls[0].Position.X + 24) / 2;
  10.   CentreBY := (Balls[0].Position.Y + 24) / 2;
  11.  
  12.   vDx := CentreBX - CentreAX;
  13.   vDy := CentreBY - CentreAY;
  14.  
  15.   fDist := Sqrt((vDx * vDx) + (vDy * vDy));
  16.  
  17.   if fDist<12 then
  18.     Result := True
  19.   else
  20.     Result := False;
  21. end;
  22.  

Mr.Madguy

  • Hero Member
  • *****
  • Posts: 844
Re: Simple ball bounce
« Reply #1 on: January 31, 2022, 06:16:43 pm »
It's simple conservation of momentum. In case of equal masses velocities are simply swapped and mirrored against plane, that is perpendicular to line between two ball centers. Collision point also lies in a middle of that line. Hit detection - is simple distance measurement between centers. If distance <= 2 radius, then it's hit.

Looks like simple googling gives proper result.
« Last Edit: January 31, 2022, 06:40:28 pm by Mr.Madguy »
Is it healthy for project not to have regular stable releases?
Just for fun: Code::Blocks, GCC 13 and DOS - is it possible?

BobDog

  • Sr. Member
  • ****
  • Posts: 394
Re: Simple ball bounce
« Reply #2 on: February 01, 2022, 01:23:29 am »

You can look at this.
(simple console graphics only)
Tested win 10, 32 and 64 bit fp.
Code: Pascal  [Select][+][-]
  1.  
  2.  
  3.  
  4. program balls;
  5.  
  6. uses windows;
  7.    const
  8.     DC_BRUSH=18;
  9.     const
  10.     DC_PEN=19;
  11.    
  12.     Type V2=object
  13.      x,y,dx,dy:single;
  14.     radius:integer;
  15.      c:longword;
  16.      End;
  17.  
  18. type aov=array[1..4] of v2;
  19.  
  20.     function SetDCBrushColor(p:hdc;colour:COLORREF): COLORREF; stdcall external 'gdi32.dll' name 'SetDCBrushColor';
  21.     function SetDCPenColor(p:hdc;colour:COLORREF): COLORREF; stdcall external 'gdi32.dll' name 'SetDCPenColor';
  22.    
  23.    
  24.    function rgb(r:word;g:word;b:word) :longword;
  25.    begin
  26.    exit(((b Shl 16) Or ((g) Shl 8) Or (r) Or $FF000000)- $FF000000)
  27.    end;
  28.  
  29.  
  30.  procedure hidecursor();
  31.  var
  32.     consoleHandle:handle;
  33.      info:CONSOLE_CURSOR_INFO ;
  34.      begin
  35.      consolehandle := GetStdHandle(STD_OUTPUT_HANDLE);
  36.      info.dwSize := 100;
  37.      info.bVisible := FALSE ;
  38.      SetConsoleCursorInfo(consoleHandle, @info);
  39.      End;
  40.      
  41.      procedure MoveAndDraw(wh:hdc;var b:aov);
  42.      var lx,ly:single;i,r,j:integer;
  43.      begin
  44.      for i:=low(b) to high(b) do
  45.      begin
  46.      lx:=b[i].x;ly:=b[i].y;
  47.      r:=b[i].radius;
  48.      SetDCBrushColor(wh,rgb(0,0,0));
  49.      SetDCPenColor(wh,rgb(0,0,0));
  50.      ellipse(wh,trunc(lx-r),trunc(ly-r),trunc(lx+r),trunc(ly+r));
  51.      b[i].x:=b[i].x+b[i].dx;
  52.      b[i].y:=b[i].y+b[i].dy;
  53.      SetDCBrushColor(wh,b[i].c);
  54.      SetDCPenColor(wh,b[i].c);
  55.      for j:=0 to 5 do
  56.      ellipse(wh,trunc(b[i].x-r),trunc(b[i].y-r),trunc(b[i].x+r),trunc(b[i].y+r));
  57.      end;
  58.      end;
  59.      
  60.      procedure HandleEdges(var b:aov);
  61.      var i,r:integer;
  62.      begin
  63.      for i:=low(b) to high(b) do
  64.      begin
  65.      r:=b[i].radius;
  66.      if (b[i].x<r) then begin b[i].x:=r;b[i].dx:=-b[i].dx; end;
  67.      if (b[i].x>(800-r)) then begin b[i].x:=800-r;b[i].dx:=-b[i].dx; end;
  68.      
  69.      if (b[i].y<r) then begin b[i].y:=r;b[i].dy:=-b[i].dy; end;
  70.      if (b[i].y>(600-r)) then begin b[i].y:=600-r;b[i].dy:=-b[i].dy; end
  71.      end;
  72.      
  73.      end;
  74.      
  75.      
  76.  function HandleBallCollisions(Var b:aov):boolean ;
  77. Var
  78.   L,impulsex,impulsey,dot,impactx,impacty: single;
  79.   ma,mb,f1,f2: single;
  80.   n1,n2: Integer;
  81.   flag:boolean=false;
  82. Begin
  83.   For n1 :=low(b) To high(b) -1 Do
  84.     Begin
  85.       For n2 :=n1+1 To high(b) Do
  86.         Begin
  87.           L := Sqrt( (b[n1].x-b[n2].x)*(b[n1].x-b[n2].x) + (b[n1].y-b[n2].y)*(b[n1].y-b[n2].y));
  88.           If L< (b[n1].radius+b[n2].radius) Then
  89.             Begin
  90.             flag:=true;
  91.               impulsex := (b[n1].x-b[n2].x)/L ;
  92.               impulsey := (b[n1].y-b[n2].y)/L ;
  93.               // in case of large overlap (non analogue motion)
  94.               b[n1].x := b[n2].x+(b[n1].radius+b[n2].radius)*impulsex ;
  95.               b[n1].y := b[n2].y+(b[n1].radius+b[n2].radius)*impulsey ;
  96.  
  97.               impactx := b[n1].dx-b[n2].dx ;
  98.               impacty := b[n1].dy-b[n2].dy ;
  99.               dot := impactx*impulsex+impacty*impulsey ;
  100.               ma := b[n1].radius;
  101.               mb := b[n2].radius;
  102.               ma := ma*ma;   // weigh by area (radius squared)
  103.               mb := mb*mb;
  104.               f1 := 2*mb/(ma+mb);   // ball weight factors
  105.               f2 := 2*ma/(ma+mb);
  106.               b[n1].dx:= b[n1].dx-dot*impulsex *f1;
  107.               b[n1].dy:= b[n1].dy-dot*impulsey *f1;
  108.               b[n2].dx:=b[n2].dx+ dot*impulsex *f2 ;
  109.               b[n2].dy:= b[n2].dy+dot*impulsey *f2 ;
  110.             End;
  111.         End; // n2
  112.     End;  // n1
  113.     exit(flag);
  114. End;
  115.      
  116.  
  117.  
  118. Var
  119.   p:hwnd;
  120.   wh:hdc;
  121.  b:aov;
  122.  
  123. begin
  124. b[1].x:=100;
  125. b[1].y:=200;
  126. b[1].dx:=0.06;
  127. b[1].dy:=0.04;
  128. b[1].radius:=45;
  129. b[1].c:=rgb(255,100,0);
  130.  
  131. b[2].x:=500;
  132. b[2].y:=300;
  133. b[2].dx:=-0.06;
  134. b[2].dy:=-0.08;
  135. b[2].radius:=40;
  136. b[2].c:=rgb(0,255,0);
  137.  
  138. b[3].x:=200;
  139. b[3].y:=500;
  140. b[3].dx:=0.06;
  141. b[3].dy:=-0.07;
  142. b[3].radius:=35;
  143. b[3].c:=rgb(0,0,255);
  144.  
  145. b[4].x:=300;
  146. b[4].y:=400;
  147. b[4].dx:=-0.06;
  148. b[4].dy:=0.07;
  149. b[4].radius:=30;
  150. b[4].c:=rgb(0,100,255);
  151.  
  152.  
  153. p := GetConsoleWindow();
  154. setwindowpos(p, HWND_TOPMOST, 100, 100, 810, 630,SWP_SHOWWINDOW);
  155.     wh:=GetDC(p);
  156.     SelectObject(wh,GetStockObject(DC_BRUSH));
  157.     SelectObject(wh,GetStockObject(DC_PEN));
  158.      hidecursor;
  159.      ShowScrollBar(p, SB_BOTH, FALSE);
  160.     while true do
  161.     begin
  162. MoveAndDraw(wh,b);
  163. HandleEdges(b);
  164. if HandleBallCollisions(b) then //clearscreen- optional
  165. begin
  166.     SetDCBrushColor(wh,rgb(0,0,0));
  167.     SetDCPenColor(wh,rgb(0,0,0));
  168.     rectangle(wh,0,0,810,630);
  169. end;
  170.  
  171.     end;
  172.  
  173. end.
  174.  
  175.  

speter

  • Sr. Member
  • ****
  • Posts: 349
Re: Simple ball bounce
« Reply #3 on: February 01, 2022, 08:30:56 am »
In case anyone is interested, here is a GUI version of BobDog's code.

cheers
S.
I climbed mighty mountains, and saw that they were actually tiny foothills. :)

loaded

  • Hero Member
  • *****
  • Posts: 825
Re: Simple ball bounce
« Reply #4 on: February 01, 2022, 09:37:05 am »
In case anyone is interested, here is a GUI version of BobDog's code.
I am one of those who are interested.
Many thanks for the code, BobDog  and speter
« Last Edit: February 01, 2022, 09:38:37 am by loaded »
Check out  loaded on Strava
https://www.strava.com/athletes/109391137

440bx

  • Hero Member
  • *****
  • Posts: 4014
Re: Simple ball bounce
« Reply #5 on: February 01, 2022, 11:00:15 am »
In case anyone is interested, here is a GUI version of BobDog's code.

cheers
S.
Nice, thank you.

There are a couple of things that you might be interested in knowing. 

Attempting to enlarge the window while the balls are bouncing fails and, after the attempt, for some reason, it seems impossible to switch to another app. the only way I found to switch to another app is to use Alt-Tab (on Windows)

The other thing is, the app won't close after clicking on the "X" or selecting close from the system menu.  Looks like processing some events isn't happening.  I suspect this is likely caused by having the GUI thread be responsible for painting the balls in addition to processing events.

Last thing, the larger the window is, the slower the balls move. It would be nice if the speed was related to the size of the window.

I'm sorry but, when I watch paint dry I insist on good paint... if I'm going to watch balls bouncing, they have to bouncing right... otherwise might as well watch TV. ;)


(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v3.2) on Windows 7 SP1 64bit.

speter

  • Sr. Member
  • ****
  • Posts: 349
Re: Simple ball bounce
« Reply #6 on: February 01, 2022, 12:36:03 pm »
Attempting to enlarge the window while the balls are bouncing fails and, after the attempt, for some reason, it seems impossible to switch to another app. the only way I found to switch to another app is to use Alt-Tab (on Windows)
I didn't have either of these problems (win11; Laz 2.2).

Quote
The other thing is, the app won't close after clicking on the "X" or selecting close from the system menu.  Looks like processing some events isn't happening.  I suspect this is likely caused by having the GUI thread be responsible for painting the balls in addition to processing events.
I did notice this problem. I've added an event handler for form-close, which fixes it for me...

Quote
Last thing, the larger the window is, the slower the balls move. It would be nice if the speed was related to the size of the window.
I am not totally sure what you mean here! BUT, I did notice the balls were quite slow moving!
I've tweaked the code, which makes it "much" faster - though the balls still bounce at a leisurely pace  :P on my laptop.

Another problem I saw (and fixed) was the program seemed tio be drawing the old circle in black to remove it (I was referring to Paintbox1.canvas.color when I should have used Paintbox1.color).

Quote
I'm sorry but, when I watch paint dry I insist on good paint... if I'm going to watch balls bouncing, they have to bouncing right... otherwise might as well watch TV. ;)
I am using the very best pixels available. :o  :D

cheers
S.
PS: I've attached an updated project.
I climbed mighty mountains, and saw that they were actually tiny foothills. :)

440bx

  • Hero Member
  • *****
  • Posts: 4014
Re: Simple ball bounce
« Reply #7 on: February 01, 2022, 01:39:07 pm »
There is a definite improvement in the new version.

I still experience the first problem I mentioned.  If I attempt to enlarge the window while the balls are bouncing around then, the only thing that gets the interface back to normal is to Alt-tab.

I am not totally sure what you mean here! BUT, I did notice the balls were quite slow moving!
I've tweaked the code, which makes it "much" faster - though the balls still bounce at a leisurely pace  :P on my laptop.
you can see the difference in speed by doing these two steps.  First step, start the application just as you posted it and notice the speed at which the balls move.  Second step, make the window/form about twice as large in both directions, horizontal and vertical, recompile and run again.  You'll notice that in the larger window the balls  move at a fraction of the speed they did in the smaller (original size) window/form.


PS: I've attached an updated project.
I attached the updated project to my CPU :)
(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v3.2) on Windows 7 SP1 64bit.

Josh

  • Hero Member
  • *****
  • Posts: 1273
Re: Simple ball bounce
« Reply #8 on: February 01, 2022, 02:54:33 pm »
Hi

Had a play and made some little changes,
draws on timage, using buffered bmp for speed,
increased to 50 balls, altered so balls move at a quicker pace, attempt to get 50fps inside loop by calculating time to draw and adjusting params, this stops the cpu high load hopoefully, fixed close issue.
The best way to get accurate information on the forum is to post something wrong and wait for corrections.

Josh

  • Hero Member
  • *****
  • Posts: 1273
Re: Simple ball bounce
« Reply #9 on: February 01, 2022, 04:44:38 pm »
Added background color change
« Last Edit: February 01, 2022, 04:58:58 pm by josh »
The best way to get accurate information on the forum is to post something wrong and wait for corrections.

BobDog

  • Sr. Member
  • ****
  • Posts: 394
Re: Simple ball bounce
« Reply #10 on: February 01, 2022, 05:49:31 pm »

Some nice examples.
Here is a windows gdi fixed screen version.
Only the top left ball is given an initial speed, this is soon transferred to the others.
Code: Pascal  [Select][+][-]
  1.  
  2. program balls;
  3.  
  4. uses windows;
  5.     const
  6.     DC_BRUSH=18;
  7.     DC_PEN=19;
  8.    
  9.     Type V2=object
  10.      x,y,dx,dy:single;
  11.      radius:integer;
  12.      c:longword;
  13.      procedure SetF(xx,yy,ddx,ddy:single;rradius:integer;cc:longword);
  14.      End;
  15.      
  16.     procedure V2.SetF(xx,yy,ddx,ddy:single;rradius:integer;cc:longword);
  17.     begin
  18.     x:=xx;y:=yy;dx:=ddx;dy:=ddy;radius:=rradius;c:=cc;
  19.     end;  
  20.  
  21. type aov=array[1..5] of v2;
  22.  
  23.     function SetDCBrushColor(p:hdc;colour:COLORREF): COLORREF; stdcall external 'gdi32.dll' name 'SetDCBrushColor';
  24.     function SetDCPenColor(p:hdc;colour:COLORREF): COLORREF; stdcall external 'gdi32.dll' name 'SetDCPenColor';
  25.     Function settimer(n:integer=1):integer;stdcall external 'winmm.dll' name 'timeBeginPeriod';
  26.     Function freetimer(n:integer=1):integer;stdcall external 'winmm.dll' name 'timeEndPeriod';
  27.    
  28.    
  29.    procedure ClearScreen(h:hdc);
  30.    var
  31.    colour:longword;
  32.    begin
  33.     colour:=rgb(100,100,100);
  34.     SetDCBrushColor(h,colour);
  35.     SetDCPenColor(h,colour);
  36.     rectangle(h,0,0,810,630);
  37.    end;
  38.  
  39.      procedure MoveAndDraw(wh:hdc;var b:aov);
  40.      var i,r:integer;
  41.      begin
  42.      for i:=low(b) to high(b) do
  43.      begin
  44.      r:=b[i].radius;
  45.      b[i].x:=b[i].x+b[i].dx;
  46.      b[i].y:=b[i].y+b[i].dy;
  47.      SetDCBrushColor(wh,b[i].c);
  48.      SetDCPenColor(wh,b[i].c);
  49.      ellipse(wh,trunc(b[i].x-r),trunc(b[i].y-r),trunc(b[i].x+r),trunc(b[i].y+r));
  50.      end;
  51.      end;
  52.      
  53.      procedure HandleEdges(var b:aov);
  54.      var i,r:integer;
  55.      begin
  56.      for i:=low(b) to high(b) do
  57.      begin
  58.      r:=b[i].radius;
  59.      if (b[i].x<r) then begin b[i].x:=r;b[i].dx:=-b[i].dx; end;
  60.      if (b[i].x>(800-r)) then begin b[i].x:=800-r;b[i].dx:=-b[i].dx; end;
  61.      
  62.      if (b[i].y<r) then begin b[i].y:=r;b[i].dy:=-b[i].dy; end;
  63.      if (b[i].y>(600-r)) then begin b[i].y:=600-r;b[i].dy:=-b[i].dy; end
  64.      end;
  65.      
  66.      end;
  67.      
  68.      
  69.  function HandleBallCollisions(Var b:aov):boolean ;
  70. Var
  71.   L,impulsex,impulsey,dot,impactx,impacty: single;
  72.   ma,mb,f1,f2: single;
  73.   n1,n2: Integer;
  74.   flag:boolean=false;
  75. Begin
  76.   For n1 :=low(b) To high(b) -1 Do
  77.     Begin
  78.       For n2 :=n1+1 To high(b) Do
  79.         Begin
  80.           L := Sqrt( (b[n1].x-b[n2].x)*(b[n1].x-b[n2].x) + (b[n1].y-b[n2].y)*(b[n1].y-b[n2].y));
  81.           If L< (b[n1].radius+b[n2].radius) Then
  82.             Begin
  83.             flag:=true;
  84.               impulsex := (b[n1].x-b[n2].x)/L ;
  85.               impulsey := (b[n1].y-b[n2].y)/L ;
  86.               // in case of large overlap (non analogue motion)
  87.               b[n1].x := b[n2].x+(b[n1].radius+b[n2].radius)*impulsex ;
  88.               b[n1].y := b[n2].y+(b[n1].radius+b[n2].radius)*impulsey ;
  89.  
  90.               impactx := b[n1].dx-b[n2].dx ;
  91.               impacty := b[n1].dy-b[n2].dy ;
  92.               dot := impactx*impulsex+impacty*impulsey ;
  93.               ma := b[n1].radius;
  94.               mb := b[n2].radius;
  95.               ma := ma*ma;   // weigh by area (radius squared)
  96.               mb := mb*mb;
  97.               f1 := 2*mb/(ma+mb);   // ball weight factors
  98.               f2 := 2*ma/(ma+mb);
  99.               b[n1].dx:= b[n1].dx-dot*impulsex *f1;
  100.               b[n1].dy:= b[n1].dy-dot*impulsey *f1;
  101.               b[n2].dx:=b[n2].dx+ dot*impulsex *f2 ;
  102.               b[n2].dy:= b[n2].dy+dot*impulsey *f2 ;
  103.             End;
  104.         End; // n2
  105.     End;  // n1
  106.     exit(flag);
  107. End;
  108.      
  109.  
  110. Var
  111.   p:hwnd;
  112.   wh:hdc;
  113.   b:aov;
  114.   emsg:msg;
  115.  
  116. begin
  117. b[1].setf(100,100,1.75,1.75,35,rgb(255,100,0));
  118. b[2].setf(300,300,0,0,35,rgb(0,255,0));
  119. b[3].setf(400,400,0,0,40,rgb(0,0,255));
  120. b[4].setf(500,500,0,0,30,rgb(0,100,255));
  121. b[5].setf(200,200,0,0,20,rgb(200,100,255));
  122.  
  123.      p:=CreateWindowEx( WS_EX_TOPMOST Or WS_EX_TOOLWINDOW,'#32770','Press ESCAPE key to finish . . .',(WS_OVERLAPPEDWINDOW Or WS_SYSMENU) - (WS_MINIMIZEBOX Or WS_MAXIMIZEBOX Or WS_THICKFRAME) Or WS_VISIBLE,200,200,810,630,0,0,0,nil);
  124.      wh:=GetDC(p);
  125.     SelectObject(wh,GetStockObject(DC_BRUSH));
  126.     SelectObject(wh,GetStockObject(DC_PEN));
  127.     ClearScreen(wh);
  128.     freeconsole();
  129.     while true do
  130.     begin
  131. while(int64(PeekMessage(@eMsg,0, 0, 0, PM_REMOVE)) > 0) do
  132.     begin
  133.      TranslateMessage (@eMsg);
  134.      DispatchMessage (@eMsg);
  135.     if boolean(GetAsyncKeyState($1B)) then exit;
  136.      end;
  137.    
  138. ClearScreen(wh);
  139. MoveAndDraw(wh,b);
  140. HandleEdges(b);
  141. HandleBallCollisions(b);
  142.  
  143. settimer();
  144. sleep(7);
  145. freetimer();
  146.     end;
  147.  
  148. end.
  149.  
  150.  

Tested win 10 fpc 32/64 bits.
Coded with Geany ide.

440bx

  • Hero Member
  • *****
  • Posts: 4014
Re: Simple ball bounce
« Reply #11 on: February 01, 2022, 09:34:00 pm »
@BobDog,

Interesting little bit of code you posted.  Increasing the timer resolution greatly diminishes the flicker caused by erasing and repainting the screen - nice trick. :)
(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v3.2) on Windows 7 SP1 64bit.

furious programming

  • Hero Member
  • *****
  • Posts: 858
Re: Simple ball bounce
« Reply #12 on: February 02, 2022, 12:17:38 am »
The flickering of the window contents is caused by clearing its area. Why clean the window in every frame, if it will be completely painted over in a moment? The easiest way to avoid flickering is to render everything on the back buffer (e.g. on a regular bitmap), which will be rendered in a window or component at each frame. Thanks to this, not only the rendering itself will be faster, but also the refresh rate will not matter.
« Last Edit: February 02, 2022, 12:19:12 am by furious programming »
Lazarus 3.2 with FPC 3.2.2, Windows 10 — all 64-bit

Working solo on an acrade, action/adventure game in retro style (pixelart), programming the engine and shell from scratch, using Free Pascal and SDL. Release planned in 2026.

Josh

  • Hero Member
  • *****
  • Posts: 1273
Re: Simple ball bounce
« Reply #13 on: February 02, 2022, 12:22:50 am »
balls and asteroids ....
The best way to get accurate information on the forum is to post something wrong and wait for corrections.

440bx

  • Hero Member
  • *****
  • Posts: 4014
Re: Simple ball bounce
« Reply #14 on: February 02, 2022, 01:59:51 am »
The flickering of the window contents is caused by clearing its area. Why clean the window in every frame, if it will be completely painted over in a moment? The easiest way to avoid flickering is to render everything on the back buffer (e.g. on a regular bitmap), which will be rendered in a window or component at each frame. Thanks to this, not only the rendering itself will be faster, but also the refresh rate will not matter.
You're absolutely right but, I am really surprised to see how much difference increasing the timer resolution has on flicker.  In BobDogs implementation, even though he clears the window before every repaint, which would normally cause a lot of flicker, the increase in timer resolution _almost_ completely eliminates flicker.  Honestly, I'm not sure why it makes such a big difference but, as they say, seeing is believing.
(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v3.2) on Windows 7 SP1 64bit.

 

TinyPortal © 2005-2018