Voltar para o Blog
Quest Log

Como Criar um Jogo com LÖVE (Love2D) e Lua

Tela de um jogo 2D simples com um quadrado azul sendo controlado pelo teclado sobre fundo escuro

Aprenda a criar um jogo com LÖVE (Love2D) e a linguagem Lua: instalar o framework, os callbacks load, update e draw, mover um jogador e distribuir o jogo.

Se você quer aprender a programar jogos escrevendo código de verdade, criar um jogo com LÖVE (o Love2D) é um dos caminhos mais diretos que existem. O LÖVE é um framework 2D grátis e de código aberto que usa a linguagem Lua, uma das linguagens mais simples de aprender. Não tem editor visual, não tem SDK gigante, não tem assinatura. Você instala, cria um arquivo, escreve algumas linhas e já tem uma janela com um jogo rodando. Neste guia você vai montar um projeto do zero, entender como o LÖVE organiza um jogo e sair com um jogador se movendo na tela e coletando pontos.

Vou ser honesto logo de cara sobre o que o LÖVE é, porque isso evita frustração. Ele é excelente para jogo 2D e para aprender lógica de programação, justamente porque te obriga a entender o loop do jogo em vez de esconder tudo atrás de botões. Em compensação, é só código: não existe interface gráfica para arrastar objetos para a cena. Para um primeiro jogo e para aprender a pensar como programador, isso é uma vantagem. Para projetos grandes com muitos recursos prontos ou qualquer coisa em 3D, uma engine com editor tende a poupar trabalho, e eu volto nesse ponto no fim.

Por que aprender a criar um jogo com LÖVE (Love2D)

O LÖVE cuida da parte chata de um jogo para você: abrir a janela, rodar o loop principal que redesenha a tela dezenas de vezes por segundo, ler o teclado, tocar som e desenhar imagens e formas. Você escreve a lógica do seu jogo em Lua e o LÖVE executa isso quadro a quadro. Como é um framework e não uma engine visual, tudo que aparece na tela apareceu porque você escreveu uma linha mandando aparecer. Parece mais trabalho, e é, mas é exatamente isso que faz você entender de verdade como um jogo funciona por dentro.

A linguagem Lua ajuda muito nessa jornada. Ela é pequena, tem poucas palavras-chave e uma sintaxe limpa, sem ponto e vírgula obrigatório nem chaves para blocos. Você aprende o essencial de programação (variáveis, funções, condições, laços e tabelas) sem se afogar em cerimônia. Se você ainda está decidindo por onde entrar no mundo do código, vale ler qual linguagem aprender para fazer jogos antes de fechar em Lua, para ver onde ela se encaixa perto de opções como C#, GDScript e JavaScript.

Instalando o LÖVE e criando o main.lua

O primeiro passo é baixar o LÖVE no site oficial, o love2d.org, e instalar. Ele é leve e roda em Windows, macOS, Linux e até Android. Depois de instalado, crie uma pasta para o seu jogo em qualquer lugar do computador. Dentro dela, crie um arquivo de texto chamado main.lua. Esse nome é obrigatório: é o arquivo que o LÖVE procura para começar a rodar.

Abra o main.lua no seu editor de texto favorito e escreva este primeiro teste, só para confirmar que tudo funciona:

function love.load()
    mensagem = "Meu primeiro jogo em LÖVE"
end

function love.draw()
    love.graphics.print(mensagem, 300, 280)
end

Aqui já aparecem dois dos três callbacks centrais do LÖVE. A função love.load() roda uma única vez, no início, e serve para preparar as coisas. A função love.draw() roda a cada quadro e é onde você desenha na tela. O love.graphics.print escreve um texto na posição x igual a 300 e y igual a 280, contando a partir do canto superior esquerdo da janela.

