feat: add 6 lua games for basic1 console
- Pong: 2-player paddle and ball game with spin mechanics - Flappy Bird: gravity physics, obstacle avoidance - Breakout: paddle control, brick grid, collision detection - Simon Says: sequence memory, animation timing - Memory Match: pair matching, flip animations, grid layout - Tetris: falling blocks, grid system, line clearing - Asteroids: vector math, rotation, projectiles, enemy spawning All games follow API conventions with state machines, touch input, frame-based animation, and persistent game.vars state management.
This commit is contained in:
168
games/lua_examples/flappy_bird.lua
Normal file
168
games/lua_examples/flappy_bird.lua
Normal file
@@ -0,0 +1,168 @@
|
||||
-- NAME: Flappy Bird
|
||||
-- DESC: Tap to flap, avoid pipes
|
||||
|
||||
-- Game states
|
||||
local STATE_MENU = 0
|
||||
local STATE_PLAYING = 1
|
||||
local STATE_GAME_OVER = 2
|
||||
|
||||
-- Game constants
|
||||
local BIRD_SIZE = 8
|
||||
local BIRD_GRAVITY = 0.3
|
||||
local BIRD_FLAP_POWER = -7
|
||||
local PIPE_WIDTH = 20
|
||||
local PIPE_GAP = 50
|
||||
local PIPE_SPEED = 3
|
||||
local SPAWN_RATE = 80 -- Frames between pipe spawns
|
||||
|
||||
function init()
|
||||
game.vars.state = STATE_MENU
|
||||
game.vars.score = 0
|
||||
game.vars.frame_count = 0
|
||||
|
||||
-- Bird
|
||||
game.vars.bird_y = game.height() / 2
|
||||
game.vars.bird_vel = 0
|
||||
|
||||
-- Pipes (array of {x, gap_y})
|
||||
game.vars.pipes = {}
|
||||
game.vars.last_pipe_frame = 0
|
||||
|
||||
-- Enable continuous updates
|
||||
game.set_frame_updates(true)
|
||||
|
||||
print("Flappy Bird initialized")
|
||||
end
|
||||
|
||||
function update(event)
|
||||
local state = game.vars.state
|
||||
|
||||
if state == STATE_MENU then
|
||||
if event.type == INPUT.TOUCH_DOWN or event.type == INPUT.BUTTON_0 or event.type == INPUT.BUTTON_1 then
|
||||
reset_game()
|
||||
game.vars.state = STATE_PLAYING
|
||||
return true
|
||||
end
|
||||
|
||||
elseif state == STATE_PLAYING then
|
||||
-- Handle flap input
|
||||
if event.type == INPUT.TOUCH_DOWN or event.type == INPUT.BUTTON_0 or event.type == INPUT.BUTTON_1 then
|
||||
game.vars.bird_vel = BIRD_FLAP_POWER
|
||||
end
|
||||
|
||||
-- Update physics on frame tick
|
||||
if event.type == INPUT.FRAME_TICK then
|
||||
update_bird()
|
||||
update_pipes()
|
||||
check_collisions()
|
||||
return true
|
||||
end
|
||||
|
||||
elseif state == STATE_GAME_OVER then
|
||||
if event.type == INPUT.TOUCH_DOWN or event.type == INPUT.BUTTON_0 or event.type == INPUT.BUTTON_1 then
|
||||
game.vars.state = STATE_MENU
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
function draw()
|
||||
renderer.clear(false) -- Black background
|
||||
|
||||
local state = game.vars.state
|
||||
|
||||
if state == STATE_MENU then
|
||||
renderer.text(game.width() / 2 - 40, game.height() / 2 - 30, "FLAPPY BIRD", true)
|
||||
renderer.text(game.width() / 2 - 50, game.height() / 2, "Tap to Start", true)
|
||||
|
||||
elseif state == STATE_PLAYING then
|
||||
-- Draw bird
|
||||
renderer.circle(20, game.vars.bird_y, BIRD_SIZE, true, true)
|
||||
|
||||
-- Draw pipes
|
||||
for i = 1, #game.vars.pipes do
|
||||
local pipe = game.vars.pipes[i]
|
||||
|
||||
-- Top pipe
|
||||
renderer.rect(pipe.x, 0, PIPE_WIDTH, pipe.gap_y, true, true)
|
||||
|
||||
-- Bottom pipe
|
||||
local bottom_start = pipe.gap_y + PIPE_GAP
|
||||
renderer.rect(pipe.x, bottom_start, PIPE_WIDTH, game.height() - bottom_start, true, true)
|
||||
end
|
||||
|
||||
-- Draw score
|
||||
renderer.text(10, 10, "Score: " .. tostring(game.vars.score), true)
|
||||
|
||||
elseif state == STATE_GAME_OVER then
|
||||
renderer.text(game.width() / 2 - 40, game.height() / 2 - 30, "GAME OVER", true)
|
||||
renderer.text(game.width() / 2 - 30, game.height() / 2, "Score: " .. tostring(game.vars.score), true)
|
||||
renderer.text(game.width() / 2 - 60, game.height() / 2 + 20, "Tap to Restart", true)
|
||||
end
|
||||
end
|
||||
|
||||
function update_bird()
|
||||
-- Apply gravity
|
||||
game.vars.bird_vel = game.vars.bird_vel + BIRD_GRAVITY
|
||||
game.vars.bird_y = game.vars.bird_y + game.vars.bird_vel
|
||||
|
||||
-- Clamp to screen (game over if hit top/bottom)
|
||||
if game.vars.bird_y - BIRD_SIZE < 0 or game.vars.bird_y + BIRD_SIZE > game.height() then
|
||||
game.vars.state = STATE_GAME_OVER
|
||||
end
|
||||
end
|
||||
|
||||
function update_pipes()
|
||||
game.vars.frame_count = game.vars.frame_count + 1
|
||||
|
||||
-- Spawn new pipe
|
||||
if game.vars.frame_count - game.vars.last_pipe_frame >= SPAWN_RATE then
|
||||
local gap_y = math.random(30, game.height() - PIPE_GAP - 30)
|
||||
table.insert(game.vars.pipes, {x = game.width(), gap_y = gap_y})
|
||||
game.vars.last_pipe_frame = game.vars.frame_count
|
||||
end
|
||||
|
||||
-- Move and remove off-screen pipes
|
||||
for i = #game.vars.pipes, 1, -1 do
|
||||
local pipe = game.vars.pipes[i]
|
||||
pipe.x = pipe.x - PIPE_SPEED
|
||||
|
||||
-- Score when pipe passes bird
|
||||
if pipe.x == 20 then
|
||||
game.vars.score = game.vars.score + 1
|
||||
end
|
||||
|
||||
-- Remove if off-screen
|
||||
if pipe.x < -PIPE_WIDTH then
|
||||
table.remove(game.vars.pipes, i)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function check_collisions()
|
||||
-- Check collision with pipes
|
||||
for i = 1, #game.vars.pipes do
|
||||
local pipe = game.vars.pipes[i]
|
||||
|
||||
-- Bird hitbox: circle at (20, bird_y) with radius BIRD_SIZE
|
||||
-- Check if within pipe's X range
|
||||
if 20 + BIRD_SIZE > pipe.x and 20 - BIRD_SIZE < pipe.x + PIPE_WIDTH then
|
||||
-- Check Y collision
|
||||
if game.vars.bird_y - BIRD_SIZE < pipe.gap_y or
|
||||
game.vars.bird_y + BIRD_SIZE > pipe.gap_y + PIPE_GAP then
|
||||
game.vars.state = STATE_GAME_OVER
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function reset_game()
|
||||
game.vars.score = 0
|
||||
game.vars.bird_y = game.height() / 2
|
||||
game.vars.bird_vel = 0
|
||||
game.vars.pipes = {}
|
||||
game.vars.frame_count = 0
|
||||
game.vars.last_pipe_frame = 0
|
||||
end
|
||||
Reference in New Issue
Block a user