Einführung
In diesem Lab lernst du, wie du ein interaktives Pong-Spiel mit Matplotlib erstellst. Pong ist ein klassisches Arcade-Spiel, bei dem zwei Spieler ein Ball mit Schlägern hin und her über ein virtuelles Spielfeld schlagen. In diesem Lab wird dir der Prozess des Erstellens deines eigenen Pong-Spiels mit Python und Matplotlib erläutert.
Tipps für die VM
Nachdem der VM-Start abgeschlossen ist, klicke in der oberen linken Ecke, um zur Registerkarte Notebook zu wechseln und Jupyter Notebook für die Übung zu nutzen.
Manchmal musst du einige Sekunden warten, bis Jupyter Notebook vollständig geladen ist. Die Validierung von Vorgängen kann aufgrund der Einschränkungen in Jupyter Notebook nicht automatisiert werden.
Wenn du bei der Lernphase Probleme hast, kannst du Labby gerne fragen. Gib nach der Sitzung Feedback, und wir werden das Problem für dich prompt beheben.
Matplotlib installieren
Bevor wir beginnen, müssen wir sicherstellen, dass Matplotlib installiert ist. Du kannst Matplotlib mit pip installieren, indem du folgenden Befehl in deiner Konsole ausführst:
pip install matplotlib
Bibliotheken importieren
Das erste, was wir tun müssen, ist, die erforderlichen Bibliotheken zu importieren. Wir werden time, numpy und matplotlib verwenden.
import time
import matplotlib.pyplot as plt
import numpy as np
Definiere die Pad- und Puck-Klassen
Als nächstes müssen wir die Pad- und Puck-Klassen definieren. Die Pad-Klasse repräsentiert die Schläger, die von den Spielern verwendet werden, während die Puck-Klasse den Ball repräsentiert.
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
## wahrscheinlich sauberer mit etwas wie...
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
## füge etwas Zufälligkeit hinzu, um es interessanter zu machen
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
Definiere die Game-Klasse
Jetzt, nachdem wir die Pad- und Puck-Klassen definiert haben, können wir fortfahren und die Game-Klasse definieren. Diese Klasse wird für die Verwaltung der Spiellogik und das Zeichnen des Spiels auf dem Bildschirm verantwortlich sein.
class Game:
def __init__(self, ax):
## erstelle die Anfangslinie
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
## Schläger
pA, = self.ax.barh(pad_a_y,.2,
height=.3, color='k', alpha=.5, edgecolor='b',
lw=2, label="Spieler 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="Spieler A",
animated=True)
## Ablenkungen
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)
## Mittelinie
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 ## zeige die Anweisungen von Anfang an
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)
## wiederherstelle den sauberen Hintergrund
self.canvas.restore_region(self.background)
## zeige die Ablenkungen
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 und Schläger
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):
## wir kommen nur hierhin, wenn jemand einen Punkt gemacht hat
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)
## zeichne nur das Achsenrechteck neu
self.canvas.blit(self.ax.bbox)
self.canvas.flush_events()
if self.cnt == 50000:
## damit wir nicht zu weit gehen
print("...und du spielst schon zu lange!!!")
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()
Erstelle die Spielanimation
Jetzt, nachdem wir die Game-Klasse definiert haben, können wir die Spielanimation erstellen, indem wir ein Game-Objekt instanziieren und in einer Schleife seine draw()-Methode aufrufen.
fig, ax = plt.subplots()
canvas = ax.figure.canvas
animation = Game(ax)
## deaktiviere die standardmäßigen Tastatureingaben
if fig.canvas.manager.key_press_handler_id is not None:
canvas.mpl_disconnect(fig.canvas.manager.key_press_handler_id)
## Setze den Blitting-Hintergrund beim Neuziehen zurück
def on_redraw(event):
animation.background = None
## Starte die Animation nach der ersten Zeichnung
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)))
Zusammenfassung
In diesem Lab haben Sie gelernt, wie Sie ein interaktives Pong-Spiel mit Matplotlib erstellen. Sie haben gelernt, wie Sie die Pad- und Puck-Klassen sowie die Game-Klasse definieren, die für die Verwaltung der Spiellogik und das Zeichnen des Spiels auf dem Bildschirm verantwortlich ist. Sie haben auch gelernt, wie Sie die Spielanimation erstellen, indem Sie ein Game-Objekt instanziieren und in einer Schleife seine draw()-Methode aufrufen. Mit diesen Kenntnissen können Sie jetzt Ihre eigenen interaktiven Spiele mit Python und Matplotlib erstellen.