Прыгающие 2D шары - Python - PullRequest
       23

Прыгающие 2D шары - Python

0 голосов
/ 01 апреля 2020

Я пытаюсь создать программу, в которой есть 6 разделов и куча шаров отскакивают друг от друга. Шары не делают то, что они должны делать, а вместо этого застревают друг на друге. Иногда они подпрыгивают, иногда нет.

Я пробовал несколько формул для отскока 2D, но однажды кажется, что он действительно сохраняет импульс. Это измененная модель из того, что я имел ранее, которая была в основном прямой копией со страницы википедии для 2D-столкновений. Это не сработало, и текущая модель, которую я взял из другого ответа stackoverflow, дает такие же результаты. Я думаю, что моя физика может быть правильной, но что-то еще в коде все испортило. Я просматривал это снова и снова, но я все еще не могу найти то, что это могло бы быть. Любая помощь будет оценена.

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()

1 Ответ

1 голос
/ 01 апреля 2020

Рассмотрим следующую ситуацию: 2 шара приближаются друг к другу, вы обнаруживаете столкновение, пересчитываете их скорости и go к следующему кадру, но в следующем кадре эти два шара все еще очень близки друг к другу. Затем вы обнаружите столкновение между ними, снова пересчитаете их скорости (вернув его обратно к исходным значениям), go к следующему кадру. Шары снова находятся близко друг к другу, снова сталкиваются и т. Д. c.

Другими словами, вам нужно отключить обнаружение столкновения для двух шаров после того, как они просто столкнулись, чтобы позволить им уйти друг от друга. Есть разные способы сделать это. Например, вы можете сделать обнаружение столкновений более сложным. Вы можете реально рассмотреть направления, в которых движутся два шарика, найти точку пересечения их прямолинейных траекторий. Если эта точка пересечения лежит в их прошлом - тогда не считайте их сталкивающимися. Если точка пересечения находится в их будущем - тогда выполните тест близости, как вы делаете сейчас.

...