Thread Rating:
  • 1 Vote(s) - 4 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Small pinball game using circle-line collision routines from Marcus
#1
This is not meant to be a proper simulation (as you will see if you run it Smile), 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
Reply


Messages In This Thread
Small pinball game using circle-line collision routines from Marcus - by kevin - 05-01-2024, 02:30 PM

Forum Jump:


Users browsing this thread: 1 Guest(s)