Neste segundo post, vamos melhorar nosso jogo.
Embora a nova versão rode em Python, ainda está muito anos 80.
A primeira coisa a corrigir é a animação. Como estamos usando um sistema de coordenadas de 280 por 192 pontos para simular as coordenadas do Apple, multiplicamos cada coordenada por 4 na hora de desenhar. Para deixar parecido com o Apple, eu reduzi o número de frames a 8 frames por segundo. Por isso a animação dá tantos saltos! Para fazer com que rode a 60 frames, temos que multiplicar as velocidades pela razão entre o número de frames antigos e o novo: 8/60. A nova versão define algumas constantes para isso:
FRAMES_ORIGINAL = 8 FRAMES_NOVO = 60 RATIO = FRAMES_ORIGINAL / FRAMES_NOVO
Olhando o código, fica fácil perceber que o tiro, o avião e as bases são objetos. Vamos extrair o código do jogo e dividir as operações em atualiza e desenha. No caso, atualiza calcula a nova posição do objeto e desenha realiza o desenho em si.
class Aviao: def __init__(self, jogo, cor=3): self.cor = cor self.velocidade_aviao = (random.randint(0, 6) + 4) * RATIO self.altura_do_aviao = 159 - (random.randint(0, 135) + 11) self.posicao_do_aviao = self.velocidade_aviao self.ativo = True self.jogo = jogo def desenha(self): y = self.altura_do_aviao x = self.posicao_do_aviao set_color(self.cor) pyglet.graphics.draw(6, pyglet.gl.GL_LINE_LOOP, ('v2f', (x, y, x, y - 8, x + 3, y - 2, x + 12, y - 2, x + 14, y, x, y))) def desativa(self): self.ativo = False self.jogo.desativa(self) def atualiza(self): self.posicao_do_aviao += self.velocidade_aviao if self.posicao_do_aviao > 265: self.desativa()
O código completo pode ser visto aqui:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import pyglet | |
from pyglet.gl import * | |
import random | |
def rgb_to_f(r, g, b): | |
return(r / 255.0, g / 255.0, b / 255.0) | |
# Fonte: https://github.com/AppleWin/AppleWin/issues/254 | |
CORES = [(0.0, 0.0, 0.0), # Preto 0 | |
rgb_to_f(20, 245, 60), # Verde 1 | |
rgb_to_f(255, 68, 253), # Magenta 2 | |
rgb_to_f(255, 255, 255), # Branco 3 | |
rgb_to_f(255, 106, 60), # Laranja 5 | |
rgb_to_f(20, 207, 253), # Azul médio 6 | |
rgb_to_f(255, 255, 255), # Branco 7 | |
] | |
FRAMES_ORIGINAL = 8 | |
FRAMES_NOVO = 60 | |
RATIO = FRAMES_ORIGINAL / FRAMES_NOVO | |
def set_color(color): | |
glColor3f(*CORES[color]) | |
class Aviao: | |
def __init__(self, jogo, cor=3): | |
self.cor = cor | |
self.velocidade_aviao = (random.randint(0, 6) + 4) * RATIO | |
self.altura_do_aviao = 159 - (random.randint(0, 135) + 11) | |
self.posicao_do_aviao = self.velocidade_aviao | |
self.ativo = True | |
self.jogo = jogo | |
def desenha(self): | |
y = self.altura_do_aviao | |
x = self.posicao_do_aviao | |
set_color(self.cor) | |
pyglet.graphics.draw(6, pyglet.gl.GL_LINE_LOOP, | |
('v2f', (x, y, | |
x, y - 8, | |
x + 3, y - 2, | |
x + 12, y - 2, | |
x + 14, y, | |
x, y))) | |
def desativa(self): | |
self.ativo = False | |
self.jogo.desativa(self) | |
def atualiza(self): | |
self.posicao_do_aviao += self.velocidade_aviao | |
if self.posicao_do_aviao > 265: | |
self.desativa() | |
class Tiro: | |
def __init__(self, jogo, posicao): | |
self.y = 159 | |
self.ativo = True | |
self.posicao = (posicao + 1) * 70 | |
self.velocidade_do_tiro = 5 * RATIO | |
self.jogo = jogo | |
self.cor = 3 | |
def desenha(self): | |
set_color(self.cor) | |
pyglet.graphics.draw(2, pyglet.gl.GL_LINES, | |
('v2f', (self.posicao, self.y, | |
self.posicao, self.y - 4))) | |
def atualiza(self): | |
self.y -= self.velocidade_do_tiro | |
def desativa(self): | |
self.ativo = False | |
self.jogo.desativa(self) | |
class Base: | |
def __init__(self, jogo, posicao): | |
self.y = 159 | |
self.cor = 3 | |
self.ativo = True | |
self.posicao = (posicao + 1) * 70 | |
self.velocidade_do_tiro = 5 * RATIO | |
self.jogo = jogo | |
def desenha(self): | |
set_color(3) | |
pyglet.graphics.draw(2, pyglet.gl.GL_LINES, | |
('v2f', (self.posicao - 5, self.y, | |
self.posicao + 5, self.y))) | |
def atualiza(self): | |
pass | |
def desativa(self): | |
pass | |
class Game: | |
def __init__(self, parent): | |
self.cor = CORES | |
self.window = parent | |
self.tiros_disparados = 0 # tiros já disparados | |
self.estado = "jogando" | |
self.pressionado = False | |
self.label = None | |
self.aviao = Aviao(self) | |
self.tiros = [] | |
self.bases = [Base(self, base) for base in range(0, 3)] | |
def desativa(self, objeto): | |
pass | |
def mostra_mensagem(self): | |
if self.label: | |
glPushMatrix() | |
glScalef(0.25, -0.25, 0.25) | |
self.label.draw() | |
pyglet.graphics.draw(4, pyglet.gl.GL_LINES, | |
('v2f', (0, 0, | |
280, 192, | |
280, 0, | |
0, 0))) | |
glPopMatrix() | |
def atualiza(self): | |
self.jogue() | |
if self.estado != "jogando": | |
if self.pressionado: | |
pyglet.app.event_loop.exit() | |
self.mostra_mensagem() | |
def jogue(self): | |
for base in self.bases: | |
base.desenha() | |
self.aviao.desenha() | |
self.processa_tiros() | |
if self.estado == "jogando": | |
if not self.aviao.ativo: | |
self.perdeu() | |
return | |
self.aviao.atualiza() | |
if self.pressionado and self.tiros_disparados < 3: | |
self.tiros.append(Tiro(self, self.tiros_disparados)) | |
self.tiros_disparados += 1 | |
self.pressionado = False | |
def processa_tiros(self): | |
for tiro in self.tiros: | |
if not tiro.ativo: | |
continue | |
tiro.desenha() | |
if self.estado == "jogando": | |
tiro.atualiza() | |
if (-1 < tiro.posicao - self.aviao.posicao_do_aviao < 15 and | |
-1 <= self.aviao.altura_do_aviao - tiro.y <= 5): | |
self.acertou() | |
def perdeu(self): | |
self.imprima("MISSED") | |
self.muda_estado("fimdejogo") | |
def acertou(self): | |
self.imprima("HIT!!!") | |
self.muda_estado("fimdejogo") | |
def muda_estado(self, estado): | |
print("Mudança de Estado: {} --> {}".format(self.estado, estado)) | |
self.estado = estado | |
def imprima(self, mensagem): | |
self.label = self.window.crialabel(mensagem, x=280 * 2, y=-192 * 2, tamanho=100) | |
class Missile(pyglet.window.Window): | |
def __init__(self): | |
self.scale = 4 # Escala os gráficos 4x | |
self.frames = FRAMES_NOVO # Frames por segundo. | |
# Define quantas vezes vamos atualizar a tela por segundo | |
super(Missile, self).__init__(280 * self.scale, | |
192 * self.scale, | |
caption="Missiles") | |
self.set_mouse_visible(False) | |
self.game = Game(self) | |
self.schedule = pyglet.clock.schedule_interval( | |
func=self.update, interval=1.0 / self.frames) | |
def update(self, interval): | |
pass | |
def on_draw(self): | |
window.clear() | |
self.game.atualiza() | |
def on_resize(self, width, height): | |
# Inicializa a view | |
glViewport(0, 0, width, height) | |
glMatrixMode(gl.GL_PROJECTION) | |
glLoadIdentity() | |
# Inverte as coordenadas do eixo Y | |
glOrtho(0, width, height, 0, -1, 1) | |
# Aplica a escala | |
glScalef(self.scale, self.scale, 1.0) | |
glMatrixMode(gl.GL_MODELVIEW) | |
def on_key_press(self, symbol, modifiers): | |
self.game.pressionado = True | |
def crialabel(self, mensagem, x=None, y=None, | |
fonte='Times New Roman', tamanho=36): | |
"""Prepara uma mensagem de texto para ser exibida""" | |
x = x or self.width // 2 | |
y = y or self.height // 2 | |
return pyglet.text.Label( | |
mensagem, font_name=fonte, font_size=tamanho, | |
x=x, y=y, anchor_x='center', anchor_y='center') | |
window = Missile() | |
pyglet.app.run() |
Os próximos passos são:
- Limpar as classes
- Vários aviões
- Múltiplos tiros.
- Generalizar os objetos do jogo em uma super classe.
- Exibir um placar
- Atribuir teclas para atirar e jogar de novo ou sair.
Até o próximo!
Nenhum comentário:
Postar um comentário