-- 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 (convert to integer) renderer.circle(20, math.floor(game.vars.bird_y + 0.5), 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