Introducción
En este laboratorio, aprenderá a crear un juego interactivo de Pong utilizando Matplotlib. Pong es un juego arcade clásico que implica dos jugadores golpeando una pelota de un lado a otro a través de un campo de juego virtual utilizando palas. Este laboratorio lo guiará a través del proceso de construcción de su propio juego de Pong utilizando Python y Matplotlib.
Consejos sobre la VM
Una vez finalizada la inicialización de la VM, haga clic en la esquina superior izquierda para cambiar a la pestaña Cuaderno y acceder a Jupyter Notebook para practicar.
A veces, es posible que tenga que esperar unos segundos a que Jupyter Notebook termine de cargarse. La validación de operaciones no puede automatizarse debido a las limitaciones de Jupyter Notebook.
Si tiene problemas durante el aprendizaje, no dude en preguntar a Labby. Deje su retroalimentación después de la sesión y resolveremos rápidamente el problema para usted.
Instalar Matplotlib
Antes de comenzar, debemos asegurarnos de que Matplotlib esté instalado. Puede instalar Matplotlib utilizando pip ejecutando el siguiente comando en su terminal:
pip install matplotlib
Importar bibliotecas
Lo primero que debemos hacer es importar las bibliotecas necesarias. Vamos a utilizar time, numpy y matplotlib.
import time
import matplotlib.pyplot as plt
import numpy as np
Definir las clases Pad y Puck
A continuación, debemos definir las clases Pad y Puck. La clase Pad representa las palas utilizadas por los jugadores, mientras que la clase Puck representa la pelota.
class Pad:
def __init__(self, disp, x, y, type='l'):
self.disp = disp
self.x = x
self.y = y
self.w =.3
self.score = 0
self.xoffset = 0.3
self.yoffset = 0.1
if type == 'r':
self.xoffset *= -1.0
if type == 'l' or type == 'r':
self.signx = -1.0
self.signy = 1.0
else:
self.signx = 1.0
self.signy = -1.0
def contains(self, loc):
return self.disp.get_bbox().contains(loc.x, loc.y)
class Puck:
def __init__(self, disp, pad, field):
self.vmax =.2
self.disp = disp
self.field = field
self._reset(pad)
def _reset(self, pad):
self.x = pad.x + pad.xoffset
if pad.y < 0:
self.y = pad.y + pad.yoffset
else:
self.y = pad.y - pad.yoffset
self.vx = pad.x - self.x
self.vy = pad.y + pad.w/2 - self.y
self._speedlimit()
self._slower()
self._slower()
def update(self, pads):
self.x += self.vx
self.y += self.vy
for pad in pads:
if pad.contains(self):
self.vx *= 1.2 * pad.signx
self.vy *= 1.2 * pad.signy
fudge =.001
## probablemente más limpio con algo como...
if self.x < fudge:
pads[1].score += 1
self._reset(pads[0])
return True
if self.x > 7 - fudge:
pads[0].score += 1
self._reset(pads[1])
return True
if self.y < -1 + fudge or self.y > 1 - fudge:
self.vy *= -1.0
## agregar algo de aleatoriedad, solo para hacerlo interesante
self.vy -= (randn()/300.0 + 1/300.0) * np.sign(self.vy)
self._speedlimit()
return False
def _slower(self):
self.vx /= 5.0
self.vy /= 5.0
def _faster(self):
self.vx *= 5.0
self.vy *= 5.0
def _speedlimit(self):
if self.vx > self.vmax:
self.vx = self.vmax
if self.vx < -self.vmax:
self.vx = -self.vmax
if self.vy > self.vmax:
self.vy = self.vmax
if self.vy < -self.vmax:
self.vy = -self.vmax
Definir la clase Game
Ahora que tenemos definidas las clases Pad y Puck, podemos pasar a definir la clase Game. Esta clase se encargará de manejar la lógica del juego y de dibujar el juego en la pantalla.
class Game:
def __init__(self, ax):
## crear la línea inicial
self.ax = ax
ax.xaxis.set_visible(False)
ax.set_xlim([0, 7])
ax.yaxis.set_visible(False)
ax.set_ylim([-1, 1])
pad_a_x = 0
pad_b_x =.50
pad_a_y = pad_b_y =.30
pad_b_x += 6.3
## palas
pA, = self.ax.barh(pad_a_y,.2,
height=.3, color='k', alpha=.5, edgecolor='b',
lw=2, label="Player B",
animated=True)
pB, = self.ax.barh(pad_b_y,.2,
height=.3, left=pad_b_x, color='k', alpha=.5,
edgecolor='r', lw=2, label="Player A",
animated=True)
## distracciones
self.x = np.arange(0, 2.22*np.pi, 0.01)
self.line, = self.ax.plot(self.x, np.sin(self.x), "r",
animated=True, lw=4)
self.line2, = self.ax.plot(self.x, np.cos(self.x), "g",
animated=True, lw=4)
self.line3, = self.ax.plot(self.x, np.cos(self.x), "g",
animated=True, lw=4)
self.line4, = self.ax.plot(self.x, np.cos(self.x), "r",
animated=True, lw=4)
## línea central
self.centerline, = self.ax.plot([3.5, 3.5], [1, -1], 'k',
alpha=.5, animated=True, lw=8)
## puck (s)
self.puckdisp = self.ax.scatter([1], [1], label='_nolegend_',
s=200, c='g',
alpha=.9, animated=True)
self.canvas = self.ax.figure.canvas
self.background = None
self.cnt = 0
self.distract = True
self.res = 100.0
self.on = False
self.inst = True ## mostrar instrucciones desde el principio
self.pads = [Pad(pA, pad_a_x, pad_a_y),
Pad(pB, pad_b_x, pad_b_y, 'r')]
self.pucks = []
self.i = self.ax.annotate(instructions, (.5, 0.5),
name='monospace',
verticalalignment='center',
horizontalalignment='center',
multialignment='left',
xycoords='axes fraction',
animated=False)
self.canvas.mpl_connect('key_press_event', self.on_key_press)
def draw(self):
draw_artist = self.ax.draw_artist
if self.background is None:
self.background = self.canvas.copy_from_bbox(self.ax.bbox)
## restaurar el fondo limpio
self.canvas.restore_region(self.background)
## mostrar las distracciones
if self.distract:
self.line.set_ydata(np.sin(self.x + self.cnt/self.res))
self.line2.set_ydata(np.cos(self.x - self.cnt/self.res))
self.line3.set_ydata(np.tan(self.x + self.cnt/self.res))
self.line4.set_ydata(np.tan(self.x - self.cnt/self.res))
draw_artist(self.line)
draw_artist(self.line2)
draw_artist(self.line3)
draw_artist(self.line4)
## pucks y palas
if self.on:
self.ax.draw_artist(self.centerline)
for pad in self.pads:
pad.disp.set_y(pad.y)
pad.disp.set_x(pad.x)
self.ax.draw_artist(pad.disp)
for puck in self.pucks:
if puck.update(self.pads):
## solo llegamos aquí si alguien marcó un gol
self.pads[0].disp.set_label(f" {self.pads[0].score}")
self.pads[1].disp.set_label(f" {self.pads[1].score}")
self.ax.legend(loc='center', framealpha=.2,
facecolor='0.5',
prop=FontProperties(size='xx-large',
weight='bold'))
self.background = None
self.ax.figure.canvas.draw_idle()
return
puck.disp.set_offsets([[puck.x, puck.y]])
self.ax.draw_artist(puck.disp)
## solo volver a dibujar el rectángulo de los ejes
self.canvas.blit(self.ax.bbox)
self.canvas.flush_events()
if self.cnt == 50000:
## solo para no excedernos
print("...y has estado jugando demasiado tiempo!!!")
plt.close()
self.cnt += 1
def on_key_press(self, event):
if event.key == '3':
self.res *= 5.0
if event.key == '4':
self.res /= 5.0
if event.key == 'e':
self.pads[0].y +=.1
if self.pads[0].y > 1 -.3:
self.pads[0].y = 1 -.3
if event.key == 'd':
self.pads[0].y -=.1
if self.pads[0].y < -1:
self.pads[0].y = -1
if event.key == 'i':
self.pads[1].y +=.1
if self.pads[1].y > 1 -.3:
self.pads[1].y = 1 -.3
if event.key == 'k':
self.pads[1].y -=.1
if self.pads[1].y < -1:
self.pads[1].y = -1
if event.key == 'a':
self.pucks.append(Puck(self.puckdisp,
self.pads[randint(2)],
self.ax.bbox))
if event.key == 'A' and len(self.pucks):
self.pucks.pop()
if event.key == '':
self.pucks[0]._reset(self.pads[randint(2)])
if event.key == '1':
for p in self.pucks:
p._slower()
if event.key == '2':
for p in self.pucks:
p._faster()
if event.key == 'n':
self.distract = not self.distract
if event.key == 'g':
self.on = not self.on
if event.key == 't':
self.inst = not self.inst
self.i.set_visible(not self.i.get_visible())
self.background = None
self.canvas.draw_idle()
if event.key == 'q':
plt.close()
Crear la animación del juego
Ahora que tenemos definida la clase Game, podemos crear la animación del juego instanciando un objeto Game y llamando a su método draw() en un bucle.
fig, ax = plt.subplots()
canvas = ax.figure.canvas
animation = Game(ax)
## deshabilitar los enlaces de teclado predeterminados
if fig.canvas.manager.key_press_handler_id is not None:
canvas.mpl_disconnect(fig.canvas.manager.key_press_handler_id)
## restablecer el fondo de la técnica de transferencia de bits en la redibujado
def on_redraw(event):
animation.background = None
## iniciar después del primer dibujo
def start_anim(event):
canvas.mpl_disconnect(start_anim.cid)
start_anim.timer.add_callback(animation.draw)
start_anim.timer.start()
canvas.mpl_connect('draw_event', on_redraw)
start_anim.cid = canvas.mpl_connect('draw_event', start_anim)
start_anim.timer = animation.canvas.new_timer(interval=1)
tstart = time.time()
plt.show()
print('FPS: %f' % (animation.cnt/(time.time() - tstart)))
Resumen
En este laboratorio, aprendiste cómo crear un juego interactivo de Pong utilizando Matplotlib. Aprendiste cómo definir las clases Pad y Puck, así como la clase Game, que se encarga de manejar la lógica del juego y de dibujar el juego en la pantalla. También aprendiste cómo crear la animación del juego instanciando un objeto Game y llamando a su método draw() en un bucle. Con este conocimiento, ahora puedes crear tus propios juegos interactivos utilizando Python y Matplotlib.