弹跳二维球 - 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个球靠得很近,你检测到碰撞,重新计算它们的速度,然后转到下一帧,但在下一帧这两个球仍然非常接近。然后你会再次检测到它们之间的碰撞重新计算它们的速度(将其恢复为原始值),转到下一帧。球再次相互靠近,再次碰撞,等等
换句话说,你需要在两个球刚刚碰撞后关闭碰撞检测,让它们远离彼此。有不同的方法可以做到这一点。例如,您可以使碰撞检测更加复杂。你其实可以考虑两个球运动的方向,找到它们直线轨迹的交点。如果这个交点就在他们的过去——那就别算他们碰撞了。如果交点在他们的未来——那么像你现在做的那样进行接近测试。
我正在尝试制作一个程序,其中有 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个球靠得很近,你检测到碰撞,重新计算它们的速度,然后转到下一帧,但在下一帧这两个球仍然非常接近。然后你会再次检测到它们之间的碰撞重新计算它们的速度(将其恢复为原始值),转到下一帧。球再次相互靠近,再次碰撞,等等
换句话说,你需要在两个球刚刚碰撞后关闭碰撞检测,让它们远离彼此。有不同的方法可以做到这一点。例如,您可以使碰撞检测更加复杂。你其实可以考虑两个球运动的方向,找到它们直线轨迹的交点。如果这个交点就在他们的过去——那就别算他们碰撞了。如果交点在他们的未来——那么像你现在做的那样进行接近测试。