How about a thread where we share small experiments that don’t quite need it into their own thread, or work in progress?
Pong
Like, shall we play a two-player game of Pong?
Video
Two players, first to reach 5 goals.
- Player 1, on
E1
. - Player 2, on
E3
. - Reset, on
K2
.
local game = { frame = 0, difficulty = 20 }
local viewport = { size = { w = 128, h = 64 } }
local ball = { pos = { x = 0, y = 0 }, dir = 0, angle = 1, speed = 0.5, size = 3, moving = false, owned = true }
local players = { a = { pos = 0, score = 0 }, b = { pos = 0, score = 0 } }
function init()
-- Render Style
screen.level(15)
screen.aa(0)
screen.line_width(1)
reset()
reset_score()
end
function start()
reset()
ball.moving = true
ball.dir = 1
end
function reset()
game.frame = 0
game.difficulty = 20
ball.pos = {x = viewport.size.w/2, y = viewport.size.h/2}
ball.angle = 0
ball.speed = 2
ball.moving = false
players.a.pos = (viewport.size.w/2) - (game.difficulty/2)
players.b.pos = (viewport.size.w/2) - (game.difficulty/2)
end
function reset_score()
players.a.score = 0
players.b.score = 0
end
function run()
-- Move ball
if ball.moving == true then
ball.pos.y = ball.pos.y + (ball.speed * ball.dir)
ball.pos.x = ball.pos.x + ball.angle
game.difficulty = clamp(20 - math.floor(game.frame/200),10,20)
game.frame = game.frame + 1
end
-- Collide with sides
if ball.pos.x > viewport.size.w - 2 or ball.pos.x < 1 then
ball.angle = ball.angle * -1
end
-- Collide with player a
if ball.pos.y < 2 and touch(players.a,ball.pos) then
collide(players.a,ball)
end
-- Collide with player b
if ball.pos.y > viewport.size.h - 3 and touch(players.b,ball.pos) then
collide(players.b,ball)
end
-- Goals
if ball.pos.y < -6 then
goal(players.b)
end
if ball.pos.y > viewport.size.h + 6 then
goal(players.a)
end
if players.a.score > 4 or players.b.score > 4 then
reset()
reset_score()
end
end
function touch(player,pos)
return pos.x > player.pos - ball.size and pos.x < player.pos + game.difficulty + ball.size
end
function collide(player,ball)
ball.dir = ball.dir * -1
ball.angle = clamp(math.floor((((ball.pos.x - (player.pos + (game.difficulty/2)))/4) * 0.25) * 100)/40,-0.8,0.8)
ball.speed = clamp(ball.speed + 0.05,0,2)
end
function goal(player)
player.score = player.score + 1
reset()
end
-- Gestures
function key(n,z)
if n == 2 and z == 1 then
reset()
reset_score()
end
end
function move(player,d)
if ball.moving == false then
start()
end
player.pos = clamp(player.pos + d,0,128 - game.difficulty)
end
function enc(n,d)
if n == 1 then
move(players.a,-d)
elseif n == 3 then
move(players.b,d)
end
end
-- Render
function draw_val(val,id)
local spacing = 15
local size = { w = 12, h = 4 }
local pos = { x = viewport.size.w - size.w, y = (viewport.size.h/2) - size.h - spacing + 4 }
if id == 1 then
pos.y = (viewport.size.h/2) - size.w + size.h + spacing + 6
end
if val == 0 then screen.move(pos.x, pos.y) ; screen.line(pos.x+size.w, pos.y) ; screen.line(pos.x+size.w, pos.y+size.h) ; screen.line(pos.x, pos.y+size.h) ; screen.line(pos.x, pos.y) ; screen.close() ; screen.stroke() end
if val == 1 then screen.move(pos.x+size.w, pos.y + size.h/2) ; screen.line(pos.x, pos.y+ size.h/2) ; screen.stroke() end
if val == 2 then screen.move(pos.x+size.w, pos.y-1) ; screen.line(pos.x+size.w, pos.y+ size.h) ; screen.line(pos.x+(size.w/2), pos.y+ size.h) ; screen.line(pos.x+(size.w/2), pos.y) ; screen.line(pos.x, pos.y) ; screen.line(pos.x, pos.y+size.h) ; screen.stroke() end
if val == 3 then screen.move(pos.x+size.w, pos.y-1); screen.line(pos.x+size.w, pos.y+ size.h); screen.line(pos.x, pos.y+size.h); screen.line(pos.x, pos.y-1); screen.move(pos.x+(size.w/2), pos.y+size.h); screen.line(pos.x+(size.w/2), pos.y); screen.stroke() end
if val == 4 then screen.move(pos.x+size.w, pos.y) ; screen.line(pos.x+(size.w/2), pos.y) ; screen.line(pos.x+(size.w/2), pos.y+size.h) ; screen.move(pos.x+size.w,pos.y+size.h) ; screen.line(pos.x-1,pos.y+size.h) ; screen.stroke() end
end
function redraw()
screen.clear()
-- Score
screen.level(1)
draw_val(players.a.score,0)
draw_val(players.b.score,1)
-- Court
for i = viewport.size.w,1,-1 do
if i % 2 == 1 then
screen.pixel(math.floor(i),(viewport.size.h/2))
end
end
screen.fill()
screen.level(15)
-- Player A
screen.move(players.a.pos, 1)
screen.line(players.a.pos + game.difficulty, 1)
-- Player B
screen.move(players.b.pos, viewport.size.h)
screen.line(players.b.pos + game.difficulty, viewport.size.h)
screen.stroke()
-- Ball
local x = ball.pos.x
local y = ball.pos.y
screen.pixel(x, y)
screen.fill()
screen.update()
end
-- Utils
function clamp(val,min,max)
return val < min and min or val > max and max or val
end
function update(sec)
run()
redraw()
end
function midi_to_hz(note)
return (440 / 32) * (2 ^ ((note - 9) / 12))
end
-- Interval
re = metro.init()
re.time = 1.0 / 15
re.event = function()
update()
end
re:start()