Small pinball game using circle-line collision routines from Marcus - kevin - 05-01-2024
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
RE: Small pinball game using circle-line collision routines from Marcus - Marcus - 05-01-2024
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!
RE: Small pinball game using circle-line collision routines from Marcus - 1micha.elok - 05-02-2024
(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...
|