Python tkinter弹跳球 - 能量进入无限

时间:2013-06-16 19:16:30

标签: python tkinter simulation physics particles

我编写了一个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

2 个答案:

答案 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的工作方式。