Thread Rating:
  • 1 Vote(s) - 4 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Circle-line collision
#31
I admit I haven't looked much at the discussion, busy days Smile But if you half the speed on everything, you can compensate it by doing two updates per frame (so that the speed actually doesn't change). And you can divide the speed with 4 but also make 4 updates per frame.
Reply
#32
(05-20-2026, 04:35 PM)kevin Wrote: Final update today.....so with the paddle_speed set to rad(1), there were no issues for over 3 hours. I then left it unobserved for a total of 6 hours, and when I checked back, there were 2 balls stuck in the "L" shaped polygon - none anywhere else, including the paddles. So a definite improvement over rad(10), but still with a frustrating issue, albeit minor. I wonder whether it may be necessary to either restrict the paddle_speed further (not sure about this, as no balls got stuck in the paddles), or maybe restrict the top speed of the balls (I have noticed that if the balls bounce off of polygons at particular angles, they can do so pretty fast)?
Incidentally, thanks are due to Johnno. It was his post last week about possible cause being balls moving through single pixels that have set me on this path.

[Image: bI210.gif]

update : 
- added outline for each shapes
- added delta time

Code:
#win32
set window "Collision", 900, 480
set redraw off
randomize time()

'shapes
v  = []
v.square    = [-50, -50, 50, -50, 50, 50, -50, 50]
v.star      = [0, -100, 25, -25, 100, 0, 25, 25, 0, 100, -25, 25, -100, 0, -25, -25]
v.cross     = [-19,-59, -19,-19, -59,-19, -59,20, -19,20, -19,60, 20,60, 20,20, 60,20, 60,-19, 20,-19, 20,-59]
v.rectangle = [-64.5, -15.5, 64.5, -15.5, 64.5, 15.5, -64.5, 15.5]
v.tmp1      = [0, 0, 200, 0, 200, 31, 0, 31]
v.tmp2      = [0, 0, 31, 0, 31, 415, 0, 415]

visible lines = [];filledPolys = []

'             MakePoly(verts,       x, y,       r, g, b,        lines, renderList) 
square      = MakePoly(v.square,    460, 320, 255, 100, 100,    lines, filledPolys)
star        = MakePoly(v.star,      350, 150, 100, 55, 100,     lines, filledPolys)
cross       = MakePoly(v.cross,     175, 125, 100, 100, 255,    lines, filledPolys)
rectangle   = MakePoly(v.rectangle, 180, 280, 255, 50, 0,       lines, filledPolys)
rectangle2  = MakePoly(v.rectangle, 550, 150, 255, 50, 0,       lines, filledPolys)
rectangle3  = MakePoly(v.rectangle, 370, 430, 55, 100, 0,       lines, filledPolys)
rectangle4  = MakePoly(v.rectangle, 550, 430, 55, 100, 0,       lines, filledPolys)
tmp1        = MakePoly(v.tmp1,       50, 416, 255, 100, 255,    lines, filledPolys)
tmp2        = MakePoly(v.tmp2,       20,  32, 255, 100, 255,    lines, filledPolys)

'balls
visible objs = []
ballCount = 0
objs[ballCount] = Object(rnd(50,width()-50), 0, 16)
ballCount = ballCount + 1

'flippers
visible flipper  = []
flipper.L       = rad(30)
flipper.L2      = rad(-30)
flipper.R       = rad(-30)
flipper.R2      = rad(30)
flipper.S       = rad(10)
flipper.autoPhase = 0

'simulation
visible simDuration = 60
visible simStartTime = 0
visible simRunning = false
visible durationOptions = [30, 60, 900, 1800,3600]
visible di = 1

'flippers
flipper.Angle3 = flipper.L
flipper.Angle4 = flipper.R
rectangle3.SetAngle(flipper.Angle3)
rectangle4.SetAngle(flipper.Angle4)

'fps count by Kevin
visible framecount,lasttime = 999,fps,frametime = 0,starttime = 0,endtime = 0

'delta time
baseFrameTime = 1000 / 60
lastFrameTime = clock()
deltaTime = 1


