Criando um Jogo de Tiro com Arco no Python: Arqueiro vs Balões

  Seja muito bem-vindo à Academy Code Christ!


🌍 Este site está disponível em vários idiomas.
Fique à vontade para usar o tradutor no topo da página e aprender no seu idioma.

Se você já domina o básico do Python, o próximo passo emocionante é aprender a lidar com multimídia: imagens, sons e colisões em tempo real. 

Hoje, na Academy Code Christ, vamos construir um jogo de ação completo onde você controla um arqueiro que deve proteger o céu!


📝 O que aprenderemos hoje?


●       Gerenciamento de Assets: Como carregar imagens e sons externos.

●       Sistema de Som: Usar o mixer do Pygame para disparos e colisões.

●       Lógica de Listas: Controlar múltiplas flechas e inimigos simultaneamente.

●       Colisões Dinâmicas: Detectar o exato momento em que a flecha atinge o alvo.

 

🛠️ Preparação dos Arquivos


Para este jogo rodar, você precisará dos seguintes arquivos na mesma pasta do seu código:


1.      Imagens: céu.jpg, arqueiro.png, flecha.png e balão2.png.


céu.jpg → Para esse jogo eu usei as imagens do Pixabay

Escolhi essa 👉 imagem 👇 Retirei esse anzol no Paint

 


Sem o anzol ficou assim👇



arqueiro.png 
→ Também peguei no Pixabay. Escolhi esse arco e tirei o "lá ele" o fundo no 👉 iloveimg ficou assim 👇

Observação: Você tera que ter uma flecha, também escolhi no Pixabay

Mesmo com essa falha no meio deu certo


Veja como ficou no Python




balão2.png. → Também peguei no Pixabay. Também "lá ele" tirei o fundo no iloveimg.com




2.      Sons: Crie uma pasta chamada sond e coloque disparo.wav, colisao com jogador.wav, etc.


Veja como ficou no Python



disparo.wav  → Também peguei no Pixabay, só que você tem quer selecionar a opção → Efeitos Sonoros e digitar: Disparo 👉 Link aqui onde peguei




💻 O Código Completo do Jogo


Aqui está a estrutura profissional para o seu jogo:

import pygame

import random

# Inicializando o pygame

pygame.init()

# Configurações da tela

tamanho_tela = (800, 600)

tela = pygame.display.set_mode(tamanho_tela)

pygame.display.set_caption("Jogo de Tiro com Arco")

# Cores

BRANCO = (255, 255, 255)

PRETO = (0, 0, 0)

VERMELHO = (255, 0, 0)

# Carregar a imagem de fundo

fundo = pygame.image.load("céu.jpg")  # Coloque o caminho da sua imagem de fundo

fundo = pygame.transform.scale(fundo, (800, 600))  # Ajuste para o tamanho da tela (800x600)

# Carregar sons

pygame.mixer.init()  # Inicializa o mixer de som

som_disparo = pygame.mixer.Sound("sond/disparo.wav")  # Som do disparo

som_colisao_inimigo = pygame.mixer.Sound("sond/flecha colidindo com o balão.mp3")  # Som da colisão com o balão

som_colisao_jogador = pygame.mixer.Sound("sond/colisao com jogador.wav")  # Som da colisão com o jogador

# Classe da nave do jogador

class Nave:

    def __init__(self):

        self.imagem = pygame.image.load("arqueiro.png")  # Coloque o caminho da sua imagem do arquiro

        self.rect = self.imagem.get_rect()

        self.rect.center = (400, 550)  # Inicia no centro inferior da tela

        self.velocidade = 30  # Velocidade de movimento da flecha

    def mover(self, esquerda, direita, cima, baixo):

        if esquerda and self.rect.left > 0:

            self.rect.x -= self.velocidade

        if direita and self.rect.right < 800:

            self.rect.x += self.velocidade

        if cima and self.rect.top > 0:

            self.rect.y -= self.velocidade

        if baixo and self.rect.bottom < 600:

            self.rect.y += self.velocidade

    def desenhar(self):

        tela.blit(self.imagem, self.rect)

# Classe da flecha (no lugar do flecha)