Para rodar, você aponta o executável do LÖVE para a pasta que contém o main.lua. No Windows, uma forma prática é arrastar a pasta inteira para cima do atalho do LÖVE. Pelo terminal, você chama o executável passando o caminho da pasta como argumento. Em qualquer caso, o alvo é a pasta, não o arquivo solto. Se aparecer uma janela com a sua frase, está tudo certo e você já pode partir para o jogo de verdade.

Os três callbacks e o delta time

Antes de mover qualquer coisa, precisa entender o coração do LÖVE, que são três funções que ele chama para você automaticamente:

  • love.load(): chamada uma vez no início. Aqui você cria variáveis, define posições iniciais e carrega imagens.
  • love.update(dt): chamada a cada quadro, antes de desenhar. Aqui fica a lógica: ler teclado, mover objetos, checar colisão, contar pontos. O dt é o delta time, o tempo em segundos desde o quadro anterior.
  • love.draw(): chamada a cada quadro, depois do update. Aqui você desenha tudo. Nada de lógica pesada aqui, só desenho.

O detalhe mais importante para acertar cedo é o dt. Computadores diferentes rodam o jogo em taxas de quadros diferentes. Se você mover o jogador somando um valor fixo por quadro, ele anda mais rápido numa máquina potente e mais devagar numa fraca. A solução é sempre multiplicar a velocidade pelo dt. Assim o movimento fica em unidades por segundo e igual em qualquer máquina. Guarde essa regra: velocidade vezes dt, sempre.

Um jogador que se move com o teclado

Agora o jogo de verdade. Vamos criar um quadrado que é o jogador, movê-lo com as setas e mostrar as coordenadas na tela. Substitua o conteúdo do main.lua por isto:

function love.load()
    jogador = {
        x = 400,
        y = 300,
        tamanho = 40,
        velocidade = 250
    }
end

function love.update(dt)
    if love.keyboard.isDown("right") then
        jogador.x = jogador.x + jogador.velocidade * dt
    end
    if love.keyboard.isDown("left") then
        jogador.x = jogador.x - jogador.velocidade * dt
    end
    if love.keyboard.isDown("down") then
        jogador.y = jogador.y + jogador.velocidade * dt
    end
    if love.keyboard.isDown("up") then
        jogador.y = jogador.y - jogador.velocidade * dt
    end
end

function love.draw()
    love.graphics.setColor(0.3, 0.6, 1)
    love.graphics.rectangle("fill", jogador.x, jogador.y, jogador.tamanho, jogador.tamanho)
end

Rode de novo e você já tem um quadrado azul que anda com as setas. Vamos ver o que cada parte faz. No love.load, criamos o jogador como uma tabela, que é a estrutura de dados do Lua para agrupar valores relacionados. Dentro dela guardamos posição, tamanho e velocidade.

No love.update, a função love.keyboard.isDown devolve verdadeiro enquanto a tecla está pressionada. Repare que cada movimento multiplica jogador.velocidade pelo dt, exatamente a regra da seção anterior. Com velocidade 250, o jogador anda 250 pixels por segundo, não importa a máquina.

No love.draw, o love.graphics.setColor define a cor em valores de 0 a 1 para vermelho, verde e azul. Depois, love.graphics.rectangle com o modo "fill" desenha um retângulo preenchido na posição e tamanho do jogador. Se você usar "line" no lugar de "fill", ele desenha só o contorno.

Próximo nível
Quer aprender isso na prática?

No CursoGame.Dev você sai dos tutoriais soltos e constrói jogos publicáveis, com trilha progressiva, quests práticas e feedback real.

Conhecer a plataforma
+500 alunos4.9/5Garantia 7 dias

Adicionando um coletável e pontuação

Um quadrado que anda é legal, mas jogo precisa de objetivo. Vamos colocar uma moeda que, ao ser tocada, some, dá um ponto e reaparece em outro lugar. Aqui entra uma colisão simples de retângulos e um contador. Atualize o main.lua:

function love.load()
    jogador = { x = 400, y = 300, tamanho = 40, velocidade = 250 }
    moeda = { x = 150, y = 150, tamanho = 24 }
    pontos = 0
end

function love.update(dt)
    if love.keyboard.isDown("right") then jogador.x = jogador.x + jogador.velocidade * dt end
    if love.keyboard.isDown("left")  then jogador.x = jogador.x - jogador.velocidade * dt end
    if love.keyboard.isDown("down")  then jogador.y = jogador.y + jogador.velocidade * dt end
    if love.keyboard.isDown("up")    then jogador.y = jogador.y - jogador.velocidade * dt end

    if colidiu(jogador, moeda) then
        pontos = pontos + 1
        moeda.x = math.random(0, 760)
        moeda.y = math.random(0, 560)
    end
end

function colidiu(a, b)
    return a.x < b.x + b.tamanho and
           b.x < a.x + a.tamanho and
           a.y < b.y + b.tamanho and
           b.y < a.y + a.tamanho
end

function love.draw()
    love.graphics.setColor(1, 0.85, 0.2)
    love.graphics.rectangle("fill", moeda.x, moeda.y, moeda.tamanho, moeda.tamanho)

    love.graphics.setColor(0.3, 0.6, 1)
    love.graphics.rectangle("fill", jogador.x, jogador.y, jogador.tamanho, jogador.tamanho)

    love.graphics.setColor(1, 1, 1)
    love.graphics.print("Pontos: " .. pontos, 10, 10)
end

Já é um jogo pequeno e completo, com objetivo e feedback. A função colidiu faz a detecção clássica de sobreposição entre dois retângulos: se os dois se cruzam nos eixos x e y ao mesmo tempo, houve colisão. O math.random sorteia a nova posição da moeda dentro da janela. E o .. é o operador do Lua para juntar texto, usado para montar a frase da pontuação.

Note como o loop update e draw trabalham juntos. Todo quadro, o update lê o teclado, move o jogador e testa a colisão. Logo depois, o draw redesenha a moeda, o jogador e o placar no estado atual. Essa separação entre "pensar" e "mostrar" é a base de qualquer jogo, e o LÖVE deixa isso explícito, o que é ótimo para quem está aprendendo.

Usando imagens no lugar dos quadrados

Quadrado ensina bem, mas você vai querer sprites. Trocar é simples. Coloque um arquivo de imagem, digamos jogador.png, dentro da pasta do jogo. Carregue a imagem no love.load e desenhe com love.graphics.draw no lugar do retângulo:

function love.load()
    jogador = { x = 400, y = 300, velocidade = 250 }
    sprite = love.graphics.newImage("jogador.png")
end

function love.draw()
    love.graphics.setColor(1, 1, 1)
    love.graphics.draw(sprite, jogador.x, jogador.y)
end

O love.graphics.newImage carrega o arquivo uma vez, no load, porque carregar imagem é caro e não pode ficar dentro do update ou do draw. O love.graphics.draw coloca a imagem na posição. Um detalhe: mantenha a cor em branco (setColor(1, 1, 1)) antes de desenhar sprite, senão a cor que você usou para o retângulo vai tingir a imagem. É um erro comum de quem está começando.

Para ajustar o tamanho da janela e outras opções, o LÖVE usa um arquivo separado chamado conf.lua, onde você configura resolução, título e módulos com love.window. Não é obrigatório para começar, mas é o lugar certo quando você quiser uma janela com tamanho e nome definidos.

Como rodar e distribuir o seu jogo

Durante o desenvolvimento, você roda como já viu: aponta o LÖVE para a pasta com o main.lua, seja arrastando a pasta, seja pelo terminal. Toda vez que salvar o código, feche e abra de novo para ver a mudança.

