Gerando Meteoros Automaticamente - Chuva de Meteoros
No capítulo anterior, criamos um meteoro e implementamos sua rotação. Mas ele ainda paira congelado na tela, sozinho e inofensivo.
Um único meteoro estático não faz jogo. Vamos transformar isso em uma chuva real: vários meteoros gerados automaticamente, caindo em ritmo contínuo.
Ao longo deste capítulo você aprenderá a:
- Criar função para geração de meteoros
- Atualizar uma lista de meteoros
- Controlar a frequência da geração
Para criar essa chuva de meteoros, primeiro precisamos automatizar a criação deles. Em vez de montar cada meteoro manualmente, vamos ensinar o jogo a gerar novos meteoros sempre que necessário.
Criando Função de Geração de Meteoros
Até aqui usamos diversas funções fornecidas pelo Python e Pygame como:
pygame.init()random.randint()pygame.display.update()
Agora vamos criar a nossa própria função.
Uma função é um bloco de código que recebe uma tarefa específica. Em vez de repetir as mesmas instruções várias vezes, podemos agrupá-las em uma função e executá-las sempre que necessário.
Pense em uma função como uma receita. Você escreve os passos uma única vez e pode executá-los sempre que precisar.
Vamos criar a função criar_meteoro(). Ela será responsável por montar a estrutura de cada meteoro — a mesma que construímos no capítulo anterior.
def criar_meteoro(): meteoro_img = pygame.image.load("img/meteoro.png") meteoro = { "imagem": meteoro_img, "x": random.randint(0, janela.get_width()), "y": -100, "velocidade": 2, "rotacao": 0, "dir_rotacao": random.randint(-1, 1) } return meteoro
Toda função criada em Python começa com a palavra-chave
def, seguida do nome da função e dos parênteses. Tudo que estiver indentado abaixo dela fará parte da função.A palavra-chave
returnencerra a execução da função e envia o valor indicado para quem a chamou.Quando escrevemos
meteoro = criar_meteoro(), o Python executa a função e deposita o dicionário retornado dentro da variávelmeteoro.
A função criar_meteoro() cria um novo meteoro e nos retorna a sua estrutura. Assim podemos acessar seus valores normalmente.
Note que a posição x do meteoro foi alterada para um número aleatório entre 0 e janela.get_width().
Também alteramos sua posição y para -100 deixando o meteoro na parte de cima fora da tela.
Mas como vamos ter vários meteoros ao mesmo tempo. Ainda precisamos de uma estrutura para armazená-los: as listas.
Criando Vários Meteoros
Trabalhamos com dicionários desde o capítulo 2 para estruturar a nave e o meteoro.
As listas funcionam de forma complementar: em vez de organizar dados por nome de chave, elas armazenam os valores em sequência. O que as torna ideais para guardar uma coleção de itens do mesmo tipo, como vários meteoros.
Em dicionários identificamos as informações usando as chaves criadas para cada valor.
meteoro = { "velocidade": 2 } meteoro["velocidade"] #> Obtem a velocidade do meteoroNas listas os valores possuem um índice. A contagem dos valores começa pelo índice
0.meteoros = ["meteoro_1", "meteoro_2", "meteoro_3"] meteoros[1] #> Obtem o segundo meteoro da lista
Vamos criar uma lista para armazenar todos os meteoros. Dentro dessa lista vamos adicionar 3 meteoros temporariamente.
meteoros = [criar_meteoro(), criar_meteoro(), criar_meteoro()]
Agora vamos ver como podemos desenhar e mover esse meteoros usado essa lista.
Atualizando Lista de Meteoros
Depois de armazenar vários meteoros na lista, precisamos executar a mesma lógica para cada um deles.
Para isso vamos percorrer todos os itens usando um loop for.
for meteoro in meteoros: meteoro["y"] += meteoro["velocidade"] meteoro["rotacao"] += meteoro["dir_rotacao"] imagem_rotacionada = pygame.transform.rotate(meteoro["imagem"], meteoro["rotacao"]) retangulo_rotacionado = imagem_rotacionada.get_rect(center = (meteoro["x"], meteoro["y"])) janela.blit(imagem_rotacionada, retangulo_rotacionado)
Dentro do loop, movemos o meteoro para baixo incrementando sua posição y com sua velocidade.
Também movemos a lógica criada para desenhar o meteoro no capítulo anterior para dentro do loop.
Ao executar o jogo você verá 3 meteoros caindo em direção a nave.
Temos apenas três meteoros e mais nenhum é gerado depois. Vamos fazer com que um novo meteoro seja gerado em um intervalo de tempo.
Gerando Meteoros Automaticamente
Temos uma função para criar um meteoro e uma lista para armazená-los.
Agora só falta gerar os meteoros de forma automática. Para isso vamos usar o sistema de eventos do Pygame.
Com a base do sistema de eventos já estabelecida — usamos pygame.QUIT para detectar o fechamento da janela — agora vamos um passo alem.
Vamos criar o nosso próprio evento personalizado. O Pygame reserva o identificador pygame.USEREVENT justamente para isso.
criar_novo_meteoro = pygame.USEREVENT + 1 pygame.time.set_timer(criar_novo_meteoro, 1500)
Aqui criamos um novo evento chamado criar_novo_meteoro e adicionamos um temporizador para que o evento seja acionado a cada 1,5 segundo.
Ao usar pygame.USEREVENT + 1 evita conflitos quando você criar vários eventos personalizados.
Dentro do gerenciamento de eventos no game loop, vamos fazer com que um novo meteoro seja adicionado à lista quando o evento for executado.
Para isso usamos um loop for, apresentado no primeiro capítulo:
for evento in pygame.event.get(): ... if evento.type == criar_novo_meteoro: meteoro = criar_meteoro() meteoros.append(meteoro)
Listas em Python possuem métodos nativos para manipular seus valores.
append()é um deles — ele adiciona um novo elemento ao final da lista.
Agora podemos deixar a lista de meteoros vazia.
meteoros = []
Ao executar o jogo, você verá que os meteoros serão gerados continuamente, criando uma chuva de meteoros.
O que Aprendemos até Agora?
Aqui você criou uma chuva de meteoros funcional, adicionando um perigo real para a nave, que será obrigada a se mover e desviar dos obstáculos.
Neste capítulo você:
- Usou uma função para criar um elemento de jogo
- Armazenou itens em uma lista de forma automática
- Automatizou a criação com eventos do Pygame
Código Completo
import pygame import random pygame.init() janela = pygame.display.set_mode((800, 600)) clock = pygame.time.Clock() nave_img = pygame.image.load("img/nave.png") nave = { "imagem": nave_img, "x": janela.get_width() // 2 - nave_img.get_width() // 2, "y": janela.get_height() // 2 - nave_img.get_height() // 2, "velocidade": 5, } def criar_meteoro(): meteoro_img = pygame.image.load("img/meteoro.png") meteoro = { "imagem": meteoro_img, "x": random.randint(0, janela.get_width()), "y": -100, "largura": meteoro_img.get_width(), "altura": meteoro_img.get_height(), "velocidade": 2, "rotacao": 0, "dir_rotacao": random.randint(-1, 1), } return meteoro meteoros = [] criar_novo_meteoro = pygame.USEREVENT + 1 pygame.time.set_timer(criar_novo_meteoro, 1500) rodando = True while rodando: for evento in pygame.event.get(): if evento.type == pygame.QUIT: rodando = False if evento.type == criar_novo_meteoro: meteoro = criar_meteoro() meteoros.append(meteoro) teclas = pygame.key.get_pressed() if teclas[pygame.K_w]: nave["y"] -= nave["velocidade"] if teclas[pygame.K_s]: nave["y"] += nave["velocidade"] if teclas[pygame.K_a]: nave["x"] -= nave["velocidade"] if teclas[pygame.K_d]: nave["x"] += nave["velocidade"] if nave["x"] <= 0: nave["x"] = 0 elif nave["x"] + nave["imagem"].get_width() >= janela.get_width(): nave["x"] = janela.get_width() - nave["imagem"].get_width() if nave["y"] <= 0: nave["y"] = 0 elif nave["y"] + nave["imagem"].get_height() >= janela.get_height(): nave["y"] = janela.get_height() - nave["imagem"].get_height() janela.fill("black") janela.blit(nave["imagem"], (nave["x"], nave["y"])) for meteoro in meteoros: meteoro["y"] += meteoro["velocidade"] meteoro["rotacao"] += meteoro["dir_rotacao"] imagem_rotacionada = pygame.transform.rotate(meteoro["imagem"], meteoro["rotacao"]) retangulo_rotacionado = imagem_rotacionada.get_rect(center = (meteoro["x"], meteoro["y"])) janela.blit(imagem_rotacionada, retangulo_rotacionado) pygame.display.flip() clock.tick(60) pygame.quit()
Próximo Passo: Implementando Colisões
A chuva de meteoros está funcionando. Mas o jogo não sabe que eles existem de verdade. Um meteoro pode atravessar a nave inteira sem consequência.
No próximo capítulo vamos mudar isso: vamos ensinar o Pygame a detectar o momento exato em que dois objetos se tocam, encerrando o jogo quando a nave for atingida.