'----------
'Main Loop
'----------
while not keydown(KEY_ESCAPE, true)
    'delta time
    currentTime = clock()
    deltaTime = (currentTime - lastFrameTime) / baseFrameTime
    if deltaTime > 2 then
        deltaTime = 2
    endif
    lastFrameTime = currentTime
   
    'fps added by Kevin
    framecount = framecount + 1
    starttime = clock()

    set color 0, 0, 0; cls

    'grids
    set color 100, 100, 100
    for gx = 0 to 640 step 30  draw line gx, 0, gx, 480
    for gy = 0 to 480 step 30  draw line 0, gy, 640, gy

    'shapes rotation
    square.SetAngle(square.Angle() + rad(0.5) * deltaTime)
    star.SetAngle(star.Angle() + rad(0.5) * deltaTime)
    cross.SetAngle(cross.Angle() - rad(0.5) * deltaTime)
    rectangle.SetAngle(rectangle.Angle() - rad(0.5) * deltaTime)
    rectangle2.SetAngle(rectangle2.Angle() + rad(0.5) * deltaTime)
   
    'flipper
    flipper.autoPhase = flipper.autoPhase + 0.05*deltaTime
    target3 = flipper.L + (flipper.L2 - flipper.L) * (sin(flipper.autoPhase) * 0.3 )
    target4 = flipper.R + (flipper.R2 - flipper.R) * (sin(-flipper.autoPhase + PI) * 0.3 )

    flipperStep = flipper.S * deltaTime
    if flipper.Angle3 < target3
        flipper.Angle3 = flipper.Angle3 + flipperStep
        if flipper.Angle3 > target3 then
            flipper.Angle3 = target3
        endif
    elseif flipper.Angle3 > target3
        flipper.Angle3 = flipper.Angle3 - flipperStep
        if flipper.Angle3 < target3 then
            flipper.Angle3 = target3
        endif
    endif

    if flipper.Angle4 < target4
        flipper.Angle4 = flipper.Angle4 + flipperStep
        if flipper.Angle4 > target4 then
            flipper.Angle4 = target4
        endif
    elseif flipper.Angle4 > target4
        flipper.Angle4 = flipper.Angle4 - flipperStep
        if flipper.Angle4 < target4 then
            flipper.Angle4 = target4
        endif
    endif
    rectangle3.SetAngle(flipper.Angle3)
    rectangle4.SetAngle(flipper.Angle4)
   
    'polygon lines
    foreach polygn in filledPolys  polygn.DrawFilled()
    set color 255,255,255; DrawLines(lines)

    'coordinate label
    set color 255, 255, 255
    shapes = [square,star,cross,rectangle,rectangle2,rectangle3,rectangle4]
    for i = 0 to sizeof(shapes)-1   
        DrawCoordLabel(shapes[i])
    next
   
    if simRunning then
        foreach ball in objs
            UpdateObject(ball, deltaTime)
            DrawObject(ball)
        next
       
        i = 0
        while i < ballCount
            if objs[i].x > 690 or objs[i].x < -50 or objs[i].y > 530
                j = i
                while j < ballCount - 1
                    objs[j] = objs[j + 1]
                    j = j + 1
                wend
                ballCount = ballCount - 1
            else
                i = i + 1
            endif
        wend
       
        while ballCount < 32
            objs[ballCount] = Object(rnd(50,width()-50), 0, 16)
            ballCount = ballCount + 1
        wend
       
        i = 0
        while i < ballCount - 1
            j = i + 1
            while j < ballCount
                ResolveBallCollision(objs[i], objs[j])
                j = j + 1
            wend
            i = i + 1
        wend
    endif
   
    'control keys         
    if keydown(KEY_RETURN,true)
        if not simRunning then
            simRunning = true
            simStartTime = clock()
        elseif ballCount < 32
            objs[ballCount] = Object(rnd(50,width()-50), 0, 16)
            ballCount = ballCount + 1
        endif
    endif
   
    if keydown(KEY_D, true)
        di = di - 1
        if di < 0 then
            di = sizeof(durationOptions) - 1
        endif
        simDuration = durationOptions[di]
    endif
   
    'infobox
    set color 30,30,30; draw rect 635,0,260,480,true
    set color 255,255,255
    set caret 650,10
    wln "SIMULATION"
    wln "=========="
    wln "FPS = " + fps
    wln "ballCount is " + ballCount 
    wln "objs Table size is " + sizeof(objs)
    wln
    wln "Duration: " + durationOptions[di] + "s"
    if simRunning then
        elapsed = (clock() - simStartTime) / 1000
        wln "Elapsed: " + int(elapsed) + "s"
       
        'progress bar
        progress = elapsed / durationOptions[di]
        if progress > 1 then
            progress = 1
        endif
        barW = int(progress * 200)
        set color 255,255,255;  draw rect 650, 200, 200, 10, false
        set color 0,255,0;      draw rect 650, 200, barW, 10, true
        set color 255,255,255
       
        if elapsed >= durationOptions[di] then
            wln "COMPLETE"
            wln "Press SPACE BAR to continue"
            redraw
            do
                wait 1
            until keydown(KEY_SPACE,true)
           
            'reset section
            simRunning = false
            end           
        endif
    else
        wln
        wln "D to adjust duration"
        wln "ENTER to start"
    endif

    redraw
    fwait 60

    'fps count added by Kevin
    endtime = clock()
    frametime = frametime + endtime - starttime
    if frametime > 1000
        fps = framecount
        framecount = 0
        frametime = 0
    endif