class Flecha:

    def __init__(self, x, y):

        self.imagem = pygame.image.load("flecha.png")  # Coloque o caminho da imagem da flecha

        self.imagem = pygame.transform.scale(self.imagem, (30, 100))  # Ajuste o tamanho da flecha

        self.rect = self.imagem.get_rect()

        self.rect.center = (x, y)

        self.velocidade = 7

    def mover(self):

        self.rect.y -= self.velocidade  # A flecha vai para cima (ajuste para movimento desejado)

    def desenhar(self):

        tela.blit(self.imagem, self.rect)

# Classe do inimigo

class Inimigo:

    def __init__(self):

        self.imagem = pygame.image.load("balão2.png")  # Coloque o caminho da imagem do Balão

        self.rect = self.imagem.get_rect()

        self.rect.center = (random.randint(50, 750), random.randint(-100, -50))  # Posiçã o aleatória no topo

        self.velocidade = 2

    def mover(self):

        self.rect.y += self.velocidade

    def desenhar(self):

        tela.blit(self.imagem, self.rect)

# Função principal do jogo

def jogo():

    # Criando objetos

    jogador = Nave()

    flechas = []  # Agora temos uma lista de flechas, ao invés de mísseis

    inimigos = [Inimigo() for _ in range(5)]

   

    # Variáveis de controle

    clock = pygame.time.Clock()

    pontos = 0

    vidas = 3

    fim_jogo = False

    # Loop principal do jogo

    while not fim_jogo:

        tela.fill(PRETO)

        # Desenhar o fundo

        tela.blit(fundo, (0, 0))  # Coloca a imagem de fundo na tela

        # Movimentos do jogador

        esquerda = direita = cima = baixo = False

        for evento in pygame.event.get():

            if evento.type == pygame.QUIT:

                fim_jogo = True

            if evento.type == pygame.KEYDOWN:

                if evento.key == pygame.K_LEFT:

                    esquerda = True

                if evento.key == pygame.K_RIGHT:

                    direita = True

                if evento.key == pygame.K_UP:

                    cima = True

                if evento.key == pygame.K_DOWN:

                    baixo = True

                if evento.key == pygame.K_SPACE:

                    # Disparar a flecha

                    flechas.append(Flecha(jogador.rect.centerx, jogador.rect.top))

                    som_disparo.play()  # Toca o som do disparo

        # Mover o arqueiro

        jogador.mover(esquerda, direita, cima, baixo)

        # Mover flechas

        for flecha in flechas:

            flecha.mover()

            if flecha.rect.bottom < 0:

                flechas.remove(flecha)

        # Mover inimigos

        for inimigo in inimigos:

            inimigo.mover()

            if inimigo.rect.top > 600:

                inimigos.remove(inimigo)

                inimigos.append(Inimigo())

            # Verificar colisão com as flechas

            for flecha in flechas:

                if flecha.rect.colliderect(inimigo.rect):

                    flechas.remove(flecha)

                    inimigos.remove(inimigo)

                    inimigos.append(Inimigo())

                    pontos += 10

                    som_colisao_inimigo.play()  # Toca o som da colisão com inimigo

            # Verificar colisão com o jogador

            if jogador.rect.colliderect(inimigo.rect):

                vidas -= 1

                inimigos.remove(inimigo)

                inimigos.append(Inimigo())

                som_colisao_jogador.play()  # Toca o som da colisão com o jogador

                if vidas <= 0:

                    fim_jogo = True

        # Desenhar tudo

        jogador.desenhar()

        for flecha in flechas:

            flecha.desenhar()

        for inimigo in inimigos:

            inimigo.desenhar()

        # Exibir informações

        fonte = pygame.font.SysFont("arial", 30)

        texto_pontos = fonte.render(f"Pontos: {pontos}", True, BRANCO)

        tela.blit(texto_pontos, (10, 10))

        texto_vidas = fonte.render(f"Vidas: {vidas}", True, BRANCO)

        tela.blit(texto_vidas, (700, 10))

        # Atualizar a tela

        pygame.display.flip()

        clock.tick(60)  # 60 FPS

    pygame.quit()

# Iniciar o jogo

jogo()


Tudo tem que estar assim👇



🔍 Mergulhando na Lógica: Entendendo o Código Passo a Passo

