Lua开发Flappy Bird游戏异常:仅显部分图片且无交互
问题排查与修复方案
1. 初始游戏状态未触发进入"play"模式
你的gameState初始值是"start",但没有任何逻辑处理从start切换到play的触发(比如按空格或点击鼠标开始游戏),导致所有游戏逻辑(鸟的移动、管道生成)都不会执行,按键自然无响应。
修复:
在love.keypressed和love.mousepressed中添加start状态的处理:
-- 键盘输入修复 function love.keypressed(key) if key == "escape" then love.event.quit() elseif key == "space" then if gameState == "start" then gameState = "play" elseif gameState == "play" then bird.vy = JUMP_VELOCITY jumpSound:play() end elseif key == "r" and gameState == "over" then reset() end end -- 鼠标输入修复 function love.mousepressed(x, y, button) if button == 1 then if gameState == "start" then gameState = "play" elseif gameState == "play" then bird.vy = JUMP_VELOCITY jumpSound:play() elseif gameState == "over" then reset() end end end
2. 管道生成逻辑错误(空表访问)
初始时pipes是空表,pipes[#pipes]返回nil,访问nil.x会导致逻辑中断,永远不会生成管道。
修复:
修改管道生成的判断条件,先检查管道列表是否为空,为空则直接生成第一个管道:
-- Spawn new pipes if #pipes == 0 or pipes[#pipes].x < WINDOW_WIDTH - PIPE_GAP * 2 then -- 调整间距避免管道太密 local pipeY = lastPipeY + love.math.random(-80, 80) pipeY = math.max(0, math.min(pipeY, WINDOW_HEIGHT - PIPE_GAP)) -- 简化边界判断 table.insert(pipes, {x = WINDOW_WIDTH, y = pipeY, scored = false}) lastPipeY = pipeY end
3. 仅绘制了上管道,缺少下管道
你的代码只加载了pipe-up.png并绘制,但Flappy Bird需要上下一对管道,下管道需要翻转图片。
修复:
加载下管道图片(或翻转上管道),并在绘制时添加下管道:
-- 变量定义部分添加 local pipeDownImage = love.graphics.newImage("pipe-down.png") -- 或者用pipe-up翻转 -- 绘制管道部分修改 for _, pipe in ipairs(pipes) do love.graphics.draw(pipeImage, pipe.x, pipe.y) -- 绘制下管道:位置在上管道下方,间距为PIPE_GAP,翻转图片 love.graphics.draw(pipeDownImage, pipe.x, pipe.y + PIPE_GAP + pipeImage:getHeight(), 0, 1, -1) -- 如果没有pipe-down.png,用pipe-up翻转: -- love.graphics.draw(pipeImage, pipe.x, pipe.y + PIPE_GAP + pipeImage:getHeight(), 0, 1, -1) end
4. 背景偏左问题
背景图片尺寸可能与窗口尺寸(480x640)不匹配,导致显示偏移。
修复:
绘制背景时调整缩放,使其适配窗口:
love.graphics.draw(background, 0, 0, 0, WINDOW_WIDTH/background:getWidth(), WINDOW_HEIGHT/background:getHeight())
5. love.keypressed中错误的dt引用
love.keypressed回调没有dt参数,代码中bird.y = bird.y + bird.vy * dt会导致dt为nil,这行代码无效且可能触发隐性错误,需要移除。
修复:
直接删除这行错误代码,鸟的位置更新应该只在love.update中通过vy * dt处理,不需要在按键时额外修改。
6. 可选:添加开始界面提示
为了让玩家知道如何开始游戏,在love.draw中添加start状态的提示:
function love.draw() -- ... 原有绘制代码 ... -- Draw start message if gameState == "start" then love.graphics.print("Press Space or Click to Start", WINDOW_WIDTH / 2 - font:getWidth("Press Space or Click to Start") / 2, WINDOW_HEIGHT / 2 - font:getHeight() / 2) end -- ... 原有游戏结束提示代码 ... end
完整修复后的代码
把以上修复整合后,你的代码应该能正常运行:
-- Constants WINDOW_WIDTH = 480 WINDOW_HEIGHT = 640 GRAVITY = 900 JUMP_VELOCITY = -300 PIPE_GAP = 120 PIPE_SPEED = 60 -- Variables local bird local pipes = {} local lastPipeY = -PIPE_GAP / 2 local score = 0 local gameState = "start" local background = love.graphics.newImage("flappy background.png") local pipeImage = love.graphics.newImage("pipe-up.png") local pipeDownImage = love.graphics.newImage("pipe-down.png") -- 或使用pipe-up翻转 local birdImage = love.graphics.newImage("bird image.png") local font = love.graphics.newFont(50) -- Load assets function love.load() love.window.setTitle("Snappy Bird") love.window.setMode(WINDOW_WIDTH, WINDOW_HEIGHT) love.graphics.setFont(font) bird = { x = WINDOW_WIDTH / 2 - birdImage:getWidth() / 2, y = WINDOW_HEIGHT / 2 - birdImage:getHeight() / 2, vy = 0, image = birdImage } -- Load audio jumpSound = love.audio.newSource("jump sound.wav", "static") scoreSound = love.audio.newSource("scoring.wav", "static") gameOverSound = love.audio.newSource("game over.wav", "static") end -- Update game state function love.update(dt) if gameState == "play" then -- Update bird bird.vy = bird.vy + GRAVITY * dt bird.y = bird.y + bird.vy * dt -- Update pipes for i, pipe in ipairs(pipes) do pipe.x = pipe.x - PIPE_SPEED * dt -- Remove pipes that are off screen if pipe.x + pipeImage:getWidth() < 0 then table.remove(pipes, i) end -- Check for collision with both pipes if checkCollision(pipe.x, pipe.y, pipeImage:getWidth(), pipeImage:getHeight(), bird.x, bird.y, birdImage:getWidth(), birdImage:getHeight()) or checkCollision(pipe.x, pipe.y + PIPE_GAP + pipeImage:getHeight(), pipeImage:getWidth(), pipeImage:getHeight(), bird.x, bird.y, birdImage:getWidth(), birdImage:getHeight()) then gameState = "over" gameOverSound:play() end -- Check for score if pipe.x + pipeImage:getWidth() < bird.x and not pipe.scored then pipe.scored = true score = score + 1 scoreSound:play() end end -- Spawn new pipes if #pipes == 0 or pipes[#pipes].x < WINDOW_WIDTH - PIPE_GAP * 2 then local pipeY = lastPipeY + love.math.random(-80, 80) pipeY = math.max(0, math.min(pipeY, WINDOW_HEIGHT - PIPE_GAP)) table.insert(pipes, {x = WINDOW_WIDTH, y = pipeY, scored = false}) lastPipeY = pipeY end -- Check for game over if bird.y > WINDOW_HEIGHT or bird.y + birdImage:getHeight() < 0 then gameState = "over" gameOverSound:play() end end end -- Draw game elements function love.draw() love.graphics.draw(background, 0, 0, 0, WINDOW_WIDTH/background:getWidth(), WINDOW_HEIGHT/background:getHeight()) -- Draw pipes for _, pipe in ipairs(pipes) do love.graphics.draw(pipeImage, pipe.x, pipe.y) -- 绘制下管道,翻转图片 love.graphics.draw(pipeDownImage, pipe.x, pipe.y + PIPE_GAP + pipeImage:getHeight(), 0, 1, -1) -- 如果没有pipe-down.png,替换为: -- love.graphics.draw(pipeImage, pipe.x, pipe.y + PIPE_GAP + pipeImage:getHeight(), 0, 1, -1) end -- Draw bird love.graphics.draw(bird.image, bird.x, bird.y) -- Draw score love.graphics.print("Score: " .. score, 10, 10) -- Draw start message if gameState == "start" then love.graphics.print("Press Space or Click to Start", WINDOW_WIDTH / 2 - font:getWidth("Press Space or Click to Start") / 2, WINDOW_HEIGHT / 2 - font:getHeight() / 2) end -- Draw game over message if gameState == "over" then love.graphics.print("Game Over", WINDOW_WIDTH / 2 - font:getWidth("Game Over") / 2, WINDOW_HEIGHT / 2 - font:getHeight() / 2) love.graphics.print("Press R to restart", WINDOW_WIDTH / 2 - font:getWidth("Press R to restart") / 2, WINDOW_HEIGHT / 2 + font:getHeight() / 2) end end -- Handle keyboard input function love.keypressed(key) if key == "escape" then love.event.quit() elseif key == "space" then if gameState == "start" then gameState = "play" elseif gameState == "play" then bird.vy = JUMP_VELOCITY jumpSound:play() end elseif key == "r" and gameState == "over" then reset() end end -- Handle mouse input function love.mousepressed(x, y, button) if button == 1 then if gameState == "start" then gameState = "play" elseif gameState == "play" then bird.vy = JUMP_VELOCITY jumpSound:play() elseif gameState == "over" then reset() end end end -- Reset game state function reset() bird.y = WINDOW_HEIGHT / 2 - birdImage:getHeight() / 2 bird.vy = 0 pipes = {} lastPipeY = -PIPE_GAP / 2 score = 0 gameState = "play" end -- Check for collision function checkCollision(ax, ay, aw, ah, bx, by, bw, bh) return ax < bx + bw and ay < by + bh and bx < ax + aw and by < ay + ah end
内容的提问来源于stack exchange,提问作者Anomalyy
相关产品推荐
相关产品推荐

