Small pinball game using circle-line collision routines from Marcus kevin Member Posts: 57 Threads: 4 Joined: Dec 2023 Reputation: 4 05-01-2024, 02:30 PM This is not meant to be a proper simulation (as you will see if you run it ), but just an experiment with the terrific circle-line functions that Marcus provided a while back. I've used the functions as they were originally written by Marcus, with one small alteration to the polygon function, where I have added a fifth argument to allow me to specify a point around which the polygon is rotated, rather than the center point of the polygon (I needed this for the 2 flippers). There a few values that can be altered in the code to change the way that the ball reacts to certain lines - I've marked some of these as "arbitrary values", and I'm sure changing these will make for a smoother game, but I've decided to move on to a new project. Control the flippers with the A and D keys, and launch the ball using the UP arrow.     Code:```' Experiment. set window "PINBALL", 480, 640 set redraw off #win32 visible score = 0 ' List of lines that objects can collide with. left_flipper = [] right_flipper = [] l_flipper = Polygon([0,0,10,-10,60,30,60,40], 140,580,  true,[0,0]) r_flipper = Polygon([0,0,-10,-10,-60,30,-60,40], 300,580,  true,[0,0]) '============================================================================= 'Note  -added fifth argument to Polygon - 0 rotates around center point, or 'use a 2 part table to rotate around any point in the polygon - i.e. [32,34] ' to rotate around the fourth point in the polygon array here : - 'l_flipper = Polygon([0,0,10,0,32,24,32,34,22,34,0,10], 320, 240, true,[32,34]) '============================================================================== l_flipper.AddTo(left_flipper) left_flipper_timer = 0 left_ang = 0 r_flipper.AddTo(right_flipper) right_flipper_timer = 0 right_ang = 0 bouncers = [] lines = [] circles = [] table1 = Polygon([180,0,300,0,360,20,420,60,460,120,480,180,480,640,                     0,640,0,180,20,120,60,60,120,20,180,0],0,0,false,0) table1.AddTo(lines) visible circle_hit = unset circle1 = Polygon([0,0,9,3,13,8,15,15,13,23,9,27,0,30,-9,27,-13,23,-15,15,-13,8,-9,3],100,230,true,0) circle1.AddTo(circles) circle2 = Polygon([0,0,9,3,13,8,15,15,13,23,9,27,0,30,-9,27,-13,23,-15,15,-13,8,-9,3],225,230,true,0) circle2.AddTo(circles) circle3 = Polygon([0,0,9,3,13,8,15,15,13,23,9,27,0,30,-9,27,-13,23,-15,15,-13,8,-9,3],350,230,true,0) circle3.AddTo(circles) circle4 = Polygon([0,0,9,3,13,8,15,15,13,23,9,27,0,30,-9,27,-13,23,-15,15,-13,8,-9,3],162,180,true,0) circle4.AddTo(circles) circle5 = Polygon([0,0,9,3,13,8,15,15,13,23,9,27,0,30,-9,27,-13,23,-15,15,-13,8,-9,3],287,180,true,0) circle5.AddTo(circles) circle6 = Polygon([0,0,9,3,13,8,15,15,13,23,9,27,0,30,-9,27,-13,23,-15,15,-13,8,-9,3],130,440,true,0) circle6.AddTo(circles) circle7 = Polygon([0,0,9,3,13,8,15,15,13,23,9,27,0,30,-9,27,-13,23,-15,15,-13,8,-9,3],310,440,true,0) circle7.AddTo(circles) 'additional parts of table 'lines - no bouncing lines[sizeof(lines)] = Line(440,640,440,180) lines[sizeof(lines)] = Line(360,640,440,590) lines[sizeof(lines)] = Line(0,590,80,640) 'left upper curve (inner) - no bouncing lines[sizeof(lines)] = Line(65,330,40,290) lines[sizeof(lines)] = Line(40,290,40,200) lines[sizeof(lines)] = Line(40,200,55,150) lines[sizeof(lines)] = Line(55,150,95,95) lines[sizeof(lines)] = Line(95,95,150,60) lines[sizeof(lines)] = Line(150,60,200,45) 'right upper curve (inner) - no bouncing lines[sizeof(lines)] = Line(300,85,315,90) lines[sizeof(lines)] = Line(315,90,360,120) lines[sizeof(lines)] = Line(360,120,385,160) lines[sizeof(lines)] = Line(385,160,400,200) lines[sizeof(lines)] = Line(40,490,40,430) lines[sizeof(lines)] = Line(400,430,400,490) 'triangles sticking out of side - not bouncing lines[sizeof(lines)] = Line(0,380,40,380) lines[sizeof(lines)] = Line(0,310,40,380) lines[sizeof(lines)] = Line(440,380,400,380) lines[sizeof(lines)] = Line(400,380,440,310) 'bouncers bouncers[sizeof(bouncers)] = Line(40,490,130,550) bouncers[sizeof(bouncers)] = Line(400,490,310,550) bouncers[sizeof(bouncers)] = Line(40,430,70,435) bouncers[sizeof(bouncers)] = Line(400,430,370,435) ' Player. visible obj = Object(472, 632, 16) obj.dx = 0 obj.dy = 0 ############  to calculate FPS  ########################## visible framecount,lasttime = 999,fps,frametime = 0,starttime = 0,endtime = 0,min_fps = 99999,max_fps = 0 ########################################################## while not keydown(KEY_ESCAPE) ###  for FPS calc ######### framecount = framecount + 1 starttime = clock() ###########################     ' Rotate obstacles.     circle1.SetAngle(circle1.Angle() + rad(3))     circle2.SetAngle(circle2.Angle() + rad(3))     circle3.SetAngle(circle3.Angle() + rad(3))     circle4.SetAngle(circle4.Angle() + rad(3))     circle5.SetAngle(circle5.Angle() + rad(3))     circle6.SetAngle(circle5.Angle() + rad(3))     circle7.SetAngle(circle5.Angle() + rad(3))     set mouse off    if obj.y >= 624 and obj.x < 440 then game_over()        obj.x = obj.x + obj.dx     obj.y = obj.y + obj.dy     '-------   left flipper  ------------     if keydown(KEY_A,true) and left_flipper_timer <= 0         left_flipper_timer = 16     endif     if left_flipper_timer > 0         left_flipper_timer = left_flipper_timer - 1         l_flipper.SetAngle(max(l_flipper.Angle() - rad(4),-1.2))     elseif left_flipper_timer <= 0         l_flipper.SetAngle(0)     endif       PushOut(obj, left_flipper)     if obj.pdy < 0     if l_flipper.Angle() <> 0         obj.dy = - 10         left_ang = angle_rad(l_flipper.trans[1][0],l_flipper.trans[1][1],l_flipper.trans[2][0],l_flipper.trans[2][1])         left_vecs = get_vecs(left_ang)         obj.dx = obj.dx + left_vecs[0] * 16'arbitrary figure     endif     endif     '==================================     '-------   right flipper  ------------     if keydown(KEY_D,true) and right_flipper_timer <= 0         right_flipper_timer = 16     endif     if right_flipper_timer > 0         right_flipper_timer = right_flipper_timer - 1         r_flipper.SetAngle(max(r_flipper.Angle() + rad(4),-1.2))     elseif right_flipper_timer <= 0         r_flipper.SetAngle(0)     endif     PushOut(obj, right_flipper)     if obj.pdy < 0     if r_flipper.Angle() <> 0         obj.dy = - 10        right_ang = angle_rad(r_flipper.trans[2][0],r_flipper.trans[2][1],r_flipper.trans[1][0],r_flipper.trans[1][1])         right_vecs = get_vecs(right_ang)         obj.dx = obj.dx + right_vecs[0] * 32'arbitrary figure     endif     endif     '==================================     PushOut(obj, bouncers)' make them bounce a little     if obj.pdx <> 0 or obj.pdy <> 0        obj.dx = obj.dx + obj.pdx*12'arbitrary figure        obj.dy = obj.dy + obj.pdy*12 'arbitrary figure     endif           PushOut(obj, lines)   '     if obj.y < 180 and obj.x > 240  and obj.dx < 0' help with initial lauch of ball         if obj.pdx <> 0 or obj.pdy <> 0             obj.dx = obj.dx + obj.pdx*4'arbitrary figure                   endif    endif    if obj.y < 180 and obj.x < 240 ' help with initial lauch of ball         if obj.pdx <> 0 or obj.pdy <> 0             obj.dx = obj.dx + obj.pdx*0.3'arbitrary figure             obj.dy = obj.dy + obj.pdy*1         endif    endif     ' obj.pdx and obj.pdy has now been set to the average "push direction" caused by all lines     ' pushing the object around.        ' Push direction y < 0 means the object is being pushed UP. In that case, set dy to 0.     if obj.pdy < 0         ' If push direction y < -0.25, let the player jump.         if obj.pdy < -0.25 and keydown(KEY_UP, true) and obj.x >= 455             obj.dy = -14        endif     endif     ' Apply gravity.     obj.dy = min(obj.dy + 0.1, 6)     ' So ... stuff to dx.     obj.dx = obj.dx + obj.pdx*0.25 ' I have no idea     obj.dx = obj.dx*0.95         PushOut(obj, circles)         if obj.pdx <> 0 or obj.pdy <> 0         score = score + 10         obj.dx = obj.pdx * 8'arbitrary         obj.dy = obj.pdy * 8'arbitrary     endif          set color 0, 0, 0     cls     set color 255, 255, 255     DrawLines(lines)     DrawLines(circles)     DrawLines(bouncers)     DrawLines(left_flipper)     DrawLines(right_flipper)     obj.Draw()         set caret 20,10     set justification left     write "SCORE : " + score;wln    '    set caret 460,10 '    set justification right '    write "FPS = " + str(fps);wln '    write "MIN_FPS = " + str(min_fps);wln '    write "MAX_FPS = " + str(max_fps);wln     redraw     fwait 60         #######  FPS calc  ############################ endtime = clock() frametime = frametime + endtime - starttime if frametime > 1000 # 1 second     fps = framecount     framecount = 0     frametime = 0     if fps < min_fps then min_fps = fps     if fps > max_fps then max_fps = fps endif ################################################ wend function DrawLines(lines)     foreach ln in lines  draw line ln[0], ln[1], ln[2], ln[3] endfunc function Object(x, y, r)     return [x: x, y: y, r: r, rsqr: r*r, pdx: 0, pdy: 0,             Draw: function(); draw ellipse .x, .y, .r, .r, false; endfunc] endfunc ' Pushout ' ------- function PushOut(obj, lines)     tests = 4     obj.pdx = 0     obj.pdy = 0     for i = 1 to tests         col = false         foreach ln in lines             dp = max(0, min(ln[6], (obj.x - ln[0])*ln[4] + (obj.y - ln[1])*ln[5]))             px = ln[0] + dp*ln[4]; py = ln[1] + dp*ln[5]             dx = obj.x - px; dy = obj.y - py             d = dx*dx + dy*dy             if d < obj.rsqr                 k = 1/sqr(d)                 obj.x = px + dx*k*obj.r                 obj.y = py + dy*k*obj.r                 obj.pdx = obj.pdx + dx*k                 obj.pdy = obj.pdy + dy*k                 col = true                       endif         next         if not col break     next     if obj.pdx or obj.pdy         k = 1/sqr(obj.pdx*obj.pdx + obj.pdy*obj.pdy)         obj.pdx = obj.pdx*k         obj.pdy = obj.pdy*k     endif endfunc ' Polygon ' ------- ' Return polygon at position x, y. ' Polygon ' ------- ' Return polygon at position x, y. 'function Polygon(points, x, y, closed,rotation_point_x,rotation_point_y) function Polygon(points, x, y, closed,rotation_point) '======================================================================== 'Note -  added fifth argument to Polygon - 0 rotates around center point, or 'use a 2 part table to rotate around any point in the polygon - i.e. [32,34] ' to rotate around the fourth point in the polygon array here :- 'l_flipper = Polygon([0,0,10,0,32,24,32,34,22,34,0,10], 320, 240, true,[32,34]) '========================================================================     p = [trans: unset, org: [], x: x, y: y, a: 0, cx:0, cy: 0]     pcount = sizeof(points)/2     centerX = 0     centerY = 0     if closed  n = pcount - 1     else    n = pcount - 2     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         if rotation_point = 0             p.cx = p.cx + points[i*2]; p.cy = p.cy + points[i*2 + 1]         else             p.cx = rotation_point[0] * pcount 'rotation_point_x             p.cy = rotation_point[1] * pcount ' rotation_point_y         endif     next     p.cx = p.cx/pcount; p.cy = p.cy/pcount     p.trans = copy(p.org)        ' AddTo     ' -----     ' Add to list of lines.     p.AddTo = function(lines)         foreach ln in .trans  lines[sizeof(lines)] = ln     endfunc     ' X     ' -     ' Return x coordinate.     p.X = function()         return .x     endfunc        ' Y     ' -     ' Return y coordinate.     p.Y = function()         return .y     endfunc        ' Angle     ' -----     ' Return angle.     p.Angle = function()         return .a     endfunc        ' SetTransform     ' ------------     ' Set position and angle and apply.           p.SetTransform = function(x, y, angle)         .x = x         .y = y         .a = angle         .Transform()     endfunc        ' SetPosition     ' -----------     ' Set position and apply.     p.SetPosition = function(x, y)         .x = x         .y = y         .Transform()     endfunc        ' SetAngle     ' --------     ' Set angle and apply.     p.SetAngle = function(angle)         .a = angle         .Transform()     endfunc        ' Transform     ' ---------     ' Update transformed polygon.     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.Transform()        return p        ' RotateLines     ' -----------     ' Helper.     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 ' Rectangle ' --------- function Rectangle(x, y, w, h)     w = w - 1     h = h - 1     return Polygon([0, 0, w, 0, w, h, 0, h], x, y, true,0) endfunc ' Line ' ---- ' Return a new line. 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 angle_rad(x0,y0,x1,y1) dx = x1 - x0 dy = y1 - y0     angle_r = atan2(dx,dy) return angle_r endfunc '----------------------- function get_vecs(angle_r) vector_x =  -cos(angle_r - PI) vector_y = sin(angle_r - PI) return [vector_x,vector_y] endfunc '----------------------- function game_over() set caret 240,320 set justification center write "GAME OVER" 'pln "GAME OVER" redraw wait 4000 obj.x = 472 obj.y = 632 score = 0 endfunc``` Marcus Administrator Posts: 316 Threads: 39 Joined: Nov 2023 Reputation: 3 05-01-2024, 03:17 PM Dude, that's really cool Probably it would be possible to ... I'm not sure, just thinking out loud ... split up the movement of the flippers into tinier steps (just logically, while updating) to get better precision when they hit the ball. That way the flippers could move a lot faster without any loss of accuracy. I know it's just a test. But still, it would probably not be that hard to use this in combination with 'draw poly image' or 'draw image xform' to get textured graphics. I'll look into that when I get the time Well done! 1micha.elok Member Posts: 191 Threads: 20 Joined: Nov 2023 Reputation: 0 05-02-2024, 01:41 AM (This post was last modified: 05-02-2024, 01:42 AM by 1micha.elok.) (05-01-2024, 02:30 PM)kevin Wrote: ... an experiment with the terrific circle-line functions that Marcus provided a while back... Control the flippers with the A and D keys, and launch the ball using the UP arrow. It's awesome. Pinball game in N7, great ! Insert Coins to continue the game... « Next Oldest | Next Newest »