A nostalgia dos computadores da década de 80 é algo que nunca parei de ter. Quando criança, tive a sorte de utilzar vários computadores de 8-bits, como ZX-81, ZX-Spectrum, Apple II e MSX, ou melhor, seus clones nacionais (TK-85, TK-90X, TK2000), uma vez que o Brasil vivia a época da Reserva do Mercado de Informática.
Numa época que não havia Internet, nós passávamos o tempo a digitar programas. Uma série de livros sobre programação de jogos foi editada pela editora Lutécia no Brasil, mas os originais americanos foram liberados pela Usborne. Neste artigo, eu vou traduzir o jogo principal do Computer Battlegames, chamado de Missile, de Apple II Basic para Python com Pyglet. A listagem original está na página 34 do livro em inglês (ver pdf acima).
10 HOME 20 HGR 30 HCOLOR=3 40 DIM Y(3),F(3) 50 N=1 : MS=5 60 PS=INT(RND(1)*6+4) 70 P=INT(RND(1)*135+11) 80 GOSUB 400 90 FOR I=PS TO 265 STEP PS 100 X=I-PS : Y=159-P : C=0 : GOSUB 300 110 X=I : C=3: GOSUB 300 120 F$="" : IF PEEK(-16384)>127 THEN GET F$ 130 IF F$="" OR N>3 THEN 160 140 F(N)=1 150 N=N+1 160 FOR J=1 TO 3 170 C=0 : GOSUB 350 180 IF F(J)=0 OR Y(J)>145 THEN 230 190 Y(J)=Y(J)+MS 200 C=3 : GOSUB 350 210 X=J*70-I : Y=P-Y(J) 220 IF X>-1 AND X<15 AND Y>-9 AND Y<5 THEN 270 230 NEXT 240 NEXT 250 VTAB 22 : PRINT "MISSED" 260 END 270 VTAB 22 : PRINT "HIT!!!" 280 END 300 HCOLOR=C 310 HPLOT X,Y TO X,Y-8 320 HPLOT TO X+3,Y-2 : HPLOT TO X+12, Y-2 330 HPLOT TO X+14,Y : HPLOT TO X,Y 340 RETURN 350 HCOLOR=C 360 HPLOT 70*J,158-Y(J) TO 70*J,154-Y(J) 340 RETURN 350 HCOLOR=C 360 HPLOT 70*J, 158-Y(J) TO 70*J,154-Y(J) 370 RETURN 400 FOR J=1 TO 3 410 HPLOT 70*J-5,159 TO 70*J+5,159 420 NEXT 430 RETURNVejamos o jogo rodando em um emulador:
Versão comentada:
# Limpa a tela 10 HOME # Entra no modo de alta resolução 280x192 20 HGR # Seleciona a cor 3 (purpura/magenta) 30 HCOLOR=3 # Cria dois vetores com 3 elementos cada 40 DIM Y(3),F(3) # Inicializa N igual a 1 e MS igual a 5 50 N=1 : MS=5 # Gera um número aleatório entre 0 e 6 + 4 60 PS=INT(RND(1)*6+4) # Gera um número aleatório entre 0 e 135 + 11 70 P=INT(RND(1)*135+11) # Desvia para subrotina 80 GOSUB 400 # Loop: repete I de PS até 265, incrementando de PS 90 FOR I=PS TO 265 STEP PS # Calcula os valores de X, Y e C. Desvia para subrotina em 300 100 X=I-PS : Y=159-P : C=0 : GOSUB 300 # Define X e C. Desvia para subrotina 300 110 X=I : C=3: GOSUB 300 # Verifica se uma tecla foi pressionada. Se foi, guarda em F 120 F$="" : IF PEEK(-16384)>127 THEN GET F$ # Se algo foi pressionado ou se N>3 desvia para 160 130 IF F$="" OR N>3 THEN 160 # F[N] = 1 140 F(N)=1 # N+=1 150 N=N+1 # Repete J de 1 até 3 160 FOR J=1 TO 3 # Zera C e desvia para subrotina da linha 350 170 C=0 : GOSUB 350 # Se F[J]==0 ou Y[J]>145 desvia para 230 180 IF F(J)=0 OR Y(J)>145 THEN 230 # Y[J]+=MS 190 Y(J)=Y(J)+MS # C=3. Desvia para subrotina da linha 350 200 C=3 : GOSUB 350 # Calcula X e Y 210 X=J*70-I : Y=P-Y(J) # Se X>-1 e X<15 e Y>-9 e Y<5 desvia para 270 220 IF X>-1 AND X<15 AND Y>-9 AND Y<5 THEN 270 # Fim do loop, volta para o for da linha 160 230 NEXT # Fim do loop, volta para for da linha 90 240 NEXT # Posiciona o cursor na linha 22 e imprime MISSED 250 VTAB 22 : PRINT "MISSED" # Termina o programa 260 END # Posiciona o cursor na linha 22 e imprime HIT!!! 270 VTAB 22 : PRINT "HIT!!!" # Termina o programa 280 END # Troca a cor de desenho para C 300 HCOLOR=C # Traça uma linha de X,Y até X,Y-8 310 HPLOT X,Y TO X,Y-8 # Continua a linha 320 HPLOT TO X+3,Y-2 : HPLOT TO X+12, Y-2 330 HPLOT TO X+14,Y : HPLOT TO X,Y # Retorno da subrotina 340 RETURN # Troca a cor de desenho para C 350 HCOLOR=C # Desenha linha 360 HPLOT 70*J,158-Y(J) TO 70*J,154-Y(J) # Volta da subrotina 340 RETURN # Troca a cor para C 350 HCOLOR=C # Desenha linha 360 HPLOT 70*J, 158-Y(J) TO 70*J,154-Y(J) # Volta da subrotina 370 RETURN # Repete J de 1 à 3 400 FOR J=1 TO 3 # Desenha linha 410 HPLOT 70*J-5,159 TO 70*J+5,159 # Fim do loop, volta para linha 400 420 NEXT # Retorna da subrotina 430 RETURNBom, como precisaremos desenhar, vamos instalar a Pyglet:
pip3 install pyglet
A primeira coisa a fazer é criar a janela da Pyglet e separar a janela do jogo em si. No caso, a janela é responsável por receber os eventos do teclado. A configuração do OpenGL também precisam ser feitas aqui. Como a alta resolução do Apple II é muito pequena 280x192 pontos, eu estou usando uma escala 4. Desta forma, as coordenadas permanecerão as mesmas, mas os gráficos serão 4 vezes maiores. Se ficar muito grande em seu monitor, você pode ajustar a escala.
Eu tentei manter os comentários originais para ficar mais fácil de relacionar o código novo com o antigo.
Outra mudança são as coordenadas Y. No Apple II, Y = 0 é a primeira linha e em OpenGL é a última.
Vejamos como ficou o código da janela:
class Missile(pyglet.window.Window): # 20 HGR def __init__(self): self.scale = 4 # Escala os gráficos 4x self.frames = 8 # 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): # Verifica se uma tecla foi pressionada. Se foi, guarda em F # 120 F$="" : IF PEEK(-16384)>127 THEN GET F$ 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')
Além disso, as cores do Apple II precisam ser definidas:
def rgb_to_f(r, g, b): return(r / 255.0, g / 255.0, b / 255.0) # Cores do modo de alta resolução HGR # 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 ]
e finalmente a classe Game com o jogo em si. Observar que os índices em Python começam em 0 e em Basic começam com 1. Primeiro passo da conversão:
class Game(): def __init__(self, parent): self.cor = CORES self.window = parent # Cria dois vetores com 3 elementos cada # 40 DIM Y(3),F(3) self.Y = [0] * 3 self.F = [0] * 3 # Inicializa N igual a 1 e MS igual a 5 # 50 N=1 : MS=5 self.N = 0 self.MS = 5 # Gera um número aleatório entre 0 e 6 + 4 # 60 PS=INT(RND(1)*6+4) self.PS = random.randint(0, 6) + 4 # Gera um número aleatório entre 0 e 135 + 11 # 70 P=INT(RND(1)*135+11) self.P = random.randint(0, 135) + 11 self.estado = "jogando" self.I = self.PS self.pressionado = False self.label = None def atualiza(self): if self.estado == "jogando": self.jogue() else: if self.label: glMatrixMode(gl.GL_PROJECTION) glLoadIdentity() glOrtho(0, self.window.width, 0, self.window.height, -1, 1) glMatrixMode(gl.GL_MODELVIEW) self.label.draw() if self.pressionado: pyglet.app.event_loop.exit() def jogue(self): # Desvia para subrotina # 80 GOSUB 400 self.sub_400() # Loop: repete I de PS até 265, incrementando de PS # 90 FOR I=PS TO 265 STEP PS if self.I > 265: self.sub_250() return # Calcula os valores de X, Y e C. Desvia para subrotina em 300 # 100 X=I-PS : Y=159-P : C=0 : GOSUB 300 self.X = self.I - self.PS self.y = 159 - self.P self.C = 0 self.sub_300() # Define X e C. Desvia para subrotina 300 # 110 X=I : C=3: GOSUB 300 self.X = self.I self.C = 3 self.sub_300() # Se algo foi pressionado ou se N>3 desvia para 160 # 130 IF F$="" OR N>3 THEN 160 if self.pressionado and self.N < 3: # F[N] = 1 # 140 F(N)=1 self.F[self.N] = 1 # N+=1 # 150 N=N+1 self.N += 1 self.pressionado = False # Repete J de 1 até 3 # 160 FOR J=1 TO 3 for self.J in range(0, 3): # Zera C e desvia para subrotina da linha 350 # 170 C=0 : GOSUB 350 self.C = 0 self.sub_350() # Se F[J]==0 ou Y[J]>145 desvia para 230 # 180 IF F(J)=0 OR Y(J)>145 THEN 230 if self.F[self.J] == 0 or self.Y[self.J] > 145: continue # Y[J]+=MS # 190 Y(J)=Y(J)+MS self.Y[self.J] += self.MS # C=3. Desvia para subrotina da linha 350 # 200 C=3 : GOSUB 350 self.C = 3 self.sub_350() # Calcula X e Y # 210 X=J*70-I : Y=P-Y(J) self.X = (self.J + 1) * 70 - self.I self.y = self.P - self.Y[self.J] # Se X>-1 e X<15 e Y>-9 e Y<5 desvia para 270 # 220 IF X>-1 AND X<15 AND Y>-9 AND Y<5 THEN 270 if self.X > -1 and self.X < 15 and self.y > -9 and self.y < 5: self.sub_270() # Fim do loop, volta para o for da linha 160 # 230 NEXT # Fim do loop, volta para for da linha 90 # 240 NEXT # 90 FOR I=PS TO 265 STEP PS self.I += self.PS def sub_250(self): # Posiciona o cursor na linha 22 e imprime MISSED # 250 VTAB 22 : PRINT "MISSED" print("MISSED") self.imprima("MISSED") # Termina o programa # 260 END self.muda_estado("fimdejogo") def sub_270(self): # Posiciona o cursor na linha 22 e imprime HIT!!! # 270 VTAB 22 : PRINT "HIT!!!" print("HIT") self.imprima("HIT!!!") # Termina o programa self.muda_estado("fimdejogo") def sub_300(self): # Troca a cor de desenho para C # 300 HCOLOR=C self.set_color(self.C) # Traça uma linha de X,Y até X,Y-8 # 310 HPLOT X,Y TO X,Y-8 # Continua a linha # 320 HPLOT TO X+3,Y-2 : HPLOT TO X+12, Y-2 # 330 HPLOT TO X+14,Y : HPLOT TO X,Y pyglet.graphics.draw(6, pyglet.gl.GL_LINE_LOOP, ('v2i', (self.X, self.y, self.X, self.y - 8, self.X + 3, self.y - 2, self.X + 12, self.y - 2, self.X + 14, self.y, self.X, self.y))) # Retorno da subrotina # 340 RETURN def sub_350(self): # Troca a cor para C # 350 HCOLOR=C self.set_color(self.C) # Desenha linha # 360 HPLOT 70*J, 158-Y(J) TO 70*J,154-Y(J) J = self.J + 1 pyglet.graphics.draw(2, pyglet.gl.GL_LINES, ('v2i', (70 * J, 158 - self.Y[self.J], 70 * J, 154 - self.Y[self.J]))) # Volta da subrotina # 370 RETURN def sub_400(self): self.set_color(3) # 400 FOR J=1 TO 3 for J in range(1, 4): # Desenha linha # 410 HPLOT 70*J-5,159 TO 70*J+5,159 pyglet.graphics.draw(2, pyglet.gl.GL_LINES, ('v2i', (70 * J - 5, 159, 70 * J + 5, 159))) # Fim do loop, volta para linha 400 # 420 NEXT # Retorna da subrotina # 430 RETURN def set_color(self, color): glColor3f(*self.cor[color]) 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)
Como em OpenGL limpamos a tela a cada frame, a rotina que apaga os objetos pode ser apagada. No caso, a chamada a sub_300() com C=0.
O nome dos métodos ainda não foram mudados, vamos renomear:
sub_250 para pedeu
sub_270 para acertou
sub_300 para desenha_aviao
sub_350 para desenha_tiro
sub_400 para desenha_bases
Apagando os comentários com o código em Basic, temos:
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) # Cores do modo de alta resolução HGR # 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 ] class Game(): def __init__(self, parent): self.cor = CORES self.window = parent # Cria dois vetores com 3 elementos cada self.Y = [0] * 3 self.F = [0] * 3 # Inicializa N igual a 1 e MS igual a 5 self.N = 0 self.MS = 5 # Gera um número aleatório entre 0 e 6 + 4 self.PS = random.randint(0, 6) + 4 # Gera um número aleatório entre 0 e 135 + 11 self.P = random.randint(0, 135) + 11 self.estado = "jogando" self.I = self.PS self.pressionado = False self.label = None def atualiza(self): if self.estado == "jogando": self.jogue() else: if self.label: glMatrixMode(gl.GL_PROJECTION) glLoadIdentity() glOrtho(0, self.window.width, 0, self.window.height, -1, 1) glMatrixMode(gl.GL_MODELVIEW) self.label.draw() if self.pressionado: pyglet.app.event_loop.exit() def jogue(self): self.desenha_bases() if self.I > 265: self.perdeu() return self.X = self.I - self.PS self.y = 159 - self.P self.C = 0 self.desenha_aviao() self.X = self.I self.C = 3 self.desenha_aviao() if self.pressionado and self.N < 3: self.F[self.N] = 1 self.N += 1 self.pressionado = False for self.J in range(0, 3): if self.F[self.J] == 0 or self.Y[self.J] > 145: continue self.Y[self.J] += self.MS self.C = 3 self.desenha_tiro() self.X = (self.J + 1) * 70 - self.I self.y = self.P - self.Y[self.J] if self.X > -1 and self.X < 15 and self.y > -9 and self.y < 5: self.acertou() self.I += self.PS def perdeu(self): self.imprima("MISSED") self.muda_estado("fimdejogo") def acertou(self): self.imprima("HIT!!!") self.muda_estado("fimdejogo") def desenha_aviao(self): self.set_color(self.C) pyglet.graphics.draw(6, pyglet.gl.GL_LINE_LOOP, ('v2i', (self.X, self.y, self.X, self.y - 8, self.X + 3, self.y - 2, self.X + 12, self.y - 2, self.X + 14, self.y, self.X, self.y))) def desenha_tiro(self): self.set_color(self.C) J = self.J + 1 pyglet.graphics.draw(2, pyglet.gl.GL_LINES, ('v2i', (70 * J, 158 - self.Y[self.J], 70 * J, 154 - self.Y[self.J]))) def desenha_bases(self): self.set_color(3) for J in range(1, 4): pyglet.graphics.draw(2, pyglet.gl.GL_LINES, ('v2i', (70 * J - 5, 159, 70 * J + 5, 159))) def set_color(self, color): glColor3f(*self.cor[color]) 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) class Missile(pyglet.window.Window): def __init__(self): self.scale = 4 # Escala os gráficos 4x self.frames = 8 # 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()
Bem melhor, mas ainda guarda várias características do programa em Basic. O nome das variáveis é uma catástrofe. Renomeando as variáveis e melhorando os comentários, o programa completo fica assim:
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) # Cores do modo de alta resolução HGR # 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 ] class Game(): def __init__(self, parent): self.cor = CORES self.window = parent # Cria dois vetores com 3 elementos cada self.Y = [0] * 3 # Altura do tiro self.F = [0] * 3 # Estado do tiro self.tiros_disparados = 0 # tiros já disparados self.velocidade_do_tiro = 5 # Gera um número aleatório entre 0 e 6 + 4 self.velocidade_aviao = random.randint(0, 6) + 4 # Gera um número aleatório entre 0 e 135 + 11 self.altura_do_aviao = random.randint(0, 135) + 11 self.estado = "jogando" self.posicao_do_aviao = self.velocidade_aviao self.pressionado = False self.label = None def atualiza(self): if self.estado == "jogando": self.jogue() else: if self.label: glMatrixMode(gl.GL_PROJECTION) glLoadIdentity() glOrtho(0, self.window.width, 0, self.window.height, -1, 1) glMatrixMode(gl.GL_MODELVIEW) self.label.draw() if self.pressionado: pyglet.app.event_loop.exit() def jogue(self): self.desenha_bases() if self.posicao_do_aviao > 265: self.perdeu() return self.y = 159 - self.altura_do_aviao self.X = self.posicao_do_aviao self.C = 3 self.desenha_aviao() if self.pressionado and self.tiros_disparados < 3: self.F[self.tiros_disparados] = 1 self.tiros_disparados += 1 self.pressionado = False self.processa_tiros() self.posicao_do_aviao += self.velocidade_aviao def processa_tiros(self): for self.J in range(0, 3): if self.F[self.J] == 0 or self.Y[self.J] > 145: continue self.Y[self.J] += self.velocidade_do_tiro self.C = 3 self.desenha_tiro() self.X = (self.J + 1) * 70 - self.posicao_do_aviao self.y = self.altura_do_aviao - self.Y[self.J] if self.X > -1 and self.X < 15 and self.y > -9 and self.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 desenha_aviao(self): self.set_color(self.C) pyglet.graphics.draw(6, pyglet.gl.GL_LINE_LOOP, ('v2i', (self.X, self.y, self.X, self.y - 8, self.X + 3, self.y - 2, self.X + 12, self.y - 2, self.X + 14, self.y, self.X, self.y))) def desenha_tiro(self): self.set_color(self.C) J = self.J + 1 pyglet.graphics.draw(2, pyglet.gl.GL_LINES, ('v2i', (70 * J, 158 - self.Y[self.J], 70 * J, 154 - self.Y[self.J]))) def desenha_bases(self): self.set_color(3) for J in range(1, 4): pyglet.graphics.draw(2, pyglet.gl.GL_LINES, ('v2i', (70 * J - 5, 159, 70 * J + 5, 159))) def set_color(self, color): glColor3f(*self.cor[color]) 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) class Missile(pyglet.window.Window): def __init__(self): self.scale = 4 # Escala os gráficos 4x self.frames = 8 # 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()
Em um próximo artigo, vou continuar a refatorar o código. Tiro e Avião são claramente classes. Animação deixa a desejar. Mas é o divertido da programação, você sempre pode melhorar.
Nenhum comentário:
Postar um comentário