Voltar para o Blog
Quest Log

Como Criar um Jogo em Python com Pygame: Tutorial para Iniciantes

Janela de um jogo feito em Python com Pygame mostrando um quadrado verde desviando de blocos vermelhos que caem

Aprenda como criar um jogo em Python com Pygame: instalação, game loop, movimento, colisão e placar em um jogo real de desviar de obstáculos, passo a passo.

Se você aprendeu Python na escola, na faculdade ou em um curso de lógica, a pergunta aparece rápido: dá para usar isso em um jogo? Dá, e este guia mostra como criar um jogo em Python de verdade usando o Pygame. Não é um "hello world" disfarçado: você vai montar um jogo completo de desviar de obstáculos, com movimento, colisão, placar e reinício, em menos de 100 linhas de código que rodam em qualquer computador.

Por que Python é a porta de entrada de tanta gente

Python virou a primeira linguagem de quase todo mundo por um motivo simples: a sintaxe sai da frente. Sem chaves, sem ponto e vírgula, sem tipos obrigatórios em todo lugar. Escolas técnicas, cursinhos de lógica e disciplinas introdutórias de faculdade adotaram Python em massa, então milhares de pessoas chegam ao desenvolvimento de jogos já sabendo escrever um if e um for em Python.

A boa notícia: esse conhecimento serve, sim, para jogos. O Pygame é uma biblioteca que dá ao Python o que falta para um jogo funcionar: janela, desenho na tela, leitura de teclado e mouse, som e controle de tempo. Com isso você consegue fazer jogos 2D completos: clones de Pong, Snake, Breakout, jogos de plataforma simples e o jogo de desviar que vamos construir aqui.

O que o Pygame não é: uma engine. Ele não tem editor visual, não tem sistema de física pronto, não tem animação por keyframes nem export para celular. Isso importa, e vamos falar disso com honestidade no final. Mas para aprender a lógica que sustenta qualquer jogo, ele é uma das melhores ferramentas que existem, justamente porque nada acontece por mágica: cada pixel na tela está lá porque o seu código mandou.

Se você ainda está decidindo por onde começar, o comparativo de qual linguagem aprender para fazer jogos coloca o Python no contexto das outras opções.

Instalação e o esqueleto de todo jogo

Você precisa do Python 3 instalado (qualquer versão a partir da 3.9 serve). Depois, um único comando no terminal:

pip install pygame

Pronto. Agora o esqueleto mínimo, que é a fundação de literalmente todo jogo já feito: uma janela, um relógio e um loop que roda até o jogador fechar. Salve como desvia.py e execute com python desvia.py:

import pygame

LARGURA = 480
ALTURA = 640
FPS = 60
COR_FUNDO = (20, 20, 30)


def main() -> None:
    pygame.init()
    tela = pygame.display.set_mode((LARGURA, ALTURA))
    pygame.display.set_caption("Desvia!")
    relogio = pygame.time.Clock()

    rodando = True
    while rodando:
        for evento in pygame.event.get():
            if evento.type == pygame.QUIT:
                rodando = False

        tela.fill(COR_FUNDO)
        pygame.display.flip()
        relogio.tick(FPS)

    pygame.quit()


if __name__ == "__main__":
    main()

Quatro coisas para entender aqui, porque elas se repetem em qualquer engine que você usar na vida:

  • Game loop: o while rodando é o coração do jogo. A cada volta ele processa entradas, atualiza o estado e redesenha tudo. Um jogo é isso, dezenas de vezes por segundo.
  • Eventos: pygame.event.get() entrega tudo que aconteceu desde o último quadro: teclas, cliques, o X da janela. Sem esse loop de eventos, a janela congela.
  • Desenho: tela.fill() pinta o fundo e pygame.display.flip() mostra o quadro pronto. Você desenha tudo primeiro e exibe de uma vez.
  • Clock: relogio.tick(FPS) limita o jogo a 60 quadros por segundo. Sem ele, o loop roda na velocidade máxima do processador e o jogo fica injogável em máquinas rápidas.

Rode e você verá uma janela escura de 480x640. Parece pouco, mas o esqueleto está vivo. Agora vem o jogo.

Como criar um jogo em Python na prática: o projeto Desvia!