Agora que você conhece os arquivos necessários para criar o nosso jogo de tiro com arco, chegou a hora de entender como tudo funciona por dentro! 

Vamos estudar o código por partes, de forma simples, para que você consiga compreender a lógica e criar suas próprias modificações.

👣 Inicializando o Pygame 🎮

import pygame

import random

 

pygame.init()

O que está acontecendo aqui?

  • import pygame: importa a biblioteca responsável por criar a janela, mostrar imagens, reproduzir sons e detectar os comandos do jogador.
  • import random: permite gerar posições aleatórias para os balões que aparecem no jogo.
  • pygame.init(): inicializa os módulos do Pygame necessários para executar o jogo.


Pense nessa etapa como a preparação de todos os equipamentos antes de começar uma partida!

👣 Criando a janela do jogo 🖥️

tamanho_tela = (800, 600)

tela = pygame.display.set_mode(tamanho_tela)

pygame.display.set_caption("Jogo de Tiro com Arco")

Aqui, criamos a janela onde toda a ação acontecerá.

  • tamanho_tela = (800, 600): define a largura de 800 pixels e a altura de 600 pixels.
  • pygame.display.set_mode(tamanho_tela): cria a janela com as dimensões definidas.
  • pygame.display.set_caption(...): coloca o título na barra superior da janela.


Em resumo: estamos preparando o cenário onde o arqueiro poderá se movimentar e acertar os balões.

3. Definindo as cores 🎨

BRANCO = (255, 255, 255)

PRETO = (0, 0, 0)

VERMELHO = (255, 0, 0)

O Pygame representa as cores usando o sistema RGB, que combina vermelho, verde e azul.

Cada cor é definida por três números, que variam de 0 a 255:

  • BRANCO = (255, 255, 255): mistura máxima das três cores.
  • PRETO = (0, 0, 0): ausência de intensidade das três cores.
  • VERMELHO = (255, 0, 0): intensidade máxima do vermelho.

Essas cores serão utilizadas para preencher áreas da tela e exibir informações, como a pontuação e as vidas do jogador.

👣 Carregando a imagem de fundo ☁️

fundo = pygame.image.load("céu.jpg")

fundo = pygame.transform.scale(fundo, (800, 600))

Nesta etapa, colocamos o céu no cenário do jogo.

  • pygame.image.load("céu.jpg"): carrega a imagem do arquivo.
  • pygame.transform.scale(fundo, (800, 600)): redimensiona a imagem para ocupar toda a janela.

O arquivo céu.jpg precisa estar no local indicado pelo código.

👣 Preparando os efeitos sonoros 🔊

pygame.mixer.init()

 

som_disparo = pygame.mixer.Sound("sond/disparo.wav")

som_colisao_inimigo = pygame.mixer.Sound(

    "sond/flecha colidindo com o balão.mp3"

)

som_colisao_jogador = pygame.mixer.Sound(

    "sond/colisao com jogador.wav"

)

Um jogo fica muito mais interessante quando as ações são acompanhadas por sons!

  • pygame.mixer.init(): inicializa o sistema de áudio.
  • pygame.mixer.Sound(...): carrega cada efeito sonoro.
  • som_disparo: representa o som do disparo da flecha.
  • som_colisao_inimigo: representa o som da flecha atingindo um balão.
  • som_colisao_jogador: representa o som de uma colisão entre o jogador e um inimigo.

Mais adiante, utilizaremos .play() para reproduzir cada som no momento certo.

Atenção: os nomes e as extensões dos arquivos precisam corresponder aos arquivos existentes na pasta sond. O Pygame também precisa conseguir ler o formato de áudio utilizado.

👣 Criando a classe do arqueiro 🏹

class Nave:

    def __init__(self):

        self.imagem = pygame.image.load("arqueiro.png")

        self.rect = self.imagem.get_rect()

        self.rect.center = (400, 550)

        self.velocidade = 30

Aqui começa a programação do personagem que será controlado pelo jogador.

