2

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

2 回答 2

1

我相信您遇到的问题是您忘记在动作循环中添加时间步:

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()
于 2013-06-17T15:23:49.760 回答
1

这并不能直接回答您的问题,但您使用 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)

这对您的方案的优势在于,您的方案有 10 毫秒的小窗口,其中 GUI 完全冻结,这可能会导致一些非常轻微的卡顿。更大的优势仅仅是这就是 tkinter 的设计方式。

于 2013-06-17T16:26:26.077 回答