Para distribuir, o LÖVE tem um formato próprio e prático. Você seleciona os arquivos do jogo (garantindo que o main.lua fique na raiz), compacta tudo em um .zip e troca a extensão para .love. Quem tiver o LÖVE instalado abre esse .love com um duplo clique. Para entregar a quem não tem o LÖVE, você junta o .love com o binário do framework e gera um executável único para cada sistema operacional. O site oficial documenta o passo a passo desse empacotamento para Windows, macOS e Linux, e a comunidade tem ferramentas que automatizam isso. Como o LÖVE é licenciado sob a zlib, você pode distribuir o seu jogo livremente, inclusive vendê-lo.

O próximo passo: aprender a pensar como programador

Se você chegou até aqui com um jogo rodando, entendeu na prática o que muita gente que usa engine visual nunca precisa encarar de frente: o loop do jogo, o delta time, a diferença entre lógica e desenho, e a colisão feita na unha. Isso é ouro. Esse jeito de pensar viaja com você para qualquer ferramenta depois, porque o que você aprendeu foi programação, não uma tela específica.

O LÖVE é um caminho legítimo e honesto para aprender a fazer jogos de verdade, e para muitos projetos 2D ele é tudo que você precisa. Mas seja realista sobre os limites: é 2D, é só código e você constrói bastante coisa manualmente. No dia em que quiser editor visual, suporte a 3D e uma pilha maior de recursos prontos, a Godot é o próximo passo natural, e você chega nela já sabendo o que acontece por baixo, o que é uma vantagem enorme. Nenhuma das duas é melhor em absoluto: são ferramentas para momentos diferentes.

Seja qual for o seu próximo passo, o importante é continuar escrevendo código e terminando pequenos projetos. Se quiser um plano de estudo em vez de sair pulando de tutorial em tutorial, veja a melhor forma de aprender a fazer jogos. E se a web te chama mais que o desktop, o mesmo raciocínio de loop e delta time aparece quando você vai criar jogo com Phaser e JavaScript, então nada do que você aprendeu aqui se perde.

Perguntas frequentes

Preciso instalar alguma coisa para usar o LÖVE?

Sim, mas é rápido. Você baixa o LÖVE no site oficial (love2d.org), instala e pronto. Ele é grátis e open source, com licença zlib, e roda em Windows, macOS, Linux e Android. Fora isso, você só precisa de um editor de texto para escrever o arquivo main.lua. Não existe instalador de SDK gigante nem conta para criar.

O LÖVE tem editor visual como a Godot ou a Unity?

Não. O LÖVE é um framework, não uma engine com interface gráfica. Não existe janela para arrastar objetos para a cena. Você programa tudo em código Lua e o LÖVE executa o seu jogo. Isso é ótimo para aprender a lógica de verdade, porque nada acontece por mágica. Se você quer editor visual, cena montada no mouse e recursos 3D, aí a Godot é o caminho mais natural.

Para que servem love.load, love.update e love.draw?

São os três callbacks centrais do LÖVE. O love.load roda uma vez no início e serve para preparar variáveis e carregar imagens. O love.update(dt) roda a cada quadro e é onde fica a lógica: mover o jogador, checar colisão, contar pontos. O dt é o delta time, o tempo em segundos desde o quadro anterior. O love.draw roda a cada quadro logo depois e é onde você desenha tudo na tela.

Por que multiplicar a velocidade pelo dt?

Porque o dt é o tempo que passou desde o último quadro. Se você somar um valor fixo por quadro, o jogo fica mais rápido em computadores potentes e mais lento em máquinas fracas. Multiplicando a velocidade pelo dt, o movimento fica igual em qualquer taxa de quadros, medido em unidades por segundo. É o hábito mais importante de acertar cedo.

Como distribuo um jogo feito em LÖVE?

Você compacta os arquivos do jogo (com o main.lua na raiz do zip) e troca a extensão .zip por .love. Quem tem o LÖVE instalado abre esse arquivo direto. Para entregar sem exigir instalação, você junta o .love com o binário do LÖVE, gerando um executável único por sistema. O site oficial documenta esse processo de empacotamento para cada plataforma.