Posts: 459
Threads: 61
Joined: Nov 2023
Reputation:
3
05-18-2026, 11:25 AM
(This post was last modified: 05-18-2026, 11:27 AM by 1micha.elok.)
(05-18-2026, 11:20 AM)kevin Wrote: Some progress with the issue of the balls moving inside the polygons. I think the issue is with the ResolveBallCollision() function, which seems, in certain circumstances, to be pushing the balls inside the polygons. To demonstrate, if you disable this function, the problem disappears.
I think it may be a little more difficult to find a solution though 
I think I have found a way to avoid trapped ball inside polygon...give me 2-3 days more....
Posts: 196
Threads: 15
Joined: Dec 2023
Reputation:
8
(05-18-2026, 11:25 AM)1micha.elok Wrote: (05-18-2026, 11:20 AM)kevin Wrote: Some progress with the issue of the balls moving inside the polygons. I think the issue is with the ResolveBallCollision() function, which seems, in certain circumstances, to be pushing the balls inside the polygons. To demonstrate, if you disable this function, the problem disappears.
I think it may be a little more difficult to find a solution though 
I think I have found a way to avoid trapped ball inside polygon...give me 2-3 days more.... That's great to hear, please let me know if you need any help with testing.....
Posts: 459
Threads: 61
Joined: Nov 2023
Reputation:
3
05-19-2026, 09:12 AM
(This post was last modified: 05-19-2026, 09:14 AM by 1micha.elok.)
(05-18-2026, 11:50 AM)kevin Wrote: (05-18-2026, 11:25 AM)1micha.elok Wrote: (05-18-2026, 11:20 AM)kevin Wrote: Some progress with the issue of the balls moving inside the polygons. I think the issue is with the ResolveBallCollision() function, which seems, in certain circumstances, to be pushing the balls inside the polygons. To demonstrate, if you disable this function, the problem disappears.
I think it may be a little more difficult to find a solution though 
I think I have found a way to avoid trapped ball inside polygon...give me 2-3 days more.... That's great to hear, please let me know if you need any help with testing.....
Hi, Kevin
I rewrote the ResolveBallCollision() function, and then limit the ballcount (It's a good idea, I think  ) ...add few more codes and shapes...
By the way, I'm using Windows 7 64bit, 4GB RAM, Intel Core i5 ...
Test the code 5 times, no more trapped balls inside polygons...
[Modified]
Code: #win32
set window "Collision", 640, 480
set redraw off
randomize time()
'Vertices
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, 32, 32, 255, 100, 255, lines, filledPolys)
'Balls
visible objs = []
ballCount = 0
objs[ballCount] = Object(rnd(50,width()-50), 0, 16)
ballCount = ballCount + 1
'Flipper position and speed
visible flipper_left = rad(30)
visible flipper_left2 = rad(-30)
visible flipper_right = rad(-30)
visible flipper_right2 = rad(30)
visible flipper_speed = rad(10)
'Initialize flippers position
visible flipper3Angle = flipper_left
visible flipper4Angle = flipper_right
rectangle3.SetAngle(flipper3Angle)
rectangle4.SetAngle(flipper4Angle)
############ to calculate FPS ##########################'added by kevin
visible framecount,lasttime = 999,fps,frametime = 0,starttime = 0,endtime = 0
##########################################################
'-----------
' Main Loop
'-----------
while not keydown(KEY_ESCAPE, true)
'added by kevin
### for FPS calc #########
framecount = framecount + 1
starttime = clock()
###########################
' Clear screen
set color 0, 0, 0; cls
' Draw grid
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
' Rotate obstacles: + = clockwise, - = counter-clockwise
square.SetAngle(square.Angle() + rad(0.5))
star.SetAngle(star.Angle() + rad(0.5))
cross.SetAngle(cross.Angle() - rad(0.5))
rectangle.SetAngle(rectangle.Angle() - rad(0.5))
rectangle2.SetAngle(rectangle2.Angle() + rad(0.5))
' Flipper control
target3 = flipper_left
target4 = flipper_right
if keydown(KEY_SPACE)
target3 = flipper_left2
target4 = flipper_right2
endif
' flipper
if flipper3Angle < target3
flipper3Angle = flipper3Angle + flipper_speed
if flipper3Angle > target3 then
flipper3Angle = target3
endif
elseif flipper3Angle > target3
flipper3Angle = flipper3Angle - flipper_speed
if flipper3Angle < target3 then
flipper3Angle = target3
endif
endif
if flipper4Angle < target4
flipper4Angle = flipper4Angle + flipper_speed
if flipper4Angle > target4 then
flipper4Angle = target4
endif
elseif flipper4Angle > target4
flipper4Angle = flipper4Angle - flipper_speed
if flipper4Angle < target4 then
flipper4Angle = target4
endif
endif
rectangle3.SetAngle(flipper3Angle)
rectangle4.SetAngle(flipper4Angle)
' draw obstacles
foreach polygn in filledPolys polygn.DrawFilled()
' coordinate labels, infobox
set color 255, 255, 255
shapes = [square,star,cross,rectangle,rectangle2,rectangle3,rectangle4]
for i = 0 to sizeof(shapes)-1
DrawCoordLabel(shapes[i])
next
set caret width()/2, height()-120; center "SPACE: flippers | ENTER: balls"
' balls
foreach ball in objs
UpdateObject(ball)
DrawObject(ball)
next
' Remove out-of-bounds balls to prevent unnecessary collision checks
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
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
'Drop ball [modified, limit ballCount]
if keydown(KEY_RETURN,true) and ballCount < 32
objs[ballCount] = Object(rnd(50,width()-50), 0, 16)
ballCount = ballCount + 1
endif
set color 255,255,255
###########################################
'added by kevin
set caret 320,10;set justification center
write "FPS = " + fps;wln
write "ballCount is " + ballCount + " | objs Table size is " + sizeof(objs);wln
'end
###########################################
redraw
fwait 60
####### FPS calc ############################
endtime = clock()
frametime = frametime + endtime - starttime
if frametime > 1000 # 1 seconde
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
' Draw ball
function DrawObject(obj)
set color 255, 255, 0
draw ellipse obj.x, obj.y, obj.r, obj.r, true
endfunc
' Update ball
function UpdateObject(obj)
obj.dy = obj.dy + 0.1
if obj.dy > 6 then obj.dy = 6
' Update position
obj.x = obj.x + obj.dx
obj.y = obj.y + obj.dy
' Collision detection
PushOut(obj, lines)
endfunc
' ResolveBallCollision
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
' Early exit if no collision or zero distance
if distSq >= minDistSq or distSq = 0 then return
dist = sqr(distSq)
' [modified]
' Push them apart so they stop overlapping
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
' Adjust speeds if they're moving toward each other
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
' PushOut: collision resolution
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
' Polygon
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
' Store original vertices
for i = 0 to pcount - 1
p.verts[sizeof(p.verts)] = points[i*2]
p.verts[sizeof(p.verts)] = points[i*2 + 1]
next
' collision lines
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
' Apply rotation
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
Posts: 196
Threads: 15
Joined: Dec 2023
Reputation:
8
05-19-2026, 12:43 PM
(This post was last modified: 05-19-2026, 02:42 PM by kevin.)
[quote pid="2688" dateline="1779181953"]
Hi, Kevin
I rewrote the ResolveBallCollision() function, and then limit the ballcount (It's a good idea, I think  ) ...add few more codes and shapes...
By the way, I'm using Windows 7 64bit, 4GB RAM, Intel Core i5 ...
Test the code 5 times, no more trapped balls inside polygons...
[Modified]
Code: #win32
set window "Collision", 640, 480
set redraw off
randomize time()
'Vertices
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, 32, 32, 255, 100, 255, lines, filledPolys)
'Balls
visible objs = []
ballCount = 0
objs[ballCount] = Object(rnd(50,width()-50), 0, 16)
ballCount = ballCount + 1
'Flipper position and speed
visible flipper_left = rad(30)
visible flipper_left2 = rad(-30)
visible flipper_right = rad(-30)
visible flipper_right2 = rad(30)
visible flipper_speed = rad(10)
'Initialize flippers position
visible flipper3Angle = flipper_left
visible flipper4Angle = flipper_right
rectangle3.SetAngle(flipper3Angle)
rectangle4.SetAngle(flipper4Angle)
############ to calculate FPS ##########################'added by kevin
visible framecount,lasttime = 999,fps,frametime = 0,starttime = 0,endtime = 0
##########################################################
'-----------
' Main Loop
'-----------
while not keydown(KEY_ESCAPE, true)
'added by kevin
### for FPS calc #########
framecount = framecount + 1
starttime = clock()
###########################
' Clear screen
set color 0, 0, 0; cls
' Draw grid
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
' Rotate obstacles: + = clockwise, - = counter-clockwise
square.SetAngle(square.Angle() + rad(0.5))
star.SetAngle(star.Angle() + rad(0.5))
cross.SetAngle(cross.Angle() - rad(0.5))
rectangle.SetAngle(rectangle.Angle() - rad(0.5))
rectangle2.SetAngle(rectangle2.Angle() + rad(0.5))
' Flipper control
target3 = flipper_left
target4 = flipper_right
if keydown(KEY_SPACE)
target3 = flipper_left2
target4 = flipper_right2
endif
' flipper
if flipper3Angle < target3
flipper3Angle = flipper3Angle + flipper_speed
if flipper3Angle > target3 then
flipper3Angle = target3
endif
elseif flipper3Angle > target3
flipper3Angle = flipper3Angle - flipper_speed
if flipper3Angle < target3 then
flipper3Angle = target3
endif
endif
if flipper4Angle < target4
flipper4Angle = flipper4Angle + flipper_speed
if flipper4Angle > target4 then
flipper4Angle = target4
endif
elseif flipper4Angle > target4
flipper4Angle = flipper4Angle - flipper_speed
if flipper4Angle < target4 then
flipper4Angle = target4
endif
endif
rectangle3.SetAngle(flipper3Angle)
rectangle4.SetAngle(flipper4Angle)
' draw obstacles
foreach polygn in filledPolys polygn.DrawFilled()
' coordinate labels, infobox
set color 255, 255, 255
shapes = [square,star,cross,rectangle,rectangle2,rectangle3,rectangle4]
for i = 0 to sizeof(shapes)-1
DrawCoordLabel(shapes[i])
next
set caret width()/2, height()-120; center "SPACE: flippers | ENTER: balls"
' balls
foreach ball in objs
UpdateObject(ball)
DrawObject(ball)
next
' Remove out-of-bounds balls to prevent unnecessary collision checks
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
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
'Drop ball [modified, limit ballCount]
if keydown(KEY_RETURN,true) and ballCount < 32
objs[ballCount] = Object(rnd(50,width()-50), 0, 16)
ballCount = ballCount + 1
endif
set color 255,255,255
###########################################
'added by kevin
set caret 320,10;set justification center
write "FPS = " + fps;wln
write "ballCount is " + ballCount + " | objs Table size is " + sizeof(objs);wln
'end
###########################################
redraw
fwait 60
####### FPS calc ############################
endtime = clock()
frametime = frametime + endtime - starttime
if frametime > 1000 # 1 seconde
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
' Draw ball
function DrawObject(obj)
set color 255, 255, 0
draw ellipse obj.x, obj.y, obj.r, obj.r, true
endfunc
' Update ball
function UpdateObject(obj)
obj.dy = obj.dy + 0.1
if obj.dy > 6 then obj.dy = 6
' Update position
obj.x = obj.x + obj.dx
obj.y = obj.y + obj.dy
' Collision detection
PushOut(obj, lines)
endfunc
' ResolveBallCollision
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
' Early exit if no collision or zero distance
if distSq >= minDistSq or distSq = 0 then return
dist = sqr(distSq)
' [modified]
' Push them apart so they stop overlapping
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
' Adjust speeds if they're moving toward each other
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
' PushOut: collision resolution
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
' Polygon
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
' Store original vertices
for i = 0 to pcount - 1
p.verts[sizeof(p.verts)] = points[i*2]
p.verts[sizeof(p.verts)] = points[i*2 + 1]
next
' collision lines
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
' Apply rotation
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
[/quote]
This is very good progress. Performance is good, with fps around 100 most of the time, with very short occasional dips to 50.
Circle collision is much better. I thought it was perfect, but unfortunately, after starting to move the flippers, a ball did get stuck inside one. I re-tested, and this happened again. I have tested for probably 40-45 minutes in total, and the flipper issue did happen quite a few times, and twice, a ball went inside the rectangle above the flippers.
I think that there is a workaround for now for the flippers. I reduced the thickness of the flippers from 31 to 20 pixels, and this seems to have fixed it. 20 pixels is a random size - it may be that it could be much closer to the original 31 if necessary, but this would need testing. It looks to me as though the ball will still go inside the flipper now, but immediately it will come back out on the same side.
It seems relevant to me that I have only seen the ball go inside the flipper, and also the the square that is closest to the flippers. I think that this may be fixed by moving the square a little further away from the flippers. I have moved it up 40 pixels, and so far, I have not seen a ball go inside the square. I think that this needs further testing to be confident about this though, and I will let the code run this afternoon (I have made a small amendment to release the balls automatically, and move the flippers randomly, so I can leave it running unattended).
Hope this helps? All the best - Kevin.
******************************************************************************************
Quick update. I have run the code with the 2 modifications (20 pixel flippers and 40 pixel higher square) for 2 hours now, and no ball has entered the square, or stayed within the flipper. But, I have observed that sometimes, when a ball starts to move inside the flipper, it will pass through it, rather than bouncing off, as I had originally thought.
Also, on one occasion in the 2 hours, a ball entered the L shape, which is again 31 pixels wide.
Posts: 196
Threads: 15
Joined: Dec 2023
Reputation:
8
I think the issue with the flippers is the speed of movement. Currently, moving the flippers constantly, I see a ball moving inside the flipper within 1-2 minutes. By reducing flipper_speed to rad(5), from rad(10), I haven't seen any issues with the balls after 15 minutes running the code, which is very encouraging. I will let it run for a while longer, and post the results.
Posts: 459
Threads: 61
Joined: Nov 2023
Reputation:
3
(05-20-2026, 08:17 AM)kevin Wrote: I think the issue with the flippers is the speed of movement. Currently, moving the flippers constantly, I see a ball moving inside the flipper within 1-2 minutes. By reducing flipper_speed to rad(5), from rad(10), I haven't seen any issues with the balls after 15 minutes running the code, which is very encouraging. I will let it run for a while longer, and post the results.
This project has turned into a mini research class
Posts: 196
Threads: 15
Joined: Dec 2023
Reputation:
8
(05-20-2026, 08:22 AM)1micha.elok Wrote: (05-20-2026, 08:17 AM)kevin Wrote: I think the issue with the flippers is the speed of movement. Currently, moving the flippers constantly, I see a ball moving inside the flipper within 1-2 minutes. By reducing flipper_speed to rad(5), from rad(10), I haven't seen any issues with the balls after 15 minutes running the code, which is very encouraging. I will let it run for a while longer, and post the results.
This project has turned into a mini research class 
I'm really enjoying studying your/Marcus's code
So, the reduced flipper speed was great for 29 minutes - no ball inside any of the polygons. For comparison, using the original speed, 3 balls went inside the flippers within 5 minutes.
But then, on the 30th minute, a ball got stuck inside one of the flippers. So I think that reducing the speed may be a workaround, it just needs a bit of experimentation to find the optimum speed. I'll try it at rad(3) next (over 30 minutes running currently, and no issues anywhere).
I expect that you have a good way of testing this, but in case anyone else wants to have a play with it, I'll add the code below that I use to allow me to run the code and just leave it running - the balls are added, and the flippers move, automatically.
Code: 'This is for testing only
#win32
set window "Collision", 640, 480
set redraw off
randomize time()
'Vertices
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.rectangle = [-64.5, -10, 64.5, -10, 64.5, 10, -64.5, 10]
'v.rectangle = [-64.5, -14, 64.5, -14, 64.5, 14, -64.5, 14]
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, 320, 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, 32, 32, 255, 100, 255, lines, filledPolys)
'Balls
visible objs = []
ballCount = 0
objs[ballCount] = Object(rnd(50,width()-50), 0, 16)
ballCount = ballCount + 1
'Flipper position and speed
visible flipper_left = rad(30)
visible flipper_left2 = rad(-30)
visible flipper_right = rad(-30)
visible flipper_right2 = rad(30)
visible flipper_speed = rad(10)
'Initialize flippers position
visible flipper3Angle = flipper_left
visible flipper4Angle = flipper_right
rectangle3.SetAngle(flipper3Angle)
rectangle4.SetAngle(flipper4Angle)
############ to calculate FPS ##########################'added by kevin
visible framecount,lasttime = 999,fps,frametime = 0,starttime = 0,endtime = 0
##########################################################
visible drop_timer = 0,paddle_timer = 0,paddle_target = rnd(30) + 10
'-----------
' Main Loop
'-----------
while not keydown(KEY_ESCAPE, true)
'added by kevin
### for FPS calc #########
framecount = framecount + 1
starttime = clock()
###########################
'automate for testing - kevin
drop_timer = drop_timer + 1
if drop_timer >= 10
drop_timer = 0
objs[ballCount] = Object(rnd(50,width()-50), 0, 16)
ballCount = ballCount + 1
endif
' target3 = flipper_left
' target4 = flipper_right
paddle_timer = paddle_timer + 1
' if paddle_timer >= paddle_target
' paddle_timer = 0
' paddle_target = rnd(30) + 10
' target3 = flipper_left2
' target4 = flipper_right2
'endif
'end of added code
' Clear screen
set color 0, 0, 0; cls
' Draw grid
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
' Rotate obstacles: + = clockwise, - = counter-clockwise
square.SetAngle(square.Angle() + rad(0.5))
star.SetAngle(star.Angle() + rad(0.5))
cross.SetAngle(cross.Angle() - rad(0.5))
rectangle.SetAngle(rectangle.Angle() - rad(0.5))
rectangle2.SetAngle(rectangle2.Angle() + rad(0.5))
' Flipper control
target3 = flipper_left
target4 = flipper_right
'if keydown(KEY_SPACE)
if paddle_timer >= paddle_target
' paddle_timer = 0
paddle_target = rnd(30) + 10
target3 = flipper_left2
target4 = flipper_right2
endif
if paddle_timer >= paddle_target + 60 then paddle_timer = 0
' flipper
if flipper3Angle < target3
flipper3Angle = flipper3Angle + flipper_speed
if flipper3Angle > target3 then
flipper3Angle = target3
endif
elseif flipper3Angle > target3
flipper3Angle = flipper3Angle - flipper_speed
if flipper3Angle < target3 then
flipper3Angle = target3
endif
endif
if flipper4Angle < target4
flipper4Angle = flipper4Angle + flipper_speed
if flipper4Angle > target4 then
flipper4Angle = target4
endif
elseif flipper4Angle > target4
flipper4Angle = flipper4Angle - flipper_speed
if flipper4Angle < target4 then
flipper4Angle = target4
endif
endif
rectangle3.SetAngle(flipper3Angle)
rectangle4.SetAngle(flipper4Angle)
' draw obstacles
foreach polygn in filledPolys polygn.DrawFilled()
' coordinate labels, infobox
set color 255, 255, 255
shapes = [square,star,cross,rectangle,rectangle2,rectangle3,rectangle4]
for i = 0 to sizeof(shapes)-1
DrawCoordLabel(shapes[i])
next
set caret width()/2, height()-120; center "SPACE: flippers | ENTER: balls"
' balls
foreach ball in objs
UpdateObject(ball)
DrawObject(ball)
next
' Remove out-of-bounds balls to prevent unnecessary collision checks
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
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
'Drop ball [modified, limit ballCount]
if keydown(KEY_RETURN,true) and ballCount < 60
objs[ballCount] = Object(rnd(50,width()-50), 0, 16)
ballCount = ballCount + 1
endif
set color 255,255,255
###########################################
'added by kevin
set caret 320,10;set justification center
write "FPS = " + fps;wln
write "ballCount is " + ballCount + " | objs Table size is " + sizeof(objs);wln
'end
###########################################
redraw
fwait 60
####### FPS calc ############################
endtime = clock()
frametime = frametime + endtime - starttime
if frametime > 1000 # 1 seconde
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
' Draw ball
function DrawObject(obj)
set color 255, 255, 0
draw ellipse obj.x, obj.y, obj.r, obj.r, true
endfunc
' Update ball
function UpdateObject(obj)
obj.dy = obj.dy + 0.1
if obj.dy > 6 then obj.dy = 6
' Update position
obj.x = obj.x + obj.dx
obj.y = obj.y + obj.dy
' Collision detection
PushOut(obj, lines)
endfunc
' ResolveBallCollision
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
' Early exit if no collision or zero distance
if distSq >= minDistSq or distSq = 0 then return
dist = sqr(distSq)
' [modified]
' Push them apart so they stop overlapping
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
' Adjust speeds if they're moving toward each other
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
' PushOut: collision resolution
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
' Polygon
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
' Store original vertices
for i = 0 to pcount - 1
p.verts[sizeof(p.verts)] = points[i*2]
p.verts[sizeof(p.verts)] = points[i*2 + 1]
next
' collision lines
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
' Apply rotation
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
Posts: 196
Threads: 15
Joined: Dec 2023
Reputation:
8
Quick update:
Using a flipper_speed of rad(3), all was perfect for about an hour, when a ball got trapped in a flipper. So, I changed it to rad(1), which I believe is twice the speed of the other polygon rotations, and it has run perfectly for just over 2 hours - I will keep it running for now.
If this continues to work well, I'm sure there are many things that could be done with this code. However, it would be really good if a different solution could be found, where faster movement would be allowed?
Posts: 196
Threads: 15
Joined: Dec 2023
Reputation:
8
05-20-2026, 04:35 PM
(This post was last modified: 05-20-2026, 04:38 PM by kevin.)
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.
Posts: 459
Threads: 61
Joined: Nov 2023
Reputation:
3
Thanks for the updates on the collision issues. I’m thinking about implementing delta time for the ball movement so it runs exactly the same on old and new machines alike.
|