原 matplotlib动画入门(3):弹球
- 2019 年 10 月 6 日
- 筆記
介绍
在平面上画一个方框代表墙壁,框内有一个运动的弹球,当弹球碰到墙壁时就弹回去,小球不停的运动。

代码
新建一个文件particle.py,增加如下代码:
先引入相应的包
import numpy as np import matplotlib.pyplot as plt import matplotlib.animation as animation
新建一个ParticleBox类
class ParticleBox: ''' 初始化方法 init_state是一个数组:[x,y,vx,vy],四个值分别表示x坐标,y坐标,x方向的速度,y方向的速度 bounds 是箱子的边界: [xmin, xmax, ymin, ymax] size: 球的半径 ''' def __init__(self, init_state = [1,0,0.5,1], bounds = [-2, 2, -2, 2], size = 0.04): self.init_state = np.asarray(init_state, dtype=float) self.size = size self.state = self.init_state.copy() self.bounds = bounds ''' 每一帧动画调用一次step函数,画出小球的位置。 dt是每一帧代表的时间,其乘以速度就是运动的距离 ''' def step(self, dt): ''' x = x + dt * vx y = y + dt * vy ''' self.state[:2] += dt * self.state[2:] ''' 如果弹球碰到墙,就弹回来 ''' crossed_x1 = (self.state[0] < self.bounds[0] + self.size) crossed_x2 = (self.state[0] > self.bounds[1] - self.size) crossed_y1 = (self.state[1] < self.bounds[2] + self.size) crossed_y2 = (self.state[1] > self.bounds[3] - self.size) if crossed_x1: self.state[0] = self.bounds[0] + self.size if crossed_x2: self.state[0] = self.bounds[1] - self.size if crossed_y1: self.state[1] = self.bounds[2] + self.size if crossed_y2: self.state[1] > self.bounds[3] - self.size #反方向运动 if crossed_x1 | crossed_x2 : self.state[2] *= -1 if crossed_y1 | crossed_y2 : self.state[3] *= -1
每一帧动画调用一次animate函数,i表示帧号。
def animate(i): global box, rect box.step(dt) rect.set_edgecolor('k') particles.set_data(box.state[0], box.state[1]) particles.set_markersize(4) return particles, rect
生成边框、小球以及让它动起来
#随机取四个数作为起始位置和速度 init_state = np.random.random(4) box = ParticleBox(init_state, size=0.04) #每帧代表0.1秒 dt = 0.1 #画图 fig = plt.figure() fig.subplots_adjust(left=0, right=1, bottom=0, top=1) ax = fig.add_subplot(111, aspect='equal', autoscale_on=False, xlim=(-3.2, 3.2), ylim=(-2.4, 2.4)) particles, = ax.plot([], [], 'bo', ms=4) rect = plt.Rectangle(box.bounds[::2], box.bounds[1] - box.bounds[0], box.bounds[3] - box.bounds[2], ec='none', lw=2, fc='none') ax.add_patch(rect) ani = animation.FuncAnimation(fig, animate, frames=600, interval=1, blit=False) plt.show()
完整代码为:
import numpy as np import matplotlib.pyplot as plt import matplotlib.animation as animation class ParticleBox: ''' init_state is an array:[x,y,vx,vy] bounds 箱子的边界: [xmin, xmax, ymin, ymax] size: 球的半径 ''' def __init__(self, init_state = [1,0,0.5,1], bounds = [-2, 2, -2, 2], size = 0.04): self.init_state = np.asarray(init_state, dtype=float) self.size = size self.state = self.init_state.copy() self.bounds = bounds ''' 每一帧动画调用一次step函数,画出小球的位置。 dt是每一帧代表的时间,其乘以速度就是运动的距离 ''' def step(self, dt): ''' x = x + dt * vx y = y + dt * vy ''' self.state[:2] += dt * self.state[2:] ''' 如果碰到墙,就弹回来 ''' crossed_x1 = (self.state[0] < self.bounds[0] + self.size) crossed_x2 = (self.state[0] > self.bounds[1] - self.size) crossed_y1 = (self.state[1] < self.bounds[2] + self.size) crossed_y2 = (self.state[1] > self.bounds[3] - self.size) if crossed_x1: self.state[0] = self.bounds[0] + self.size if crossed_x2: self.state[0] = self.bounds[1] - self.size if crossed_y1: self.state[1] = self.bounds[2] + self.size if crossed_y2: self.state[1] > self.bounds[3] - self.size if crossed_x1 | crossed_x2 : self.state[2] *= -1 if crossed_y1 | crossed_y2 : self.state[3] *= -1 def animate(i): global box, rect box.step(dt) rect.set_edgecolor('k') particles.set_data(box.state[0], box.state[1]) particles.set_markersize(4) return particles, rect #随机取四个数作为起始位置和速度 init_state = np.random.random(4) box = ParticleBox(init_state, size=0.04) #每帧代表0.1秒 dt = 0.1 #画图 fig = plt.figure() fig.subplots_adjust(left=0, right=1, bottom=0, top=1) ax = fig.add_subplot(111, aspect='equal', autoscale_on=False, xlim=(-3.2, 3.2), ylim=(-2.4, 2.4)) particles, = ax.plot([], [], 'bo', ms=4) rect = plt.Rectangle(box.bounds[::2], box.bounds[1] - box.bounds[0], box.bounds[3] - box.bounds[2], ec='none', lw=2, fc='none') ax.add_patch(rect) ani = animation.FuncAnimation(fig, animate, frames=600, interval=1, blit=False) plt.show()
执行命令
python3 particle.py