Embora a classe tenha o nome Nave, neste jogo ela representa o arqueiro.

  • class Nave: cria uma classe, que funciona como um modelo para construir o personagem.
  • __init__(self): inicializa as características do personagem quando um objeto é criado.
  • self.imagem: guarda a imagem do arqueiro.
  • self.rect: representa a área retangular usada para posicionar a imagem e verificar colisões.
  • self.rect.center = (400, 550): coloca o centro do personagem próximo à parte inferior da tela.
  • self.velocidade = 30: define quantos pixels o personagem se desloca a cada atualização em que o movimento é aplicado.

Como o arqueiro se movimenta?

def mover(self, esquerda, direita, cima, baixo):

    if esquerda and self.rect.left > 0:

        self.rect.x -= self.velocidade

 

    if direita and self.rect.right < 800:

        self.rect.x += self.velocidade

 

    if cima and self.rect.top > 0:

        self.rect.y -= self.velocidade

 

    if baixo and self.rect.bottom < 600:

        self.rect.y += self.velocidade

Esse método verifica em qual direção o jogador deseja se movimentar.

  • esquerda: diminui a posição horizontal.
  • direita: aumenta a posição horizontal.
  • cima: diminui a posição vertical.
  • baixo: aumenta a posição vertical.

As condições verificam os limites da janela para evitar que o personagem ultrapasse as bordas, desde que as dimensões da imagem e a velocidade sejam compatíveis com esses limites.

Como o arqueiro aparece na tela?

def desenhar(self):

    tela.blit(self.imagem, self.rect)

O método desenhar() utiliza blit() para colocar a imagem do arqueiro na tela, na posição indicada por self.rect.

👣 Criando a classe da flecha ➶

class Flecha:

    def __init__(self, x, y):

        self.imagem = pygame.image.load("flecha.png")

        self.imagem = pygame.transform.scale(

            self.imagem, (30, 100)

        )

        self.rect = self.imagem.get_rect()

        self.rect.center = (x, y)

        self.velocidade = 7

A classe Flecha define como cada flecha será criada.

  • x e y: indicam a posição inicial da flecha.
  • pygame.image.load("flecha.png"): carrega a imagem.
  • pygame.transform.scale(...): ajusta a imagem para 30 por 100 pixels.
  • self.rect.center = (x, y): posiciona o centro da flecha nas coordenadas recebidas.
  • self.velocidade = 7: define a velocidade do disparo.

Para fazer a flecha subir, usamos:

def mover(self):

    self.rect.y -= self.velocidade

Como a coordenada vertical diminui, a flecha se desloca para cima.

E para desenhá-la:

def desenhar(self):

    tela.blit(self.imagem, self.rect)

Cada vez que o jogador dispara, o programa pode criar uma nova flecha.

👣 Criando os balões inimigos 🎈

class Inimigo:

    def __init__(self):

        self.imagem = pygame.image.load("balão2.png")

        self.rect = self.imagem.get_rect()

        self.rect.center = (

            random.randint(50, 750),

            random.randint(-100, -50)

        )

        self.velocidade = 2

Agora criamos os alvos que o arqueiro deverá acertar.

  • balão2.png: imagem do inimigo.
  • random.randint(50, 750): escolhe uma posição horizontal aleatória.
  • random.randint(-100, -50): escolhe uma posição vertical acima da tela.
  • self.velocidade = 2: define a velocidade de descida.

O movimento acontece neste método:

def mover(self):

    self.rect.y += self.velocidade

Como a coordenada vertical aumenta, o balão desce pela tela.

O método desenhar() utiliza tela.blit() para mostrar o balão na posição atual.

👣 Iniciando a função principal do jogo ⚙️

def jogo():

    jogador = Nave()

    flechas = []

    inimigos = [Inimigo() for _ in range(5)]

A função jogo() reúne os elementos necessários para começar a partida.

  • jogador = Nave(): cria o arqueiro.
  • flechas = []: cria uma lista vazia para armazenar as flechas disparadas.
  • inimigos = [Inimigo() for _ in range(5)]: cria cinco balões inimigos.

Por que usamos listas?

Porque o jogador poderá disparar várias flechas, e vários balões poderão aparecer ao mesmo tempo. As listas permitem acompanhar todos esses objetos.

Em seguida, preparamos as variáveis de controle:

clock = pygame.time.Clock()

pontos = 0

vidas = 3

