This is an expanded version of the bigballdemo posted in the filled circle thread (Here) that uses vector reflection to bounce balls off of each other in a realistic way. This is something I am in the process of learning.
This v2 demo adds a 2nd big ball with an extra layer of bouncing balls. So there balls inside a bigger ball, which is inside an even bigger ball, and there's a layer of bouncing balls between those two big balls. On the outside is another layer of balls bouncing around. I made 3 SUBs to handle the 3 kinds of collision, Ball vs Ball, Ball vs Inside of Larger ball edge (when the balls are on the inside), and Ball vs Outside of a larger ball (when balls are on the outside).
Move the middle big ball with the mouse, it will move all the others and they will react to each other.
- Dav
This v2 demo adds a 2nd big ball with an extra layer of bouncing balls. So there balls inside a bigger ball, which is inside an even bigger ball, and there's a layer of bouncing balls between those two big balls. On the outside is another layer of balls bouncing around. I made 3 SUBs to handle the 3 kinds of collision, Ball vs Ball, Ball vs Inside of Larger ball edge (when the balls are on the inside), and Ball vs Outside of a larger ball (when balls are on the outside).
Move the middle big ball with the mouse, it will move all the others and they will react to each other.
- Dav
Code: (Select All)
'================
'BIGBALLDEMO2.BAS
'================
'Bigger Bouncing balls demo using vector reflection.
'By Dav, SEP 16th/2024, for QB64 Phoenix Edition.
'===============
'About this demo
'===============
'This demo shows balls bouncing inside a bigger ball, which is inside
'a bigger outer ball. Between the big balls are more bouncing balls.
'There are more balls bouncing on the outside, so there are 3 layers
'of bouncing balls. Use the mouse to drag the big middle ball, which
'will move the outer ball. You will see that all the balls collide
'and reflect off each other. It uses the FC SUB to draw all the balls.
'============================
'More details about this demo
'============================
'This demo was a challenge and a great learning experience for me.
'Instead of just reversing velocity direction when a ball hits an object,
'this demo uses 'vector reflection' to make them bounce realistically.
'When two balls collide, their velocity vector changes direction based on
'angle of impact, and the normal vector at the contact point. Their
'reflection velocities are computed based on their sizes, and their x/y
'positions are adjusted to prevent overlapping after collision.
'There are three collision SUB used in this program. One to handle
'two balls colliding, one to bounce a ball off the inside of a larger
'ball, and one to bouce off the outside of a larger ball.
Randomize Timer
Screen _NewImage(1000, 700, 32)
'=== defaults for the bigball ===
bigballsize = 190
bigballx = _Width / 2
bigbally = _Height / 2
'=== defaults for the outer ball ===
outerballsize = 300 ' size of the outer ball
outerballx = _Width / 2
outerbally = _Height / 2
'=== arrays for inside balls ===
insidenum = 45 'num of inside balls
Dim insidex(insidenum) 'x positions of inside balls
Dim insidey(insidenum) 'ypositions of inside balls
Dim insidexv(insidenum) 'x velocities of inside balls
Dim insideyv(insidenum) 'y velocities of inside balls
Dim insidesize(insidenum) 'sizes of inside balls
Dim insideclr~&(insidenum) 'colors of inside balls
'=== arrays for middle balls ===
middlenum = 125 'num of inside balls
Dim middlex(middlenum) 'x positions of inside balls
Dim middley(middlenum) 'ypositions of inside balls
Dim middlexv(middlenum) 'x velocities of inside balls
Dim middleyv(middlenum) 'y velocities of inside balls
Dim middlesize(middlenum) 'sizes of inside balls
Dim middleclr~&(middlenum) 'colors of inside balls
'=== arrays for outside balls ===
outsidenum = 125 'num of outside balls
Dim outsidex(outsidenum) 'x positions of outside balls
Dim outsidey(outsidenum) 'y positions of outside balls
Dim outsidexv(outsidenum) 'x velocities of outside balls
Dim outsideyv(outsidenum) 'y velocities of outside balls
Dim outsidesizes(outsidenum) 'sizes of outside balls
Dim outsideclr~&(outsidenum) 'colors of outside balls
'=== initialize inside balls ===
For i = 0 To insidenum - 1
insidesize(i) = 5 + (Rnd * 15) 'random size
insideclr~&(i) = _RGBA(Rnd * 255, Rnd * 255, Rnd * 255, 200) 'color
insidexv(i) = (Rnd * 2 + 1) * (2 * Rnd - 1) 'x velocity between -3 and 3
insideyv(i) = (Rnd * 2 + 1) * (2 * Rnd - 1) 'y velocity between -3 and 3
Next
'=== initialize middle balls ===
For i = 0 To middlenum - 1
middlex(i) = Rnd * (outerballsize - 50) + outerballx 'x position of middle ball
middley(i) = Rnd * (outerballsize - 50) + outerbally 'y x position of middle ball
middlexv(i) = (Rnd * 2 + 1) * (2 * Rnd - 1) 'x velocity between -3 and 3
middleyv(i) = (Rnd * 2 + 1) * (2 * Rnd - 1) 'y velocity between -3 and 3
middlesize(i) = 5 + (Rnd * 10) 'random size
middleclr~&(i) = _RGBA(Rnd * 200, Rnd * 100, Rnd * 255, 170) 'color
Next
'=== initialize outside Balls ===
For j = 0 To outsidenum - 1
outsidesizes(j) = Int(Rnd * 26) + 5 'random size
outsideclr~&(j) = _RGBA(Rnd * 225, Rnd * 225, Rnd * 225, 125) 'color
outsidex(j) = Int(Rnd * _Width) 'x position
outsidey(j) = Int(Rnd * _Height) 'y position
outsidexv(j) = (Rnd * 2 + 1) * (2 * Rnd - 1) 'x velocity between -3 and 3
outsideyv(j) = (Rnd * 2 + 1) * (2 * Rnd - 1) 'y velocity between -3 and 3
Next
'=== draw a background image ===
For i = 1 To 1000
fc Rnd * _Width, Rnd * _Height, 20, _RGBA(55 + (Rnd * 100), 55 + (Rnd * 150), 55 + (Rnd * 200), 30), 0
Next: back& = _CopyImage(_Display)
'=== put mouse in middle of screen ===
_MouseMove _Width / 2, _Height / 2
'=========
'MAIN LOOP
'=========
Do
'=== put down background image ===
Cls: _PutImage (0, 0), back&
'=== get mouse input ===
While _MouseInput: Wend
'=== assign bigball x/y to mouse x/y ===
bigballx = _MouseX: bigbally = _MouseY
'=== handle inside balls ===
For i = 0 To insidenum - 1
'== move inside balls ==
insidex(i) = insidex(i) + insidexv(i)
insidey(i) = insidey(i) + insideyv(i)
'=== check collision with the big ball ===
Ball2BallInsideEdgeCollision insidex(i), insidey(i), insidesize(i), insidexv(i), insideyv(i), bigballx, bigbally, bigballsize
'=== finally draw insideball ===
fc insidex(i), insidey(i), insidesize(i), insideclr~&(i), 1
Next
'=== handle collisions of insideballs ===
For i = 0 To insidenum - 1
For j = i + 1 To insidenum - 1
If i <> j Then
Ball2BallCollision insidex(i), insidey(i), insidesize(i), insidexv(i), insideyv(i), insidex(j), insidey(j), insidesize(j), insidexv(j), insideyv(j)
End If
Next
Next
'=== handle middle balls ===
For i = 0 To middlenum - 1
'== move middle balls ==
middlex(i) = middlex(i) + middlexv(i)
middley(i) = middley(i) + middleyv(i)
'=== Check for boundary with the outer ball ===
Ball2BallInsideEdgeCollision middlex(i), middley(i), middlesize(i), middlexv(i), middleyv(i), outerballx, outerbally, outerballsize
'=== Check middleball collision with the bigball ===
Ball2BallOutsideEdgeCollision middlex(i), middley(i), middlesize(i), middlexv(i), middleyv(i), bigballx, bigbally, bigballsize
'=== finally draw middle ball ===
fc middlex(i), middley(i), middlesize(i), middleclr~&(i), 1
Next
'=== Handle middleball collisions with each other ===
For i = 0 To middlenum - 1
For j = i + 1 To middlenum - 1
If i <> j Then
Ball2BallCollision middlex(i), middley(i), middlesize(i), middlexv(i), middleyv(i), middlex(j), middley(j), middlesize(j), middlexv(j), middleyv(j)
End If
Next
Next
'=== handle Outside balls ===
For j = 0 To outsidenum - 1
'draw outside ball
fc outsidex(j), outsidey(j), outsidesizes(j), outsideclr~&(j), 1
outsidex(j) = outsidex(j) + outsidexv(j)
outsidey(j) = outsidey(j) + outsideyv(j)
'=== bounce outside balls off the screen edges
If outsidex(j) < outsidesizes(j) Then
outsidex(j) = outsidesizes(j): outsidexv(j) = -outsidexv(j)
End If
If outsidex(j) > _Width - outsidesizes(j) Then
outsidex(j) = _Width - outsidesizes(j): outsidexv(j) = -outsidexv(j)
End If
If outsidey(j) < outsidesizes(j) Then
outsidey(j) = outsidesizes(j): outsideyv(j) = -outsideyv(j)
End If
If outsidey(j) > _Height - outsidesizes(j) Then
outsidey(j) = _Height - outsidesizes(j): outsideyv(j) = -outsideyv(j)
End If
'==== check for outside ball collision with outer ball ====
Ball2BallOutsideEdgeCollision outsidex(j), outsidey(j), outsidesizes(j), outsidexv(j), outsideyv(j), outerballx, outerbally, outerballsize
Next
'=== handle collisions between outside balls ===
For i = 0 To outsidenum - 1
For j = i + 1 To outsidenum - 1
If i <> j Then
Ball2BallCollision outsidex(i), outsidey(i), outsidesizes(i), outsidexv(i), outsideyv(i), outsidex(j), outsidey(j), outsidesizes(j), outsidexv(j), outsideyv(j)
End If
Next
Next
'=== check for collision between the bigball and the outer ball ===
'==== this keeps the bigball and outerball at the edge of each other ===
dis = Sqr((bigballx - outerballx) ^ 2 + (bigbally - outerbally) ^ 2)
If dis > (bigballsize / 2) Then
'calculate direction from bigball to outerball
angle = _Atan2(outerbally - bigbally, outerballx - bigballx)
'make distance from center of bigball to edge of outerball
newdis = outerballsize + (bigballsize / 2)
'move the outer ball to exactly touch the big ball's edge
outerballx = bigballx + Cos(angle) * (newdis - outerballsize)
outerbally = bigbally + Sin(angle) * (newdis - outerballsize)
End If
'=== draw the outer ball ===
fc outerballx, outerbally, outerballsize, _RGBA(200, 100, 255, 50), 0
'draw an edge around it
Circle (outerballx, outerbally), outerballsize, _RGBA(255, 255, 255, 75)
'=== draw the bigball ===
fc bigballx, bigbally, bigballsize, _RGBA(100, 200, 255, 75), 0
'draw an edge around it
Circle (bigballx, bigbally), bigballsize, _RGBA(255, 255, 255, 75)
_Display
_Limit 60
Loop Until InKey$ <> ""
Sub fc (cx, cy, radius, clr~&, grad)
'FC SUB by Dav
'Draws filled circle at cx/cy with given radius and color.
'If grad=1 it will create a gradient effect, otherwise it's a solid color.
If radius < 1 Then Exit Sub 'a safety bail (thanks bplus!)
If grad = 1 Then
red = _Red32(clr~&)
grn = _Green32(clr~&)
blu = _Blue32(clr~&)
alpha = _Alpha32(clr~&)
End If
r2 = radius * radius
For y = -radius To radius
x = Sqr(r2 - y * y)
' If doing gradient
If grad = 1 Then
For i = -x To x
dis = Sqr(i * i + y * y) / radius
red2 = red * (1 - dis) + (red / 2) * dis
grn2 = grn * (1 - dis) + (grn / 2) * dis
blu2 = blu * (1 - dis) + (blu / 2) * dis
clr2~& = _RGBA(red2, grn2, blu2, alpha)
Line (cx + i, cy + y)-(cx + i, cy + y), clr2~&, BF
Next
Else
Line (cx - x, cy + y)-(cx + x, cy + y), clr~&, BF
End If
Next
End Sub
Sub Ball2BallCollision (Ball1x, Ball1y, Ball1s, Ball1xv, Ball1yv, Ball2x, Ball2y, Ball2s, Ball2xv, Ball2yv)
'This SUB handles ball to ball collision
'calculate distance between the two balls
dx = Ball2x - Ball1x
dy = Ball2y - Ball1y
dis = Sqr(dx * dx + dy * dy)
'check for collision, if so...
If dis < (Ball1s + Ball2s) Then
'calculate normal vector and overlapping distance
x = dx / dis: y = dy / dis 'normal
over = (Ball1s + Ball2s) - dis 'overlap distance
'move balls apart based on overlap amount
Ball1x = Ball1x - x * (over / 2)
Ball1y = Ball1y - y * (over / 2)
Ball2x = Ball2x + x * (over / 2)
Ball2y = Ball2y + y * (over / 2)
'reflect velocities based on collision
vr = (Ball2xv - Ball1xv) * x + (Ball2yv - Ball1yv) * y
'update ball velocities based on collision
Ball1xv = Ball1xv + vr * x: Ball1yv = Ball1yv + vr * y
Ball2xv = Ball2xv - vr * x: Ball2yv = Ball2yv - vr * y
End If
End Sub
Sub Ball2BallInsideEdgeCollision (Ball1x, Ball1y, Ball1s, Ball1xv, Ball1yv, BallEdgex, BallEdgey, BallEdgeSize)
'This sub handles balls colliding with the inside edge of a larger ball
dis = Sqr((Ball1x - BallEdgex) ^ 2 + (Ball1y - BallEdgey) ^ 2)
'check if iball collides with ball edge
If dis + Ball1s > BallEdgeSize Then
'calculate normal vector for reflection
x = (Ball1x - BallEdgex) / dis
y = (Ball1y - BallEdgey) / dis
'calculate the reflection of velocity based impact angle
vr = Ball1xv * x + Ball1yv * y
'update velocity of inside ball based on the normal
Ball1xv = Ball1xv - 2 * vr * x
Ball1yv = Ball1yv - 2 * vr * y
'push back to prevent overlapping
over = (dis + Ball1s) - BallEdgeSize
Ball1x = Ball1x - x * over
Ball1y = Ball1y - y * over
End If
End Sub
Sub Ball2BallOutsideEdgeCollision (Ball1x, Ball1y, Ball1s, Ball1xv, Ball1yv, BallEdgex, BallEdgey, BallEdgeSize)
'This sub handles balls colliding with the outside edge of a larger ball
dis = Sqr((Ball1x - BallEdgex) ^ 2 + (Ball1y - BallEdgey) ^ 2)
If dis < BallEdgeSize + Ball1s Then
'calculate normal vector for reflection
x = (Ball1x - BallEdgex) / dis
y = (Ball1y - BallEdgey) / dis
'reflect velocity based on normal vector
vr = Ball1xv * x + Ball1yv * y
Ball1xv = Ball1xv - 2 * vr * x
Ball1yv = Ball1yv - 2 * vr * y
'move the middle ball out to prevent overlap
over = (BallEdgeSize + Ball1s) - dis
Ball1x = Ball1x + x * over
Ball1y = Ball1y + y * over
End If
End Sub