Introdução
Neste projeto, vamos guiá-lo através do processo de criação de um jogo simples chamado "Aliens" usando a biblioteca Pygame. O jogo envolve defender-se contra invasores alienígenas, atirando neles. Vamos dividir o processo de desenvolvimento em várias etapas, desde a configuração dos arquivos do projeto até a execução do jogo completo.
Pygame é uma biblioteca popular para criar jogos 2D em Python. Ela fornece funções para lidar com gráficos, som e entrada do usuário, tornando-a uma excelente escolha para iniciantes interessados em desenvolvimento de jogos.
👀 Pré-visualização

Este jogo é revisado a partir dos exemplos do Pygame.
🎯 Tarefas
Neste projeto, você aprenderá:
- Como configurar a estrutura inicial do projeto e carregar recursos necessários, como imagens e sons.
- Como definir as classes para o personagem do jogador e os invasores alienígenas.
- Como criar classes adicionais para lidar com explosões, tiros do jogador, bombas alienígenas e pontuação do jogo.
- Como inicializar o jogo, carregar recursos e configurar a janela do jogo.
- Como implementar o loop principal do jogo, lidar com a entrada do jogador, atualizar as entidades do jogo, lidar com colisões e desenhar a cena do jogo.
🏆 Conquistas
Após concluir este projeto, você será capaz de:
- Usar a biblioteca Pygame para desenvolver um jogo 2D.
- Carregar e exibir imagens no Pygame.
- Lidar com a entrada do usuário e controlar o movimento do jogador.
- Criar e atualizar entidades do jogo usando classes de sprites.
- Lidar com colisões entre entidades do jogo.
- Desenhar a cena do jogo e atualizar a tela.
- Tocar efeitos sonoros e música no jogo.
- Implementar um loop principal do jogo para gerenciar a lógica do jogo.
Criar os Arquivos do Projeto
Nesta etapa, vamos configurar a estrutura inicial do projeto e carregar os recursos necessários, como imagens e sons.
Crie um novo arquivo chamado aliens.py e adicione o seguinte código:
cd ~/project
touch aliens.py
sudo pip install pygame
Em seguida, abra o arquivo em seu editor de texto favorito e adicione o seguinte código:
import os
import random
from typing import List
## import basic pygame modules
import pygame as pg
## see if we can load more than standard BMP
if not pg.image.get_extended():
raise SystemExit("Sorry, extended image module required")
## game constants
MAX_SHOTS = 2 ## most player bullets onscreen
ALIEN_ODDS = 22 ## chances a new alien appears
BOMB_ODDS = 60 ## chances a new bomb will drop
ALIEN_RELOAD = 12 ## frames between new aliens
SCREENRECT = pg.Rect(0, 0, 640, 480)
SCORE = 0
main_dir = os.path.split(os.path.abspath(__file__))[0]
def load_image(file):
"""loads an image, prepares it for play"""
file = os.path.join(main_dir, "data", file)
try:
surface = pg.image.load(file)
except pg.error:
raise SystemExit(f'Could not load image "{file}" {pg.get_error()}')
return surface.convert()
def load_sound(file):
"""because pygame can be compiled without mixer."""
if not pg.mixer:
return None
file = os.path.join(main_dir, "data", file)
try:
sound = pg.mixer.Sound(file)
return sound
except pg.error:
print(f"Warning, unable to load, {file}")
return None
Nesta etapa, importamos os módulos necessários, definimos as constantes do jogo e criamos funções para carregar imagens e efeitos sonoros. Também configuramos o diretório principal do projeto.
Em def load_image e def load_sound, usamos a função os.path.join para juntar o diretório principal do projeto com o diretório data, onde armazenaremos nossos recursos do jogo. Criaremos este diretório na próxima etapa.
Definir as Classes do Jogador e Alienígenas
Agora, vamos definir as classes para os personagens do nosso jogo: Player (Jogador) e Alien (Alienígena). A classe Player representa o personagem do jogador, enquanto a classe Alien representa os invasores alienígenas.
## ... (código da etapa 1)
## Define a classe Player
class Player(pg.sprite.Sprite):
"""Representando o jogador como um carro tipo buggy lunar."""
speed = 10
bounce = 24
gun_offset = -11
images: List[pg.Surface] = []
def __init__(self, *groups):
pg.sprite.Sprite.__init__(self, *groups)
self.image = self.images[0]
self.rect = self.image.get_rect(midbottom=SCREENRECT.midbottom)
self.reloading = 0
self.origtop = self.rect.top
self.facing = -1
def move(self, direction):
if direction:
self.facing = direction
self.rect.move_ip(direction * self.speed, 0)
self.rect = self.rect.clamp(SCREENRECT)
if direction < 0:
self.image = self.images[0]
elif direction > 0:
self.image = self.images[1]
self.rect.top = self.origtop - (self.rect.left // self.bounce % 2)
def gunpos(self):
pos = self.facing * self.gun_offset + self.rect.centerx
return pos, self.rect.top
Aqui, definimos a classe Player. Ela inclui atributos como velocidade, "bounce" (quique) e deslocamento da arma. O método move lida com o movimento do jogador. O método gunpos retorna a posição da arma do jogador. A classe Player herda da classe pg.sprite.Sprite, que é uma classe base para objetos de jogo visíveis.
## Define a classe Alien
class Alien(pg.sprite.Sprite):
"""Uma nave espacial alienígena. Que se move lentamente para baixo na tela."""
speed = 13
animcycle = 12
images: List[pg.Surface] = []
def __init__(self, *groups):
pg.sprite.Sprite.__init__(self, *groups)
self.image = self.images[0]
self.rect = self.image.get_rect()
self.facing = random.choice((-1, 1)) * Alien.speed
self.frame = 0
if self.facing < 0:
self.rect.right = SCREENRECT.right
def update(self):
self.rect.move_ip(self.facing, 0)
if not SCREENRECT.contains(self.rect):
self.facing = -self.facing
self.rect.top = self.rect.bottom + 1
self.rect = self.rect.clamp(SCREENRECT)
self.frame = self.frame + 1
self.image = self.images[self.frame // self.animcycle % 3]
A classe Alien representa os invasores alienígenas. Ela tem atributos para velocidade e animação. Os alienígenas se movem para a esquerda e para a direita, e suas imagens se alternam. O método update lida com o movimento e a animação dos alienígenas. A classe Alien herda da classe pg.sprite.Sprite, que é uma classe base para objetos de jogo visíveis.
Definir Classes Adicionais do Jogo
Nesta etapa, definiremos classes adicionais relacionadas ao jogo: Explosion (Explosão), Shot (Tiro), Bomb (Bomba) e Score (Pontuação). Essas classes lidam com explosões, tiros do jogador, bombas alienígenas e a pontuação do jogo.
## ... (código da etapa 2)
## Define a classe Explosion
class Explosion(pg.sprite.Sprite):
"""Uma explosão. Esperamos que seja do Alien e não do jogador!"""
defaultlife = 12
animcycle = 3
images: List[pg.Surface] = []
def __init__(self, actor, *groups):
pg.sprite.Sprite.__init__(self, *groups)
self.image = self.images[0]
self.rect = self.image.get_rect(center=actor.rect.center)
self.life = self.defaultlife
def update(self):
"""chamado a cada volta do loop do jogo.
Mostra a superfície da explosão por 'defaultlife'.
A cada tick (atualização) do jogo, diminuímos a 'life'.
Também animamos a explosão.
"""
self.life = self.life - 1
self.image = self.images[self.life // self.animcycle % 2]
if self.life <= 0:
self.kill()
## Define a classe Shot
class Shot(pg.sprite.Sprite):
"""Uma bala que o sprite do Jogador dispara."""
speed = -11
images: List[pg.Surface] = []
def __init__(self, pos, *groups):
pg.sprite.Sprite.__init__(self, *groups)
self.image = self.images[0]
self.rect = self.image.get_rect(midbottom=pos)
def update(self):
"""chamado a cada volta do loop do jogo.
A cada tick, movemos o tiro para cima.
"""
self.rect.move_ip(0, self.speed)
if self.rect.top <= 0:
self.kill()
## Define a classe Bomb
class Bomb(pg.sprite.Sprite):
"""Uma bomba que os alienígenas lançam."""
speed = 9
images: List[pg.Surface] = []
def __init__(self, alien, explosion_group, *groups):
pg.sprite.Sprite.__init__(self, *groups)
self.image = self.images[0]
self.rect = self.image.get_rect(midbottom=alien.rect.move(0, 5).midbottom)
self.explosion_group = explosion_group
def update(self):
"""chamado a cada volta do loop do jogo.
A cada frame, movemos o 'rect' do sprite para baixo.
Quando ele atinge a parte inferior, nós:
- fazemos uma explosão.
- removemos a Bomba.
"""
self.rect.move_ip(0, self.speed)
if self.rect.bottom >= 470:
Explosion(self, self.explosion_group)
self.kill()
## Define a classe Score
class Score(pg.sprite.Sprite):
"""para acompanhar a pontuação."""
def __init__(self, *groups):
pg.sprite.Sprite.__init__(self, *groups)
self.font = pg.font.Font(None, 20)
self.font.set_italic(1)
self.color = "white"
self.lastscore = -1
self.update()
self.rect = self.image.get_rect().move(10, 450)
def update(self):
"""Atualizamos a pontuação em update() somente quando ela mudou."""
if SCORE != self.lastscore:
self.lastscore = SCORE
msg = f"Score: {SCORE}"
self.image = self.font.render(msg, 0, self.color)
Essas classes lidam com diferentes aspectos do jogo, como explosões, tiros, bombas e exibição da pontuação do jogador.
Inicializar o Jogo e Carregar Recursos
Agora, continuaremos inicializando o jogo e carregando os recursos necessários, como imagens e sons.
## ... (código da etapa 3)
def main(winstyle=0):
## Inicializar pygame
if pg.get_sdl_version()[0] == 2:
pg.mixer.pre_init(44100, 32, 2, 1024)
pg.init()
if pg.mixer and not pg.mixer.get_init():
print("Warning, no sound")
pg.mixer = None
fullscreen = False
## Definir o modo de exibição
winstyle = 0 ## |FULLSCREEN
bestdepth = pg.display.mode_ok(SCREENRECT.size, winstyle, 32)
screen = pg.display.set_mode(SCREENRECT.size, winstyle, bestdepth)
## Carregar imagens, atribuir às classes de sprite
## (faça isso antes que as classes sejam usadas, após a configuração da tela)
img = load_image("player1.gif")
Player.images = [img, pg.transform.flip(img, 1, 0)]
img = load_image("explosion1.gif")
Explosion.images = [img, pg.transform.flip(img, 1, 1)]
Alien.images = [load_image(im) for im in ("alien1.gif", "alien2.gif", "alien3.gif")]
Bomb.images = [load_image("bomb.gif")]
Shot.images = [load_image("shot.gif")]
Aqui, inicializamos o Pygame, configuramos a janela do jogo e carregamos imagens para o jogador, alienígenas, explosões, bombas e tiros.
Completar a Configuração do Jogo e o Loop Principal
Agora, completaremos a configuração do jogo e criaremos o loop principal do jogo, onde toda a lógica e interações do jogo ocorrerão.
## ... (código da etapa 4)
def main(winstyle=0):
## ... (código anterior)
## decorar a janela do jogo
icon = pg.transform.scale(Alien.images[0], (32, 32))
pg.display.set_icon(icon)
pg.display.set_caption("Pygame Aliens")
pg.mouse.set_visible(0)
## criar o fundo, telhar a imagem bgd
bgdtile = load_image("background.gif")
background = pg.Surface(SCREENRECT.size)
for x in range(0, SCREENRECT.width, bgdtile.get_width()):
background.blit(bgdtile, (x, 0))
screen.blit(background, (0, 0))
pg.display.flip()
## carregar os efeitos sonoros
boom_sound = load_sound("boom.wav")
shoot_sound = load_sound("car_door.wav")
if pg.mixer:
music = os.path.join(main_dir, "data", "house_lo.wav")
pg.mixer.music.load(music)
pg.mixer.music.play(-1)
## Inicializar Grupos do Jogo
aliens = pg.sprite.Group()
shots = pg.sprite.Group()
bombs = pg.sprite.Group()
all = pg.sprite.RenderUpdates()
lastalien = pg.sprite.GroupSingle()
## Criar alguns valores iniciais
alienreload = ALIEN_RELOAD
clock = pg.time.Clock()
## inicializar nossos sprites iniciais
global SCORE
player = Player(all)
Alien(
aliens, all, lastalien
) ## note, this 'lives' because it goes into a sprite group
if pg.font:
all.add(Score(all))
## Executar nosso loop principal enquanto o jogador estiver vivo.
while player.alive():
## obter entrada
for event in pg.event.get():
if event.type == pg.QUIT:
return
if event.type == pg.KEYDOWN and event.key == pg.K_ESCAPE:
return
if event.type == pg.KEYDOWN:
if event.key == pg.K_f:
if not fullscreen:
print("Changing to FULLSCREEN")
screen_backup = screen.copy()
screen = pg.display.set_mode(
SCREENRECT.size, winstyle | pg.FULLSCREEN, bestdepth
)
screen.blit(screen_backup, (0, 0))
else:
print("Changing to windowed mode")
screen_backup = screen.copy()
screen = pg.display.set_mode(
SCREENRECT.size, winstyle, bestdepth
)
screen.blit(screen_backup, (0, 0))
pg.display.flip()
fullscreen = not fullscreen
keystate = pg.key.get_pressed()
## limpar/apagar os últimos sprites desenhados
all.clear(screen, background)
## atualizar todos os sprites
all.update()
## lidar com a entrada do jogador
direction = keystate[pg.K_RIGHT] - keystate[pg.K_LEFT]
player.move(direction)
firing = keystate[pg.K_SPACE]
if not player.reloading and firing and len(shots) < MAX_SHOTS:
Shot(player.gunpos(), shots, all)
if pg.mixer and shoot_sound is not None:
shoot_sound.play()
player.reloading = firing
## Criar novo alien
if alienreload:
alienreload = alienreload - 1
elif not int(random.random() * ALIEN_ODDS):
Alien(aliens, all, lastalien)
alienreload = ALIEN_RELOAD
## Soltar bombas
if lastalien and not int(random.random() * BOMB_ODDS):
Bomb(lastalien.sprite, all, bombs, all)
## Detectar colisões entre alienígenas e jogadores.
for alien in pg.sprite.spritecollide(player, aliens, 1):
if pg.mixer and boom_sound is not None:
boom_sound.play()
Explosion(alien, all)
Explosion(player, all)
SCORE = SCORE + 1
player.kill()
## Ver se os tiros atingem os alienígenas.
for alien in pg.sprite.groupcollide(aliens, shots, 1, 1).keys():
if pg.mixer and boom_sound is not None:
boom_sound.play()
Explosion(alien, all)
SCORE = SCORE + 1
## Ver se as bombas alienígenas atingem o jogador.
for bomb in pg.sprite.spritecollide(player, bombs, 1):
if pg.mixer and boom_sound is not None:
boom_sound.play()
Explosion(player, all)
Explosion(bomb, all)
player.kill()
## desenhar a cena
dirty = all.draw(screen)
pg.display.update(dirty)
## limitar a taxa de quadros a 40fps. Também chamado de 40HZ ou 40 vezes por segundo.
clock.tick(40)
if pg.mixer:
pg.mixer.music.fadeout(1000)
pg.time.wait(1000)
## chamar a função "main" se estiver executando este script
if __name__ == "__main__":
main()
pg.quit()
Na função principal, inicializamos grupos de jogos para várias entidades, criamos valores iniciais e implementamos o loop principal do jogo. A lógica do jogo, o tratamento da entrada do jogador, as colisões e o desenho da cena do jogo são realizados dentro desse loop.
Executando o Jogo
Agora, podemos executar o jogo executando o seguinte comando:
cd ~/project
python aliens.py
Executando o Jogo
Agora, podemos executar o jogo executando o seguinte comando:
cd ~/project
python aliens.py

Resumo
Neste projeto, dividimos o processo de construção de um jogo "Aliens" simples usando Pygame em várias etapas. Configuramos os arquivos do projeto, definimos as classes de jogador e alienígena, criamos classes adicionais relacionadas ao jogo e inicializamos o jogo com recursos. Finalmente, implementamos o loop principal do jogo para lidar com a lógica e as interações do jogo.
Nas próximas etapas, continuaremos a adicionar funcionalidade às classes de jogador e alienígena, lidar com movimento e tiro, e atualizar os elementos visuais do jogo.