fim_jogo = False

  • clock: controla a velocidade de atualização do jogo.
  • pontos: registra a pontuação, começando em zero.
  • vidas: define três vidas iniciais.
  • fim_jogo: indica se a partida deve terminar.


👣 Mantendo o jogo em funcionamento 🔄

while not fim_jogo:

Este é um dos trechos mais importantes de todo o programa!

O while repete as instruções enquanto fim_jogo for falso. A cada repetição, o jogo verifica os comandos, movimenta os personagens, confere colisões e atualiza a imagem.

No início de cada ciclo:

tela.fill(PRETO)

tela.blit(fundo, (0, 0))

A primeira instrução preenche a tela de preto. A segunda desenha o fundo do céu por cima dessa cor.

Isso prepara a imagem que será exibida naquele quadro.

👣 Detectando os comandos do jogador ⌨️

for evento in pygame.event.get():

    if evento.type == pygame.QUIT:

        fim_jogo = True

O programa verifica os eventos da janela. Se o usuário clicar no botão de fechar, fim_jogo recebe True, indicando que o laço principal deverá terminar.

O código também verifica as teclas pressionadas.

if evento.type == pygame.KEYDOWN:

    if evento.key == pygame.K_SPACE:

        flechas.append(

            Flecha(jogador.rect.centerx, jogador.rect.top)

        )

        som_disparo.play()

Quando a barra de espaço é pressionada:

  1. Uma nova flecha é criada.
  2. A flecha é posicionada na região superior do arqueiro.
  3. O objeto é adicionado à lista flechas.
  4. O som do disparo é reproduzido.

As setas do teclado são utilizadas para controlar o movimento do arqueiro. Para que ele continue se movimentando enquanto uma tecla fica pressionada, é necessário consultar as teclas com pygame.key.get_pressed() em cada ciclo do jogo.

👣 Movimentando e removendo as flechas 🎯

for flecha in flechas:

    flecha.mover()

 

    if flecha.rect.bottom < 0:

        flechas.remove(flecha)

O programa percorre a lista de flechas e atualiza a posição de cada uma.

Quando uma flecha ultrapassa a parte superior da tela, ela é removida da lista.

Isso evita manter no jogo objetos que já não podem ser vistos.

Observação técnica: para evitar problemas ao modificar uma lista durante sua iteração, é mais seguro percorrer uma cópia ou construir uma nova lista com os objetos que devem continuar ativos.

👣 Movimentando os inimigos 👾

for inimigo in inimigos:

    inimigo.mover()

 

    if inimigo.rect.top > 600:

        inimigos.remove(inimigo)

        inimigos.append(Inimigo())

Cada inimigo desce pela tela.

Quando um balão ultrapassa a borda inferior, ele é removido e um novo inimigo é criado. Assim, o jogador continua tendo alvos para acertar.

Essa lógica faz com que o jogo permaneça ativo até que o jogador perca todas as vidas ou feche a janela.

👣 Detectando as colisões entre flechas e balões 💥

if flecha.rect.colliderect(inimigo.rect):

    flechas.remove(flecha)

    inimigos.remove(inimigo)

    inimigos.append(Inimigo())

    pontos += 10

    som_colisao_inimigo.play()

Aqui está uma das principais mecânicas do jogo!

  • colliderect(): verifica se os retângulos de dois objetos se sobrepõem.
  • flechas.remove(flecha): remove a flecha que atingiu o alvo.
  • inimigos.remove(inimigo): remove o balão atingido.
  • inimigos.append(Inimigo()): cria um novo balão.
  • pontos += 10: adiciona dez pontos à pontuação.
  • som_colisao_inimigo.play(): reproduz o efeito sonoro da colisão.


Portanto, quando uma flecha atinge um balão, o jogador ganha dez pontos e um novo alvo aparece.

👣 Detectando colisões com o arqueiro ❤️

if jogador.rect.colliderect(inimigo.rect):

    vidas -= 1

    inimigos.remove(inimigo)

    inimigos.append(Inimigo())

    som_colisao_jogador.play()

 

    if vidas <= 0:

        fim_jogo = True

Se um balão encostar no arqueiro, o jogador perde uma vida.

  • vidas -= 1: diminui uma vida.
  • O balão que atingiu o jogador é removido.
  • Um novo balão é criado.
  • O som da colisão é reproduzido.
  • Se as vidas chegarem a zero, a variável fim_jogo passa a ser verdadeira.


