Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Another small filled circe sub (not as fast as fcirc)
Well, it looks like this thread is finally winding down.  Thanks for all the fun action, everyone!  I'll will post my final FC SUB and last ball demo here using it.   This had to be the most fun and challenging ball demo I've worked on.  Bouncing balls inside a big ball, moving on the screen.  It was a great learning experience, and I tried to fully comment the code.  Hope you like it.  FC SUB is posted at the bottom.

- Dav

Code: (Select All)
'===============
'BIGBALLDEMO.BAS
'===============
'Bouncing balls demo using vector reflection.
'By Dav, SEP 3rd/2024, for QB64 Phoenix Edition.

'===============
'About this demo
'===============

'This demo shows balls bouncing inside a bigger ball, and other balls
'bouncing on the outside.  It uses my FC SUB to draw all the balls.
'Use the mouse to move the big ball.  Press Any key to exit the demo.

'============================
'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.

Randomize Timer

Screen _NewImage(1000, 700, 32)

'=== defaults for the bigball ===
bigballsize = 200
bigballx = _Width / 2
bigbally = _Height / 2

'=== arrays for inside balls ===
insidenum = 50 '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 outside balls ===
outsidenum = 150 '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 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 if they collide with bigball edge ===
        'calculate distance from the center x/y of bigball
        dis = Sqr((insidex(i) - bigballx) ^ 2 + (insidey(i) - bigbally) ^ 2)

        'check if distance + insideball size exceeds bigball size
        If dis + insidesize(i) > bigballsize Then
            'calculate normal vector for reflection
            x = (insidex(i) - bigballx) / dis
            y = (insidey(i) - bigbally) / dis
            'calculate the reflection of velocity based impact angle
            vr = insidexv(i) * x + insideyv(i) * y
            'update velocity of insideball based on the normal
            insidexv(i) = insidexv(i) - 2 * vr * x
            insideyv(i) = insideyv(i) - 2 * vr * y
            'below prevents overlapping by pushing insideball back
            over = (dis + insidesize(i)) - bigballsize
            insidex(i) = insidex(i) - x * over
            insidey(i) = insidey(i) - y * over
        End If

        '=== 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
                'calculate distance between the two insideballs
                dx = insidex(j) - insidex(i)
                dy = insidey(j) - insidey(i)
                dis = Sqr(dx * dx + dy * dy)
                'check for collision, if so...
                If dis < (insidesize(i) + insidesize(j)) Then
                    'calculate normal vector and overlapping distance
                    x = dx / dis: y = dy / dis 'normal
                    over = (insidesize(i) + insidesize(j)) - dis 'overlap distance
                    'move balls apart based on overlap amount
                    insidex(i) = insidex(i) - x * (over / 2)
                    insidey(i) = insidey(i) - y * (over / 2)
                    insidex(j) = insidex(j) + x * (over / 2)
                    insidey(j) = insidey(j) + y * (over / 2)
                    'reflect velocities based on collision
                    vr = (insidexv(j) - insidexv(i)) * x + (insideyv(j) - insideyv(i)) * y
                    'update ball velocities based on collision
                    insidexv(i) = insidexv(i) + vr * x: insideyv(i) = insideyv(i) + vr * y
                    insidexv(j) = insidexv(j) - vr * x: insideyv(j) = insideyv(j) - vr * y
                End If
            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)
        'these bounce the ball off the edges of screen.
        'if outsideballs hits the edge, reverse directions.
        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 otsideball collision with bigball ===
        'calculate distance from center
        dis = Sqr((outsidex(j) - bigballx) ^ 2 + (outsidey(j) - bigbally) ^ 2)
        If dis < (bigballsize + outsidesizes(j)) Then
            'calculate the normal vector
            x = (outsidex(j) - bigballx) / dis
            y = (outsidey(j) - bigbally) / dis
            'reflect the velocity off the normal vector
            vr = outsidexv(j) * x + outsideyv(j) * y
            outsidexv(j) = outsidexv(j) - 2 * vr * x
            outsideyv(j) = outsideyv(j) - 2 * vr * y
            'move outside ball back
            'calculate how much it's overlapping...
            over = (bigballsize + outsidesizes(j)) - dis
            'move it away from the bigball
            outsidex(j) = outsidex(j) + x * over
            outsidey(j) = outsidey(j) + y * over
        End If
    Next

    '=== handle collisions between the outsideballs ===
    For i = 0 To outsidenum - 1
        For j = i + 1 To outsidenum - 1
            If i <> j Then
                'get distance between the two outside balls
                dx = outsidex(j) - outsidex(i)
                dy = outsidey(j) - outsidey(i)
                dis = Sqr(dx * dx + dy * dy)
                'check for collision, if so...
                If dis < (outsidesizes(i) + outsidesizes(j)) Then
                    'calculate normal vector and overlapping distance
                    x = dx / dis: y = dy / dis
                    'total overlap distance
                    over = (outsidesizes(i) + outsidesizes(j)) - dis
                    'move balls apart based on overlap
                    outsidex(i) = outsidex(i) - x * (over / 2)
                    outsidey(i) = outsidey(i) - y * (over / 2)
                    outsidex(j) = outsidex(j) + x * (over / 2)
                    outsidey(j) = outsidey(j) + y * (over / 2)
                    'reflect velocities between balls
                    vr = (outsidexv(j) - outsidexv(i)) * x + (outsideyv(j) - outsideyv(i)) * y
                    'update velocities based on collision
                    outsidexv(i) = outsidexv(i) + vr * x
                    outsideyv(i) = outsideyv(i) + vr * y
                    outsidexv(j) = outsidexv(j) - vr * x
                    outsideyv(j) = outsideyv(j) - vr * y
                End If
            End If
        Next
    Next

    '=== 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 = 0 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

Code: (Select All)
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 = 0 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

Find my programs here in Dav's QB64 Corner
Reply


Messages In This Thread
RE: Another small filled circe sub (not as fast as fcirc) - by Dav - 09-04-2024, 02:49 AM



Users browsing this thread: 69 Guest(s)