Pipong
一个基于Matplotlib的Pong游戏,说明了一种编写交互动画的方法,它很容易移植到多个后端pipong.py由Paul Ivanov撰写http://pirsquared.org
import numpy as npimport matplotlib.pyplot as pltfrom numpy.random import randn, randintfrom matplotlib.font_manager import FontPropertiesinstructions = """Player A: Player B:'e' up 'i''d' down 'k'press 't' -- close these instructions(animation will be much faster)press 'a' -- add a puckpress 'A' -- remove a puckpress '1' -- slow down all puckspress '2' -- speed up all puckspress '3' -- slow down distractorspress '4' -- speed up distractorspress ' ' -- reset the first puckpress 'n' -- toggle distractors on/offpress 'g' -- toggle the game on/off"""class Pad(object):def __init__(self, disp, x, y, type='l'):self.disp = dispself.x = xself.y = yself.w = .3self.score = 0self.xoffset = 0.3self.yoffset = 0.1if type == 'r':self.xoffset *= -1.0if type == 'l' or type == 'r':self.signx = -1.0self.signy = 1.0else:self.signx = 1.0self.signy = -1.0def contains(self, loc):return self.disp.get_bbox().contains(loc.x, loc.y)class Puck(object):def __init__(self, disp, pad, field):self.vmax = .2self.disp = dispself.field = fieldself._reset(pad)def _reset(self, pad):self.x = pad.x + pad.xoffsetif pad.y < 0:self.y = pad.y + pad.yoffsetelse:self.y = pad.y - pad.yoffsetself.vx = pad.x - self.xself.vy = pad.y + pad.w/2 - self.yself._speedlimit()self._slower()self._slower()def update(self, pads):self.x += self.vxself.y += self.vyfor pad in pads:if pad.contains(self):self.vx *= 1.2 * pad.signxself.vy *= 1.2 * pad.signyfudge = .001# probably cleaner with something like...if self.x < fudge:pads[1].score += 1self._reset(pads[0])return Trueif self.x > 7 - fudge:pads[0].score += 1self._reset(pads[1])return Trueif self.y < -1 + fudge or self.y > 1 - fudge:self.vy *= -1.0# add some randomness, just to make it interestingself.vy -= (randn()/300.0 + 1/300.0) * np.sign(self.vy)self._speedlimit()return Falsedef _slower(self):self.vx /= 5.0self.vy /= 5.0def _faster(self):self.vx *= 5.0self.vy *= 5.0def _speedlimit(self):if self.vx > self.vmax:self.vx = self.vmaxif self.vx < -self.vmax:self.vx = -self.vmaxif self.vy > self.vmax:self.vy = self.vmaxif self.vy < -self.vmax:self.vy = -self.vmaxclass Game(object):def __init__(self, ax):# create the initial lineself.ax = axax.set_ylim([-1, 1])ax.set_xlim([0, 7])padAx = 0padBx = .50padAy = padBy = .30padBx += 6.3# padspA, = self.ax.barh(padAy, .2,height=.3, color='k', alpha=.5, edgecolor='b',lw=2, label="Player B",animated=True)pB, = self.ax.barh(padBy, .2,height=.3, left=padBx, color='k', alpha=.5,edgecolor='r', lw=2, label="Player A",animated=True)# distractorsself.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)# center lineself.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.canvasself.background = Noneself.cnt = 0self.distract = Trueself.res = 100.0self.on = Falseself.inst = True # show instructions from the beginningself.background = Noneself.pads = []self.pads.append(Pad(pA, padAx, padAy))self.pads.append(Pad(pB, padBx, padBy, 'r'))self.pucks = []self.i = self.ax.annotate(instructions, (.5, 0.5),name='monospace',verticalalignment='center',horizontalalignment='center',multialignment='left',textcoords='axes fraction',animated=False)self.canvas.mpl_connect('key_press_event', self.key_press)def draw(self, evt):draw_artist = self.ax.draw_artistif self.background is None:self.background = self.canvas.copy_from_bbox(self.ax.bbox)# restore the clean slate backgroundself.canvas.restore_region(self.background)# show the distractorsif 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 and padsif 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):# we only get here if someone scoredself.pads[0].disp.set_label(" " + str(self.pads[0].score))self.pads[1].disp.set_label(" " + str(self.pads[1].score))self.ax.legend(loc='center', framealpha=.2,facecolor='0.5',prop=FontProperties(size='xx-large',weight='bold'))self.background = Noneself.ax.figure.canvas.draw_idle()return Truepuck.disp.set_offsets([[puck.x, puck.y]])self.ax.draw_artist(puck.disp)# just redraw the axes rectangleself.canvas.blit(self.ax.bbox)self.canvas.flush_events()if self.cnt == 50000:# just so we don't get carried awayprint("...and you've been playing for too long!!!")plt.close()self.cnt += 1return Truedef key_press(self, event):if event.key == '3':self.res *= 5.0if event.key == '4':self.res /= 5.0if event.key == 'e':self.pads[0].y += .1if self.pads[0].y > 1 - .3:self.pads[0].y = 1 - .3if event.key == 'd':self.pads[0].y -= .1if self.pads[0].y < -1:self.pads[0].y = -1if event.key == 'i':self.pads[1].y += .1if self.pads[1].y > 1 - .3:self.pads[1].y = 1 - .3if event.key == 'k':self.pads[1].y -= .1if self.pads[1].y < -1:self.pads[1].y = -1if 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 == ' ' and len(self.pucks):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.distractif event.key == 'g':self.on = not self.onif event.key == 't':self.inst = not self.instself.i.set_visible(not self.i.get_visible())self.background = Noneself.canvas.draw_idle()if event.key == 'q':plt.close()