Assim, o programa sabe quando a partida deve terminar.

👣 Desenhando os objetos na tela 🖼️

jogador.desenhar()

 

for flecha in flechas:

    flecha.desenhar()

 

for inimigo in inimigos:

    inimigo.desenhar()

Depois de movimentar os objetos e verificar as colisões, o programa desenha o arqueiro, todas as flechas ativas e todos os balões existentes.

Essa etapa é repetida em cada ciclo do jogo para que as imagens acompanhem os movimentos.

👣 Exibindo a pontuação e as vidas 📊

fonte = pygame.font.SysFont("arial", 30)

 

texto_pontos = fonte.render(

    f"Pontos: {pontos}", True, BRANCO

)

tela.blit(texto_pontos, (10, 10))

 

texto_vidas = fonte.render(

    f"Vidas: {vidas}", True, BRANCO

)

tela.blit(texto_vidas, (700, 10))

O jogo também precisa mostrar ao jogador como está sua partida.

  • pygame.font.SysFont("arial", 30): seleciona a fonte Arial, tamanho 30.
  • fonte.render(...): transforma o texto em uma imagem.
  • tela.blit(...): desenha o texto na janela.
  • (10, 10) e (700, 10): definem as posições da pontuação e das vidas.

Dessa forma, o jogador consegue acompanhar seu desempenho.

👣 Atualizando a tela e controlando a velocidade ⏱️

pygame.display.flip()

clock.tick(60)

Essas duas instruções finalizam cada ciclo de atualização.

  • pygame.display.flip(): apresenta na janela tudo o que foi desenhado no quadro atual.
  • clock.tick(60): limita o ciclo a aproximadamente 60 quadros por segundo.

Sem a atualização da tela, as alterações feitas nos desenhos não seriam apresentadas corretamente. Já o controle de quadros ajuda a manter o jogo funcionando em uma velocidade mais consistente.

👣 Encerrando o jogo e iniciando a partida 🏁

No final da função jogo(), encontramos:

pygame.quit()

Essa instrução encerra os módulos do Pygame quando a partida termina.

E, na última parte do programa:

jogo()

A chamada executa a função principal e inicia a partida.

Pronto! Agora você conhece o caminho completo: o programa prepara os recursos, cria os objetos, recebe os comandos do jogador, movimenta as flechas e os balões, detecta colisões, atualiza a pontuação e encerra a partida quando necessário.

✝️ Reflexão: Precisão e Foco

Na programação, cada instrução tem sua função e precisa estar no lugar certo para que o projeto funcione. Da mesma forma, precisamos de foco, paciência e perseverança para alcançar nossos objetivos.

Assim como o arqueiro precisa mirar com atenção, quem aprende programação precisa observar os detalhes, testar suas ideias e aprender com os erros.

“Os teus olhos olhem direitinho, e as tuas pálpebras mirem defronte de ti.” (Provérbios 4:25)

🏆 Desafio do Arqueiro

Agora que você compreendeu a lógica do jogo, que tal experimentar algumas melhorias?

  1. Níveis de dificuldade: aumente a velocidade dos balões conforme a pontuação cresce.
  2. Flecha especial: crie um disparo mais rápido ou com tamanho diferente.
  3. Vidas ilustradas: substitua o texto das vidas por imagens de corações.
  4. Recorde de pontos: registre a maior pontuação alcançada.
  5. Tela de encerramento: mostre uma mensagem quando o jogador perder todas as vidas.

Continue praticando! Cada projeto é uma oportunidade de transformar conhecimento em criatividade.

 


Este conteúdo faz parte do Academy Code Christ. 

Um projeto criado para ajudar jovens e iniciantes a aprender programação do zero, de forma simples e gratuita.

Nossa missão é compartilhar conhecimento e incentivar mais pessoas a descobrir que também podem aprender tecnologia, criar projetos e desenvolver novas habilidades.
Todo o conteúdo é disponibilizado gratuitamente, de forma clara, prática e 
sem enrolação. 

Você pode apoiar o projeto se quiser 
com um café. 


Post a Comment

Postagem Anterior Próxima Postagem