.. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: .. "gallery/event_handling/pipong.py" .. LINE NUMBERS ARE GIVEN BELOW. .. only:: html .. note:: :class: sphx-glr-download-link-note Click :ref:`here ` to download the full example code .. rst-class:: sphx-glr-example-title .. _sphx_glr_gallery_event_handling_pipong.py: ====== Pipong ====== A Matplotlib based game of Pong illustrating one way to write interactive animation which are easily ported to multiple backends pipong.py was written by Paul Ivanov .. GENERATED FROM PYTHON SOURCE LINES 10-291 .. code-block:: default import numpy as np import matplotlib.pyplot as plt from numpy.random import randn, randint from matplotlib.font_manager import FontProperties instructions = """ Player A: Player B: 'e' up 'i' 'd' down 'k' press 't' -- close these instructions (animation will be much faster) press 'a' -- add a puck press 'A' -- remove a puck press '1' -- slow down all pucks press '2' -- speed up all pucks press '3' -- slow down distractors press '4' -- speed up distractors press ' ' -- reset the first puck press 'n' -- toggle distractors on/off press 'g' -- toggle the game on/off """ 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 # probably cleaner with something like... 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 # add some randomness, just to make it interesting 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 class Game: def __init__(self, ax): # create the initial line self.ax = ax ax.set_ylim([-1, 1]) ax.set_xlim([0, 7]) pad_a_x = 0 pad_b_x = .50 pad_a_y = pad_b_y = .30 pad_b_x += 6.3 # pads 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) # distractors 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) # center line 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 # show instructions from the beginning self.background = None 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', textcoords='axes fraction', animated=False) self.canvas.mpl_connect('key_press_event', self.on_key_press) def draw(self, event): draw_artist = self.ax.draw_artist if self.background is None: self.background = self.canvas.copy_from_bbox(self.ax.bbox) # restore the clean slate background self.canvas.restore_region(self.background) # show the distractors 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 and pads 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): # we only get here if someone scored self.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 = None self.ax.figure.canvas.draw_idle() return True puck.disp.set_offsets([[puck.x, puck.y]]) self.ax.draw_artist(puck.disp) # just redraw the axes rectangle self.canvas.blit(self.ax.bbox) self.canvas.flush_events() if self.cnt == 50000: # just so we don't get carried away print("...and you've been playing for too long!!!") plt.close() self.cnt += 1 return True 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 == ' ' 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.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() .. _sphx_glr_download_gallery_event_handling_pipong.py: .. only :: html .. container:: sphx-glr-footer :class: sphx-glr-footer-example .. container:: sphx-glr-download sphx-glr-download-python :download:`Download Python source code: pipong.py ` .. container:: sphx-glr-download sphx-glr-download-jupyter :download:`Download Jupyter notebook: pipong.ipynb ` .. only:: html .. rst-class:: sphx-glr-signature Keywords: matplotlib code example, codex, python plot, pyplot `Gallery generated by Sphinx-Gallery `_