我编写了一个Python脚本,它创建了一个Ball类,然后创建了它的实例。球应该从墙壁上反弹,然后相互脱离(尚未实施)。请注意,当球到达墙壁时,不是仅仅反转球的方向,而是在球上施加一个力(或加速度)。
不幸的是,在从墙上弹回后,球会进入振荡运动,振幅变得越来越大。
我是Python的新手,但之前已经编写了各种javascript / canvas模拟。
这个特殊的python脚本使用Verlet集成来计算位置和速度。我基于这个脚本:
http://physics.weber.edu/schroeder/software/demos/MolecularDynamics.html
任何人都可以建议我应该如何修改代码,以便在球从墙上反弹时“稳定”?谢谢
from Tkinter import *
import random
window = Tk()
canvas = Canvas(window, width = 400, height = 300)
canvas.pack()
wallStiffness=0.05
dt=0.02
class Ball:
def __init__(self):
self.x=0
self.y=0
self.xvel=random.random()*2-1
self.yvel=random.random()*2-1
self.ax=0
self.ay=0
self.rad=20
def computeAccelerations(z):
if z.x<z.rad:
z.ax=wallStiffness*(z.rad-z.x)
else:
if z.x>400-z.rad:
z.ax=wallStiffness*(400-z.rad-z.x)
#only have horizontal bounces so far, and no bounces between balls yet
list=[]
for i in range(100): #0 to 99, make 100 balls
myball=Ball()
list.append(myball)
#script to place in a crystal to begin with
current_x=30
current_y=0
for j in list:
j.x=current_x
current_x+=30
j.y=current_y
if current_x+30>370:
current_x=30
current_y+=30
j.ball=canvas.create_oval(j.x,j.y,j.x+j.rad,j.y+j.rad,fill="blue")
while True:
for i in range(10): #10 steps per frame
for j in list:
j.x+=j.xvel*dt+0.5*j.ax*dt*dt
j.y+=j.yvel*dt+0.5*j.ay*dt*dt
j.xvel+=0.5*j.ax*dt
j.yvel+=0.5*j.ay*dt
computeAccelerations(j)
for j in list:
j.xvel+=0.5*j.ax*dt
j.yvel+=0.5*j.ay*dt
canvas.after(10) #duration of frame in ms
for z in list:
canvas.move(z.ball,z.xvel, z.yvel)
canvas.update()
window.mainloop() #keeps the window open
答案 0 :(得分:0)
我相信您遇到的问题是您忘记在操作循环中加入时间步骤:
while True:
for i in range(10):
for j in list:
j.x += j.xvel * dt + 0.5 * j.ax * dt * dt
j.y += j.yvel * dt + 0.5 * j.ay * dt * dt
j.xvel += 0.5 * j.ax * dt
j.yvel += 0.5 * j.ay * dt
compute_accelerations(j)
for j in list:
j.xvel += 0.5 * j.ax * dt
j.yvel += 0.5 * j.ay * dt
此外,尽管您的代码有效,但我认为您的Ball
加速方法可以清理:
# box_width = 400
def compute_accelerations(self):
if self.x < self.rad:
self.ax = wallStiffness * (self.rad - self.x)
elif self.x > box_width - self.rad:
self.ax = wallStiffness * (box_width - self.rad - self.x)
在这里,您会看到使用self
代替z
。这是在Python中创建类内部方法的标准方法。这里self
代表Ball
对象的当前实例。这有意义吗?
您可以像这样调用这个新修改的方法:
j.compute_accelerations()
答案 1 :(得分:0)
这并没有直接回答你的问题,但你使用Tkinter是错的。作为一般经验法则,你不应该调用update
,你永远不应该有一个无限循环,你永远不应该用一个参数调用after
。 Tkinter已经有一个无限循环运行 - mainloop
- 所以为什么不利用它呢?通过这样做,您可以消除我列出的所有三种不良做法。
您应该删除while True
循环的主体并将其放入函数中。然后,你有这个功能安排自己再次以某个间隔被调用。例如:
def draw_frame():
for i in range(10):
for j in list:
...
for z in list:
canvas.move(z.ball,z.xvel, z.yvel)
canvas.after(10, draw_frame)
这将绘制一个帧,等待10毫秒,然后绘制另一帧,永远重复。如果你想要能够停止动画,你可以创建一个设置标志变量的按钮(例如:we_should_continue=False
),在这种情况下你可以这样做:
if we_should_continue:
canvas.after(10, draw_frame)
这对您的方案的优势在于,您的方案具有小的10ms窗口,其中GUI完全冻结,这可能导致一些非常轻微的口吃。更大的优势在于,这就是tkinter的工作方式。