wend


'-----------
' Functions
'-----------
function Object(x, y, r)
    return [x: x, y: y, r: r, rsqr: r*r, dx: 0, dy: 0, pdx: 0, pdy: 0]
endfunc

function DrawObject(obj)
    set color 255, 255, 0
    draw ellipse obj.x, obj.y, obj.r, obj.r, true
endfunc

function UpdateObject(obj, dt)
    obj.dy = obj.dy + 0.1 * dt
    if obj.dy > 6.0 then obj.dy = 6.0
    obj.x = obj.x + obj.dx * dt
    obj.y = obj.y + obj.dy * dt
    PushOut(obj, lines)
endfunc

function ResolveBallCollision(b1, b2)
    dx = b2.x - b1.x; dy = b2.y - b1.y
    distSq = dx*dx + dy*dy
    minDist = b1.r + b2.r
    minDistSq = minDist * minDist
    if distSq >= minDistSq or distSq = 0 then return
    dist = sqr(distSq)
    overlap = minDist - dist
    nx = dx / dist; ny = dy / dist
    b1.x = b1.x - nx * overlap * 0.5; b1.y = b1.y - ny * overlap * 0.5
    b2.x = b2.x + nx * overlap * 0.5; b2.y = b2.y + ny * overlap * 0.5
    dvx = b2.dx - b1.dx; dvy = b2.dy - b1.dy
    dvn = dvx*nx + dvy*ny
    if dvn < 0
        bounce = -(1 + 0.7) * dvn * 0.3
        b1.dx = b1.dx - bounce * nx;  b1.dy = b1.dy - bounce * ny
        b2.dx = b2.dx + bounce * nx;  b2.dy = b2.dy + bounce * ny
    endif
endfunc

function PushOut(obj, lines)
    for iter = 1 to 4
        col = false
        deepest = 0
        nx = 0; ny = 0
        foreach ln in lines
            t = (obj.x - ln[0])*ln[4] + (obj.y - ln[1])*ln[5]
            if t < 0 then t = 0
            if t > ln[6] then t = ln[6]
            px = ln[0] + t*ln[4]; py = ln[1] + t*ln[5]
            dx = obj.x - px; dy = obj.y - py
            dSq = dx*dx + dy*dy
            if dSq < obj.rsqr and dSq > 0.001
                dist = sqr(dSq)
                pen = obj.r - dist
                if pen > deepest
                    deepest = pen
                    nx = dx / dist; ny = dy / dist
                    col = true
                endif
            endif
        next
        if not col break
        obj.x = obj.x + nx * (deepest + 0.5)
        obj.y = obj.y + ny * (deepest + 0.5)
        vn = obj.dx * nx + obj.dy * ny
        if vn < 0
            obj.dx = obj.dx - 1.7 * vn * nx
            obj.dy = obj.dy - 1.7 * vn * ny
        endif
        obj.dx = obj.dx * 0.98
        obj.dy = obj.dy * 0.98
    next
endfunc

