If things gone wrong, try changing MIN_DT (physics loop time).
Code: Select all
'=== Atom breakout ===
const SCRN_W = 800, SCRN_H = 600
const as double MIN_DT = 2.0 / 1000 's
const as string KEY_ESC = chr(27)
'-------------------------------------------------------------------------------
type int2d
as integer x, y
declare operator cast () as string
end type
operator =(a as int2d, b as int2d) as boolean
if a.x <> b.x then return false
if a.y <> b.y then return false
return true
end operator
operator <>(a as int2d, b as int2d) as boolean
if a.x = b.x and a.y = b.y then return false
return true
end operator
' a - b
operator - (a as int2d, b as int2d) as int2d
return type(a.x - b.x, a.y - b.y)
end operator
' -a
operator - (a as int2d) as int2d
return type(-a.x, -a.y)
end operator
'-------------------------------------------------------------------------------
type sgl2d
as single x, y
declare operator cast () as string
end type
' a + b
operator + (a as sgl2d, b as sgl2d) as sgl2d
return type(a.x + b.x, a.y + b.y)
end operator
' a - b
operator - (a as sgl2d, b as sgl2d) as sgl2d
return type(a.x - b.x, a.y - b.y)
end operator
' a * mul
operator * (a as sgl2d, mul as single) as sgl2d
return type(a.x * mul, a.y * mul)
end operator
' a / div
operator / (a as sgl2d, div as single) as sgl2d
return type(a.x / div, a.y / div)
end operator
' distance / lenth
operator len (a as sgl2d) as single
return sqr(a.x * a.x + a.y * a.y)
end operator
'-------------------------------------------------------------------------------
type loop_timer_type
'private:
dim as double tNow
dim as double tPrev
dim as double dt
dim as double dtAvg
public:
declare sub init()
declare sub update()
end type
sub loop_timer_type.init()
tNow = timer
tPrev = tNow
dt = 0.0
dtAvg = 0.0
end sub
sub loop_timer_type.update()
tPrev = tNow
tNow = timer
dt = tNow - tPrev
dtAvg = 0.95 * dtAvg + 0.05 * dt
end sub
'-------------------------------------------------------------------------------
#DEFINE MOUSE_IDLE 0
#DEFINE MOUSE_POS_CHANGED 1
#DEFINE MOUSE_LB_PRESSED 2
#DEFINE MOUSE_LB_RELEASED 3
#DEFINE MOUSE_RB_PRESSED 4
#DEFINE MOUSE_RB_RELEASED 5
#DEFINE MOUSE_MB_PRESSED 6
#DEFINE MOUSE_MB_RELEASED 7
#DEFINE MOUSE_WHEEL_UP 8
#DEFINE MOUSE_WHEEL_DOWN 9
type mouseType
pos as int2d
posChange as int2d
wheel as integer
buttons as integer
lb as integer 'left button
rb as integer 'right button
mb as integer 'middle button
end type
function handleMouse(byref mouse as mouseType) as integer
static previous as mouseType
dim as integer change = MOUSE_IDLE
getmouse mouse.pos.x, mouse.pos.y, mouse.wheel, mouse.buttons
if (mouse.buttons = -1) then
mouse.lb = 0
mouse.rb = 0
mouse.mb = 0
mouse.posChange.x = 0
mouse.posChange.y = 0
else
mouse.lb = (mouse.buttons and 1)
mouse.rb = (mouse.buttons shr 1) and 1
mouse.mb = (mouse.buttons shr 2) and 1
'if (previous.pos.x <> mouse.pos.x or previous.pos.y <> mouse.pos.y) then
if previous.pos <> mouse.pos then
change = MOUSE_POS_CHANGED
end if
'mouse.posChange.x = mouse.pos.x - previous.pos.x
'mouse.posChange.y = mouse.pos.y - previous.pos.y
mouse.posChange = mouse.pos - previous.pos
if (previous.buttons <> mouse.buttons) then
if (previous.lb = 0 and mouse.lb = 1) then change = MOUSE_LB_PRESSED
if (previous.lb = 1 and mouse.lb = 0) then change = MOUSE_LB_RELEASED
if (previous.rb = 0 and mouse.rb = 1) then change = MOUSE_RB_PRESSED
if (previous.rb = 1 and mouse.rb = 0) then change = MOUSE_RB_RELEASED
if (previous.mb = 0 and mouse.mb = 1) then change = MOUSE_MB_PRESSED
if (previous.mb = 1 and mouse.mb = 0) then change = MOUSE_MB_RELEASED
end if
if (mouse.wheel > previous.wheel) then change = MOUSE_WHEEl_UP
if (mouse.wheel < previous.wheel) then change = MOUSE_WHEEl_DOWN
previous = mouse
end if
return change
end function
'-------------------------------------------------------------------------------
sub setScreen(w as integer, h as integer)
screenres w, h, 32
width w \ 8, h \ 16
end sub
sub clearScreen(c as ulong)
line(0, 0)-(SCRN_W - 1, SCRN_H - 1), c, bf
end sub
function toInt(p as sgl2d) as int2d
return type<int2d>(p.x, p.y)
end function
function toSgl(p as int2d) as sgl2d
return type<sgl2d>(p.x, p.y)
end function
function SgnSqr(a as single) as single
return sgn(a) * a * a
end function
'-------------------------------------------------------------------------------
type ball_type
dim as sgl2d p 'position
dim as sgl2d v 'velocity
dim as sgl2d F 'force [N]
dim as single r = 15 'raduis [px]
dim as single kc = 1000 'compression spring constant [N/px]
dim as single ka = 1 'attraction spring constant [N/px]
dim as single m = 1.0 'mass [kg]
dim as single b = 0.5 'Linear drag coexficient [N/(px/s)]
dim as single intF, intFmax = -1
dim as single xMin = 0, xMax = SCRN_W-1
dim as single yMin = 0, yMax = SCRN_H-1
dim as ulong colour
dim as ulong active = 1
dim as sgl2d pt 'target pos
declare sub init(p as sgl2d, r as single, kc as single, ka as single, m as single, b as single, c as ulong)
declare sub boundColl() 'wall / boundary collisions
declare sub setTarget(targetPos as sgl2d)
declare sub update(dt as double)
declare sub draw_()
end type
sub ball_type.init(p as sgl2d, r as single, kc as single, ka as single, m as single, b as single, c as ulong)
this.p = p
this.r = r
this.kc = kc
this.ka = ka
this.m = m
this.b = b
this.colour = c
end sub
sub ball_type.boundColl()
dim as single edgeDist
edgeDist = (p.x - r) - xMin
if (edgeDist < 0) then F.x -= kc * edgeDist
edgeDist = (p.y - r) - yMin
if (edgeDist < 0) then F.y -= kc * edgeDist
edgeDist = xMax - (p.x + r)
if (edgeDist < 0) then F.x += kc * edgeDist
edgeDist = yMax - (p.y + r)
if (edgeDist < 0) then F.y += kc * edgeDist
end sub
sub ball_type.setTarget(targetPos as sgl2d)
pt = targetPos
end sub
sub ball_type.update(dt as double)
dim as sgl2d a 'acceleration
dim as sgl2d dp = pt - p 'delta position
F += dp * ka 'F = k * x
if intFmax > 0 then
intF += len(F) * dt
if intF > intFmax then active = 0
end if
F -= v * b 'drag
a = F / m 'F = m * a -> a = F / m
v += a * dt
'if len(v) > vMax then v *= (vMax / len(v))
p += v * dt
F = type(0, 0) 'reset for next run
end sub
sub ball_type.draw_()
if active = 1 then
circle (p.x, p.y), r, colour,,,,f
if intFmax > 0 then
dim as integer damage = 9 - int(10 * (intF / intFmax))
draw string (p.x - 3, p.y - 7), str(damage), rgb(255, 255, 0)
end if
end if
end sub
'-------------------------------------------------------------------------------
sub ballColl(byref b1 as ball_type, byref b2 as ball_type)
if (b1.active = 1) and (b2.active = 1) then
dim as single dx = b1.p.x - b2.p.x
dim as single dy = b1.p.y - b2.p.y
dim as single cntrDist = sqr(dx * dx + dy * dy)
dim as single edgeDist = cntrDist - (b1.r + b2.r)
if(edgeDist < 0) then
dim as single factor = edgeDist / cntrDist
dim as single F1 = b1.kc * factor
b1.F.x -= F1 * dx
b1.F.y -= F1 * dy
dim as single F2 = b2.kc * factor
b2.F.x += F2 * dx
b2.F.y += F2 * dy
end if
end if
end sub
'-------------------------------------------------------------------------------
const BRICK_X_NUM = 12, BRICK_Y_NUM = 6
dim as string key
dim as integer quit
dim as mouseType mouse
dim as ball_type paddle
dim as ball_type ball
dim as ball_type brick(BRICK_X_NUM - 1, BRICK_Y_NUM - 1)
dim as loop_timer_type loopTimer
dim as int2d lastValidMousePos = type(SCRN_W \ 2, 0.9 * SCRN_H)
dim as integer yMinMouse = 0.8 * SCRN_H
dim as integer xi, yi, x, y
dim as integer numSteps, iStep
dim as double dtStep 's
setScreen(SCRN_W, SCRN_H)
paddle.init(type(0.5 * SCRN_W, 0.9 * SCRN_H), 30, 2000, 1000, 0.1, 10.0, rgb(127, 0, 0))
ball.init(type(0.5 * SCRN_W, 0.6 * SCRN_H), 10, 1000, 0.5, 1, 0.25, rgb(0, 127, 0))
ball.yMax = SCRN_H * 2
for yi = 0 to BRICK_Y_NUM - 1
y = 0.05 * SCRN_H + 0.5 * SCRN_H * ((0.5 + yi) / BRICK_Y_NUM)
for xi = 0 to BRICK_X_NUM - 1
x = 0.1 * SCRN_W + 0.8 * SCRN_W * ((0.5 + xi) / BRICK_X_NUM)
brick(xi, yi).init(type(x, y), 15, 1000, 1000, 0.2, 5.0, rgb(127, 127, 0))
brick(xi, yi).setTarget(type(x, y))
brick(xi, yi).intFmax = 1e3
next
next
loopTimer.init()
while quit = 0
key = inkey
select case key
case KEY_ESC : quit = 1
end select
handleMouse(mouse)
if mouse.buttons <> -1 then
lastValidMousePos = mouse.pos
if lastValidMousePos.y < yMinMouse then lastValidMousePos.y = yMinMouse
end if
'take smaller time steps for updates
numSteps = int(loopTimer.dt / MIN_DT) + 1 'ceiling, at least 1 step
dtStep = loopTimer.dt / numSteps
for iStep = 0 to numSteps - 1
'check inter-collisions
ballColl(ball, paddle)
for yi = 0 to BRICK_Y_NUM - 1
for xi = 0 to BRICK_X_NUM - 1
ballColl(ball, brick(xi, yi))
next
next
'paddle / bat
paddle.boundColl()
paddle.setTarget(toSgl(lastValidMousePos))
paddle.update(dtStep)
'ball / bullet
ball.boundColl()
ball.setTarget(paddle.p)
ball.update(dtStep)
'update bricks
for yi = 0 to BRICK_Y_NUM - 1
for xi = 0 to BRICK_X_NUM - 1
brick(xi, yi).update(dtStep)
next
next
next
if ball.p.y > SCRN_H then quit = 1
screenlock
line(0, 0)-(SCRN_W - 1, 0.8 * SCRN_H - 1), &h000000, bf
line(0, 0.8 * SCRN_H)-(SCRN_W - 1, SCRN_H - 1), &h202020, bf
for yi = 0 to BRICK_Y_NUM - 1
for xi = 0 to BRICK_X_NUM - 1
brick(xi, yi).draw_()
next
next
paddle.draw_()
ball.draw_()
locate 1, 1 : print "Use mouse, <esc> to exit";
screenunlock
sleep 15
loopTimer.update()
wend
draw string (0.5 * SCRN_W - 36, 0.65 * SCRN_H), "GAME OVER", &he0e0e0
while inkey = "" : wend
'Todo: Add spin