弹跳二维球 - Python

Bouncing 2D balls - Python

我正在尝试制作一个程序,其中有 6 个部分和一堆相互弹跳的球。球没有做它们应该做的事情,而是相互卡住。有时它们会反弹,有时则不会。

我已经尝试了多种 2D 弹跳公式,但这一次似乎实际上保持了势头。这是我之前的模型的修改版,它基本上是 2D 碰撞维基百科页面的直接副本。那没有用,我从另一个 Whosebug 答案中获取的当前模型产生了相同的结果。我在想我的物理学可能是正确的,但代码中的其他东西把它搞砸了。我看了一遍又一遍,但我似乎仍然无法找到那可能是什么。任何帮助将不胜感激。

from tkinter import *
import random

tk = Tk()

#canvas object
canv_width = 300
canv_height = 300


canvas1 = Canvas(tk, width = canv_width, height = canv_height, bg = 'black',highlightthickness=1, highlightbackground='white')
canvas2 = Canvas(tk, width = canv_width, height = canv_height, bg = 'black',highlightthickness=1, highlightbackground='white')
canvas3 = Canvas(tk, width = canv_width, height = canv_height, bg = 'black',highlightthickness=1, highlightbackground='white')
canvas4 = Canvas(tk, width = canv_width, height = canv_height, bg = 'black',highlightthickness=1, highlightbackground='white')
canvas5 = Canvas(tk, width = canv_width, height = canv_height, bg = 'black',highlightthickness=1, highlightbackground='white')
canvas6 = Canvas(tk, width = canv_width, height = canv_height, bg = 'black',highlightthickness=1, highlightbackground='white')

canvas1.grid(row=0,column=0)
canvas2.grid(row=0,column=1)
canvas3.grid(row=1,column=0)
canvas4.grid(row=1,column=1)
canvas5.grid(row=2,column=0)
canvas6.grid(row=2,column=1)
tk.title("BounceBox")
towns = {'1': canvas1, '2' : canvas2, '3': canvas3, '4' : canvas4, '5' : canvas5, '6': canvas6}
pops = {'1' : [], '2': [], '3': [], '4': [], '5': [], '6':[]}

def subtract(v1,v2):
    x_comp = v1[0] - v2[0]
    y_comp = v1[1] - v2[1]
    return ((x_comp,y_comp))

def add(v1,v2):
    x_comp = v1[0] + v2[0]
    y_comp = v1[1] + v2[1]
    return((x_comp,y_comp))


def dotproduct(v1,v2):
    x_comp = v1[0] * v2[0]
    y_comp = v1[1] * v2[1]
    return(x_comp + y_comp)

def magnitude(v):
    mag = (v[0]**2 + v[1]**2)**0.5
    return mag

def mult_sv(s,v):
    x_comp = s*v[0]
    y_comp = s*v[1]
    return((x_comp,y_comp))

def arctan(y,x):
    try:
        ans = math.atan(y/x)
        if x >= 0 and y >= 0:
            return ans
        elif x <= 0 and y >= 0:
            return ans + math.pi
        elif x >= 0 and y <= 0:
            return ans
        else:
            return ans - math.pi
    except:
        if y>0:
            return math.pi
        else:
            return -math.pi

class Ball:

    def __init__(self,radius,color,location,location_name):
        self.location = location
        self.location_name = location_name
        self.radius = radius
        ball_x = random.randint(0,canv_width - radius)
        ball_y = random.randint(0,canv_height - radius)
        self.pos = (ball_x,ball_y,ball_x + 2*self.radius, ball_y + 2*self.radius)
        self.center = (self.pos[0] + self.radius, self.pos[1] + self.radius)
        self.xspeed = random.randint(-1,1)
        self.yspeed = random.randint(-1,1)


        self.color = color
        self.shape = self.location.create_oval(self.pos[0],self.pos[1],self.pos[2], self.pos[3], fill = self.color)
        self.ball_update()

    def check_collision(self):
        for person in pops[self.location_name]:
            center = person.center
            distance = ((self.center[0] - person.center[0])**2 + (self.center[1] - person.center[1])**2)**0.5
            if (distance <= 2*self.radius and person != self):
                return (True,person)
        return (False,None)


    def ball_update(self):
        #print(arctan(-1,1))
        self.pos = self.location.coords(self.shape)
        self.center = (self.pos[0] + self.radius, self.pos[1] + self.radius)
        if (self.pos[0] <= 0 or self.pos[2] >= canv_width):
            self.xspeed = -self.xspeed
        if (self.pos[1] <= 0 or self.pos[3] >= canv_height):
            self.yspeed = -self.yspeed
        collision = self.check_collision()
        if collision[0] == True:
            v1 = (self.xspeed,self.yspeed)
            v2 = (collision[1].xspeed, collision[1].yspeed)

            dist = subtract(self.center,collision[1].center)
            v12 = subtract(v1,v2)
            dv = mult_sv(dotproduct(v12,dist)/ magnitude(dist)**2,dist)
            self.xspeed -= dv[0]
            self.yspeed -= dv[1]
            collision[1].xspeed += dv[0]
            collision[1].yspeed += dv[1]

        self.location.move(self.shape, self.xspeed, self.yspeed)

        self.center = (self.pos[0] + self.radius, self.pos[1] + self.radius)
        tk.after(15,self.ball_update)

def create_populations():
    for town in towns:
        for pop in range(5):
            person = Ball(10,'white',towns[town],town)
            pops[town].append(person)

create_populations()
tk.mainloop()

考虑以下情况:2个球靠得很近,你检测到碰撞,重新计算它们的速度,然后转到下一帧,但在下一帧这两个球仍然非常接近。然后你会再次检测到它们之间的碰撞重新计算它们的速度(将其恢复为原始值),转到下一帧。球再次相互靠近,再次碰撞,等等

换句话说,你需要在两个球刚刚碰撞后关闭碰撞检测,让它们远离彼此。有不同的方法可以做到这一点。例如,您可以使碰撞检测更加复杂。你其实可以考虑两个球运动的方向,找到它们直线轨迹的交点。如果这个交点就在他们的过去——那就别算他们碰撞了。如果交点在他们的未来——那么像你现在做的那样进行接近测试。