A ideia do Desvia! cabe em uma frase: você controla um quadrado na parte de baixo da tela, blocos caem do topo em posições e velocidades aleatórias, cada bloco que você deixa passar vale 1 ponto, e encostar em um bloco encerra a partida. Simples de descrever, e completo o bastante para exercitar movimento, spawn, colisão, placar e estado de fim de jogo.

O jogador e o movimento

O jogador é um pygame.Rect, a estrutura mais útil do Pygame: um retângulo com posição e tamanho que já sabe detectar colisão e se manter dentro de limites. Adicione estas constantes e funções ao arquivo, acima de main:

VEL_JOGADOR = 6
COR_JOGADOR = (80, 200, 120)
AREA_TELA = pygame.Rect(0, 0, LARGURA, ALTURA)


def criar_jogador() -> pygame.Rect:
    return pygame.Rect(LARGURA // 2 - 25, ALTURA - 70, 50, 50)


def mover_jogador(jogador: pygame.Rect) -> None:
    teclas = pygame.key.get_pressed()
    if teclas[pygame.K_LEFT]:
        jogador.x -= VEL_JOGADOR
    if teclas[pygame.K_RIGHT]:
        jogador.x += VEL_JOGADOR
    jogador.clamp_ip(AREA_TELA)

Repare na diferença de abordagem: para movimento contínuo usamos pygame.key.get_pressed(), que pergunta a cada quadro "essa tecla está apertada agora?". O loop de eventos serve para coisas que acontecem uma vez (fechar a janela, apertar ENTER); o estado das teclas serve para movimento que dura enquanto o dedo estiver na seta. A linha clamp_ip(AREA_TELA) prende o retângulo dentro da tela, então o jogador não escapa pelas bordas.

Dentro do loop, antes do tela.fill, chame mover_jogador(jogador) e desenhe com pygame.draw.rect(tela, COR_JOGADOR, jogador). Crie o jogador antes do loop com jogador = criar_jogador(). Rode: o quadrado verde já obedece às setas.

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

Obstáculos caindo do céu

Cada obstáculo é um retângulo com uma velocidade própria, então vamos representá-lo como uma lista de dois itens: [rect, velocidade]. Para criar obstáculos em intervalos regulares, o Pygame tem um recurso elegante: um evento customizado disparado por timer, que cai no mesmo loop de eventos do QUIT.

import random

VEL_MINIMA = 3
VEL_MAXIMA = 7
INTERVALO_SPAWN_MS = 600
EVENTO_SPAWN = pygame.USEREVENT + 1
COR_OBSTACULO = (220, 80, 80)


def criar_obstaculo() -> list:
    largura = random.randint(40, 90)
    x = random.randint(0, LARGURA - largura)
    rect = pygame.Rect(x, -30, largura, 30)
    velocidade = random.randint(VEL_MINIMA, VEL_MAXIMA)
    return [rect, velocidade]

O obstáculo nasce com y = -30, ou seja, logo acima da borda superior, e desce a cada quadro somando a própria velocidade. Larguras e velocidades sorteadas deixam o jogo imprevisível sem precisar de nenhum sistema de dificuldade: às vezes vem um bloco largo e lento, às vezes um estreito e rápido.

Dentro de main, logo depois de criar o relógio, ligue o timer com pygame.time.set_timer(EVENTO_SPAWN, INTERVALO_SPAWN_MS). A cada 600 milissegundos, o Pygame coloca um EVENTO_SPAWN na fila e o seu loop de eventos cria um obstáculo novo. Quer o jogo mais difícil? Diminua o intervalo.

O jogo completo

Agora a versão final, juntando tudo com placar, colisão e reinício por ENTER. Este é o arquivo desvia.py inteiro, pronto para rodar:

import random

import pygame

LARGURA = 480
ALTURA = 640
FPS = 60
VEL_JOGADOR = 6
VEL_MINIMA = 3
VEL_MAXIMA = 7
INTERVALO_SPAWN_MS = 600
EVENTO_SPAWN = pygame.USEREVENT + 1

COR_FUNDO = (20, 20, 30)
COR_JOGADOR = (80, 200, 120)
COR_OBSTACULO = (220, 80, 80)
COR_TEXTO = (240, 240, 240)
AREA_TELA = pygame.Rect(0, 0, LARGURA, ALTURA)


def criar_jogador() -> pygame.Rect:
    return pygame.Rect(LARGURA // 2 - 25, ALTURA - 70, 50, 50)


def criar_obstaculo() -> list:
    largura = random.randint(40, 90)
    x = random.randint(0, LARGURA - largura)
    rect = pygame.Rect(x, -30, largura, 30)
    velocidade = random.randint(VEL_MINIMA, VEL_MAXIMA)
    return [rect, velocidade]


def mover_jogador(jogador: pygame.Rect) -> None:
    teclas = pygame.key.get_pressed()
    if teclas[pygame.K_LEFT]:
        jogador.x -= VEL_JOGADOR
    if teclas[pygame.K_RIGHT]:
        jogador.x += VEL_JOGADOR
    jogador.clamp_ip(AREA_TELA)


def main() -> None:
    pygame.init()
    tela = pygame.display.set_mode((LARGURA, ALTURA))
    pygame.display.set_caption("Desvia!")
    relogio = pygame.time.Clock()
    fonte = pygame.font.Font(None, 36)
    pygame.time.set_timer(EVENTO_SPAWN, INTERVALO_SPAWN_MS)

    jogador = criar_jogador()
    obstaculos: list = []
    placar = 0
    game_over = False

    rodando = True
    while rodando:
        for evento in pygame.event.get():
            if evento.type == pygame.QUIT:
                rodando = False
            elif evento.type == EVENTO_SPAWN and not game_over:
                obstaculos.append(criar_obstaculo())
            elif evento.type == pygame.KEYDOWN and game_over:
                if evento.key == pygame.K_RETURN:
                    jogador = criar_jogador()
                    obstaculos.clear()
                    placar = 0
                    game_over = False

        if not game_over:
            mover_jogador(jogador)

            sobraram = []
            for obstaculo in obstaculos:
                obstaculo[0].y += obstaculo[1]
                if obstaculo[0].top > ALTURA:
                    placar += 1
                else:
                    sobraram.append(obstaculo)
            obstaculos = sobraram

            for obstaculo in obstaculos:
                if jogador.colliderect(obstaculo[0]):
                    game_over = True

        tela.fill(COR_FUNDO)
        pygame.draw.rect(tela, COR_JOGADOR, jogador)
        for obstaculo in obstaculos:
            pygame.draw.rect(tela, COR_OBSTACULO, obstaculo[0])

        texto = fonte.render(f"Placar: {placar}", True, COR_TEXTO)
        tela.blit(texto, (10, 10))

        if game_over:
            aviso = fonte.render("Bateu! ENTER para reiniciar", True, COR_TEXTO)
            tela.blit(aviso, (LARGURA // 2 - aviso.get_width() // 2, ALTURA // 2))

        pygame.display.flip()
        relogio.tick(FPS)

    pygame.quit()


if __name__ == "__main__":
    main()

Três detalhes que valem sua atenção nessa versão final:

  • Colisão em uma linha: jogador.colliderect(obstaculo[0]) faz todo o trabalho de checar sobreposição entre retângulos. É por isso que usar Rect desde o início compensa.
  • Limpeza da lista: obstáculos que passam do fundo da tela viram ponto e saem da lista sobraram. Sem essa remoção, a lista cresce para sempre e o jogo trava depois de alguns minutos.
  • Estado de game over: em vez de fechar o jogo na colisão, a flag game_over congela a lógica e mantém o desenho rodando, mostrando a mensagem de reinício. Máquinas de estado começam assim, com um booleano honesto.

Rode, jogue, e depois mexa nos números do topo do arquivo. Aumentar VEL_MAXIMA, encolher o jogador, acelerar o spawn: cada constante é um botão de game design. Essa é a parte mais valiosa do exercício, porque ajustar a sensação do jogo mexendo em valores é o trabalho diário de quem faz jogos.

Os limites honestos do Pygame

Aqui vai a parte que muitos tutoriais escondem: o Pygame é ótimo para aprender e fraco para publicar. Os limites são concretos:

  • Sem editor: não existe cena, hierarquia visual nem inspector. Tudo é código, incluindo posicionar cada elemento na tela por coordenada. Em um projeto pequeno isso é até didático; em um jogo com 30 telas, vira sofrimento.
  • Export limitado: transformar seu .py em um executável já exige ferramentas externas como PyInstaller. Levar para celular, console ou navegador vai de "muito difícil" a "na prática, inviável". Engines modernas exportam para essas plataformas com poucos cliques.
  • Física e animação manuais: gravidade, pulo, rampas, knockback, animação de personagem: tudo isso você escreve do zero. De novo, didático em dose pequena, improdutivo em jogo real.
  • Desempenho: Python é interpretado e o Pygame desenha na CPU. Para centenas de objetos simultâneos ou efeitos visuais elaborados, o quadro cai antes do que você imagina.

Nada disso é defeito escondido: o Pygame nunca prometeu ser engine. Ele é uma camada fina sobre a biblioteca SDL, feita para você tocar cada engrenagem do jogo com a mão. Como ferramenta de aprendizado, isso é uma qualidade rara.

E depois do Pygame?

A resposta depende do que você quer, e vale ser direto sobre os dois caminhos.

Se o seu objetivo é aprender programação, ficar no Pygame por um bom tempo é uma escolha legítima. Faça um Snake, um Pong, um clone de Breakout. Cada projeto vai aprofundar estruturas de dados, funções e organização de código, e isso vale para qualquer carreira em tecnologia, não só jogos.

Agora, se o que você quer é fazer jogos de verdade, jogos que outras pessoas baixam e jogam no celular ou na Steam, em algum momento você migra para uma engine. E aqui o Python te dá uma vantagem concreta: o GDScript, linguagem da engine Godot, tem sintaxe abertamente inspirada em Python. Indentação define blocos, if e for funcionam do mesmo jeito, e a função _process(delta) da Godot é o primo direto do seu game loop. Quem fez o Desvia! deste tutorial refaz ele na Godot em uma tarde, com a diferença de que lá o jogo exporta para Windows, web e celular sem drama.

O raciocínio para decidir a hora certa de trocar de ferramenta é o mesmo que explicamos no guia sobre quando migrar do Scratch para a Godot: a troca faz sentido quando a ferramenta atual vira o teto do seu projeto, não antes. Se os seus jogos no Pygame começaram a esbarrar nos limites da lista acima, o teto chegou.

E se você quer encurtar esse caminho com acompanhamento, currículo estruturado e projetos guiados do zero até o export, veja nossa análise de qual é o melhor curso de desenvolvimento de jogos para entender o que separa um curso sério de uma playlist de tutoriais soltos.

O resumo é este: Python com Pygame é um excelente primeiro passo, talvez o melhor disponível para quem já sabe a linguagem. Só não confunda o primeiro passo com o destino. Faça o Desvia!, faça mais dois ou três jogos, e quando sentir o teto, pule para uma engine sabendo que quase tudo que você aprendeu vai junto.

Perguntas frequentes

Preciso saber muito Python antes de usar o Pygame?

Não. Se você já entende variável, condicional, loop e função, consegue seguir este tutorial. O Pygame não exige orientação a objetos nem bibliotecas avançadas. Na prática, fazer um jogo pequeno é uma das melhores formas de consolidar exatamente esses fundamentos, porque cada conceito aparece com um propósito visível na tela.

Dá para fazer jogo profissional com Python?

Dá, mas é raro e trabalhoso. Existem jogos comerciais feitos com Python, mas são exceção: o Pygame não tem editor de cenas, o export para consoles e mobile é praticamente inviável e o desempenho limita projetos maiores. Para aprender lógica, Python é excelente. Para lançar um jogo comercial, uma engine como Godot, Unity ou Unreal resolve distribuição, física e animação que no Pygame você teria que construir na mão.

Python ou Godot para começar?

Depende do objetivo. Se você quer aprender programação e já usa Python na escola ou faculdade, começar com Pygame aproveita o que você sabe. Se o objetivo é fazer jogos de verdade, vale ir direto para a Godot: o GDScript tem sintaxe muito parecida com Python, então quase nada do que você aprendeu se perde, e a engine entrega editor, física, animação e export que o Pygame não tem.

O Pygame roda em qualquer computador?

Roda em praticamente qualquer máquina com Windows, Linux ou macOS, incluindo computadores antigos e modestos. Como os jogos de estudo usam formas geométricas ou sprites 2D simples, não há exigência de placa de vídeo dedicada. Você só precisa do Python 3 instalado e de um pip install pygame no terminal.

Por que meu jogo no Pygame fica lento ou travando?

As causas mais comuns são esquecer o clock.tick() no loop, carregar imagens dentro do loop principal em vez de uma vez no início, ou acumular objetos em uma lista sem nunca remover os que saíram da tela. No jogo deste tutorial, os obstáculos que passam do fundo da janela são removidos da lista a cada quadro justamente para evitar esse acúmulo.