function Polygon(points, x, y, r, g, b)
    p = [trans: unset, org: [], verts: [], x: x, y: y, a: 0, cx: 0, cy: 0, r: r, g: g, b: b]
    pcount = sizeof(points)/2
    for i = 0 to pcount - 1
        p.verts[sizeof(p.verts)] = points[i*2]
        p.verts[sizeof(p.verts)] = points[i*2 + 1]
    next
    n = pcount - 1
    for i = 0 to n
        j = (i + 1)%pcount
        p.org[sizeof(p.org)] = Line(
                points[i*2], points[i*2 + 1],
                points[j*2], points[j*2 + 1])
    next
    for i = 0 to pcount - 1
        p.cx = p.cx + points[i*2]; p.cy = p.cy + points[i*2 + 1]
    next
    p.cx = p.cx/pcount; p.cy = p.cy/pcount
    p.trans = copy(p.org)
    p.AddTo = function(lines)
        foreach ln in .trans  lines[sizeof(lines)] = ln
    endfunc
    p.X = function(); return .x ;endfunc
    p.Y = function(); return .y ;endfunc
    p.Angle = function(); return .a ;endfunc
    p.SetTransform = function(x, y, angle)
        .x = x; .y = y; .a = angle; .Transform()
    endfunc
    p.SetPosition = function(x, y)
        .x = x; .y = y; .Transform()
    endfunc
    p.SetAngle = function(angle)
        .a = angle; .Transform()
    endfunc
    p.Transform = function()
        RotateLines(.org, .trans, .cx, .cy, .a)
        foreach ln in .trans
            ln[0] = ln[0] + .x; ln[1] = ln[1] + .y
            ln[2] = ln[2] + .x; ln[3] = ln[3] + .y
            ln[4] = (ln[2] - ln[0])/ln[6]; ln[5] = (ln[3] - ln[1])/ln[6]
        next
    endfunc
    p.DrawFilled = function()
        set color .r, .g, .b
        vcount = sizeof(.verts)/2
        polyArgs = []
        for i = 0 to vcount - 1
            vx = .verts[i*2] - .cx; vy = .verts[i*2 + 1] - .cy
            rx = vx*cos(.a) - vy*sin(.a)
            ry = vx*sin(.a) + vy*cos(.a)
            polyArgs[sizeof(polyArgs)] = rx + .cx + .x
            polyArgs[sizeof(polyArgs)] = ry + .cy + .y
        next
        draw poly polyArgs, true
    endfunc
    p.Transform()
    return p
    function RotateLines(srcLines, dstLines, aroundX, aroundY, angle)
        c = cos(angle); s = sin(angle)
        for i = 0 to sizeof(srcLines) - 1
            srcLn = srcLines[i]; dstLn = dstLines[i]
            x = srcLn[0] - aroundX; y = srcLn[1] - aroundY
            dstLn[0] = aroundX + x*c - y*s; dstLn[1] = aroundY + y*c + x*s
            x = srcLn[2] - aroundX; y = srcLn[3] - aroundY
            dstLn[2] = aroundX + x*c - y*s; dstLn[3] = aroundY + y*c + x*s
        next
    endfunc   
endfunc

function Line(x0, y0, x1, y1)
    ln = [x0, y0, x1, y1]
    dx = ln[2] - ln[0]; dy = ln[3] - ln[1]
    ln[6] = sqr(dx*dx + dy*dy)
    ln[4] = dx/ln[6]
    ln[5] = dy/ln[6]
    return ln
endfunc

function MakePoly(verts, x, y, r, g, b, lines, renderList)
    p = Polygon(verts, x, y, r, g, b)
    p.AddTo(lines)
    renderList[sizeof(renderList)] = p
    return p
endfunc

function DrawCoordLabel(p)
    set caret p.X(), p.Y()
    wln "X"
    wln (int(p.X())) + "," + (int(p.Y()))
endfunc

function DrawLines(lines)
    foreach ln in lines  draw line ln[0], ln[1], ln[2], ln[3]
endfunc
Reply
#33
Very nicely done indeed!
Logic is the beginning of wisdom.

Live long and prosper.
Reply
#34
Many congratulations.....I can see that you have put a lot of effort into getting this working properly, and I have now been running the code continuously for 21 hours with no issues whatsoever. A great achievement.
Reply
#35
Very nice!
Reply
#36
Is it my imagination or can I see the potential of a Pinball game in the making? Moo Ha Ha Ha....
Logic is the beginning of wisdom.

Live long and prosper.
Reply
#37
(05-23-2026, 09:58 AM)johnno56 Wrote: Is it my imagination or can I see the potential of a Pinball game in the making? Moo Ha Ha Ha....

You may refer to Kevin's work here https://www.naalaa.com/forum/thread-129.html
It's a small pinball game using Marcus' circle-line collision routines
Reply


Forum Jump:


Users browsing this thread: 2 Guest(s)