Build a Platformer with Love2D

Polish & Next Steps

15 min+50 XP

Polish & Next Steps

Congratulations! You've built a complete platformer game. Let's add some final polish and discuss where to go from here.

Adding Polish

Small details make games feel better:

  • Screen shake on landing
  • Particles when collecting coins
  • Smooth camera following the player
  • Sound effects and music

Here's our final, polished version:

lua
function love.load() player = { x = 50, y = 100, width = 30, height = 40, speed = 200, velocityY = 0, gravity = 800, jumpForce = -380, onGround = false } platforms = { {x = 0, y = 250, width = 400, height = 50}, {x = 80, y = 190, width = 90, height = 15}, {x = 220, y = 140, width = 100, height = 15}, {x = 30, y = 90, width = 80, height = 15}, {x = 300, y = 60, width = 90, height = 15}, } coins = { {x = 120, y = 165, radius = 8, collected = false}, {x = 260, y = 115, radius = 8, collected = false}, {x = 60, y = 65, radius = 8, collected = false}, {x = 340, y = 35, radius = 8, collected = false}, {x = 350, y = 225, radius = 8, collected = false}, } particles = {} score = 0 totalCoins = #coins gameWon = false shake = 0 time = 0 end function love.update(dt) time = time + dt shake = math.max(0, shake - dt * 10) -- Update particles for i = #particles, 1, -1 do local p = particles[i] p.x = p.x + p.vx * dt p.y = p.y + p.vy * dt p.life = p.life - dt if p.life <= 0 then table.remove(particles, i) end end if gameWon then return end -- Movement if love.keyboard.isDown("left") or love.keyboard.isDown("a") then player.x = player.x - player.speed * dt end if love.keyboard.isDown("right") or love.keyboard.isDown("d") then player.x = player.x + player.speed * dt end -- Gravity player.velocityY = player.velocityY + player.gravity * dt player.y = player.y + player.velocityY * dt local wasOnGround = player.onGround player.onGround = false -- Platform collisions for i, plat in ipairs(platforms) do if checkCollision(player, plat) then local overlapTop = (player.y + player.height) - plat.y if overlapTop > 0 and overlapTop < 20 and player.velocityY > 0 then player.y = plat.y - player.height if player.velocityY > 300 then shake = 0.2 end player.velocityY = 0 player.onGround = true end end end -- Coin collection local collected = 0 for i, coin in ipairs(coins) do if not coin.collected then if checkCoinCollision(player, coin) then coin.collected = true score = score + 10 spawnParticles(coin.x, coin.y) end end if coin.collected then collected = collected + 1 end end if collected == totalCoins then gameWon = true end if player.x < 0 then player.x = 0 end if player.x + player.width > 400 then player.x = 400 - player.width end end function spawnParticles(x, y) for i = 1, 8 do local angle = (i / 8) * math.pi * 2 table.insert(particles, { x = x, y = y, vx = math.cos(angle) * 100, vy = math.sin(angle) * 100, life = 0.5 }) end end function checkCollision(a, b) return a.x < b.x + b.width and a.x + a.width > b.x and a.y < b.y + b.height and a.y + a.height > b.y end function checkCoinCollision(player, coin) local closestX = math.max(player.x, math.min(coin.x, player.x + player.width)) local closestY = math.max(player.y, math.min(coin.y, player.y + player.height)) local distX, distY = coin.x - closestX, coin.y - closestY return (distX * distX + distY * distY) < (coin.radius * coin.radius) end function love.keypressed(key) if gameWon and key == "r" then love.load(); return end if (key == "space" or key == "w" or key == "up") and player.onGround then player.velocityY = player.jumpForce end end function love.draw() -- Screen shake local ox = (math.random() - 0.5) * shake * 10 local oy = (math.random() - 0.5) * shake * 10 love.graphics.push() love.graphics.translate(ox, oy) -- Sky gradient effect love.graphics.setColor(0.3, 0.6, 0.9) love.graphics.rectangle("fill", 0, 0, 400, 150) love.graphics.setColor(0.5, 0.8, 1) love.graphics.rectangle("fill", 0, 150, 400, 150) -- Platforms with shadow for i, plat in ipairs(platforms) do love.graphics.setColor(0.2, 0.5, 0.2) love.graphics.rectangle("fill", plat.x + 3, plat.y + 3, plat.width, plat.height) love.graphics.setColor(0.4, 0.8, 0.4) love.graphics.rectangle("fill", plat.x, plat.y, plat.width, plat.height) end -- Particles love.graphics.setColor(1, 0.9, 0.3) for i, p in ipairs(particles) do love.graphics.circle("fill", p.x, p.y, 4 * p.life) end -- Coins with animation for i, coin in ipairs(coins) do if not coin.collected then local bob = math.sin(time * 4 + i) * 2 love.graphics.setColor(1, 0.8, 0) love.graphics.circle("fill", coin.x, coin.y + bob, coin.radius) love.graphics.setColor(1, 1, 0.6) love.graphics.circle("fill", coin.x - 2, coin.y + bob - 2, 3) end end -- Player with shadow love.graphics.setColor(0.3, 0.1, 0.1) love.graphics.rectangle("fill", player.x + 2, player.y + 2, player.width, player.height) love.graphics.setColor(1, 0.3, 0.3) love.graphics.rectangle("fill", player.x, player.y, player.width, player.height) love.graphics.setColor(1, 1, 1) love.graphics.circle("fill", player.x + 10, player.y + 12, 5) love.graphics.circle("fill", player.x + 22, player.y + 12, 5) love.graphics.setColor(0, 0, 0) love.graphics.circle("fill", player.x + 12, player.y + 12, 2) love.graphics.circle("fill", player.x + 24, player.y + 12, 2) love.graphics.pop() -- UI love.graphics.setColor(0, 0, 0, 0.6) love.graphics.rectangle("fill", 5, 5, 130, 25, 5, 5) love.graphics.setColor(1, 1, 1) love.graphics.print("Score: " .. score .. " Coins: " .. score/10 .. "/" .. totalCoins, 12, 10) if gameWon then love.graphics.setColor(0, 0, 0, 0.8) love.graphics.rectangle("fill", 0, 0, 400, 300) love.graphics.setColor(1, 0.8, 0) love.graphics.printf("CONGRATULATIONS!", 0, 80, 400, "center") love.graphics.setColor(1, 1, 1) love.graphics.printf("You collected all coins!", 0, 120, 400, "center") love.graphics.printf("Final Score: " .. score, 0, 150, 400, "center") love.graphics.setColor(0.7, 0.7, 0.7) love.graphics.printf("Press R to play again", 0, 200, 400, "center") end end

What You've Learned

  • The Love2D game loop (load, update, draw)
  • Drawing shapes and colors
  • Reading keyboard input
  • Implementing physics (gravity, velocity)
  • Collision detection (AABB)
  • Game state management
  • Adding polish (particles, screen shake, animations)

Next Steps

You've completed your first game! Here are some ideas to continue learning:

  1. Add enemies - Create moving obstacles
  2. Multiple levels - Load level data from tables
  3. Animations - Use sprite sheets for animated characters
  4. Sound effects - Use love.audio for audio
  5. Save progress - Use love.filesystem to save high scores

Keep building! The best way to learn game development is by making games.

Ready to experiment?

Open the sandbox to modify and run the code yourself.