Reputation: 62
I'm a medical editor for Wikipedia (with near zero python experience) and we're attempting to build a simulation of the effects of social distancing. I'm trying to make a bunch of circles bounce around in a square space. I've got them bouncing off the walls but I'm unsure how to detect a collision between the balls. I've created a definition is_collided_with but the line
if is_collided_with(ball, ball):
ball.dy *=-1
ball.dx *=-1
freezes everything up. If you remove it you can see the motion (speed may vary by system from what I understand). The eventual goal is to change colours from health, to infectious, to cured and show how social distancing works with various numbers of people following it.
Here is the whole code,
#bouncing balls
import turtle
import random
wn = turtle.Screen()
wn.bgcolor("white")
wn.title("ball simulator")
wn.tracer(0)
balls = []
for _ in range(10):
balls.append(turtle.Turtle())
for ball in balls:
ball.shape("circle")
ball.color("red")
ball.penup()
ball.speed(1)
x = random.randint(-290,290)
y = random.randint(-290,290)
ball.goto(x, y)
ball.dy = (random.randint(-3, 3))/5+.1
ball.dx = (random.randint(-3, 3))/5+.1
def is_collided_with(a, b):
return abs(a.xcor() - b.xcor()) < 10 and abs(a.ycor() - b.ycor()) < 10
while True:
wn.update()
for ball in balls:
ball.sety(ball.ycor() + ball.dy)
ball.setx(ball.xcor() + ball.dx)
#check for a bounce
if is_collided_with(ball, ball):
ball.dy *=-1
ball.dx *=-1
if ball.ycor() <-300:
ball.dy *=-1
if ball.ycor() >+300:
ball.dy *=-1
if ball.xcor() >+300:
ball.dx *=-1
if ball.xcor() <-300:
ball.dx *=-1
wn.mainloop()
Upvotes: 0
Views: 552
Reputation: 10799
The problem is this line:
if is_collided_with(ball, ball):
You are passing in the same ball
object twice, treating it as if it were to separate balls. Basically, for every ball in your list of balls, the if-statement is saying "is this ball colliding with itself?" - which will always be true for every frame of the simulation. Therefore, you always enter the body of the if-statement, and flip the current balls x- and y- direction vectors, which results in every ball just kind of oscillating in place.
The naive solution involves comparing the current ball with all other balls in the simulation. I say "naive" because, while this does work, it becomes terribly inefficient and slow for greater numbers of balls. A more sophisticated solution would use some kind of space-partitioning technique like a quadtree to significantly improve performance (this works by only comparing balls which have the possibility of being close, whereas all other balls which are far away are culled and not considered for the comparison).
In your case, as long as the number of balls is small, the naive solution should work OK. It would look something like this (notice the nested for-loop):
while True:
wn.update()
for ball in balls:
ball.sety(ball.ycor() + ball.dy)
ball.setx(ball.xcor() + ball.dx)
for other_ball in balls:
if other_ball is ball:
# We are not interested in balls colliding with themselves.
# Skip the current iteration of the inner for-loop, and move on to the next ball
continue
if is_collided_with(ball, other_ball):
ball.dx *= -1
ball.dy *= -1
if ball.ycor() <-300:
ball.dy *=-1
if ball.ycor() >+300:
ball.dy *=-1
if ball.xcor() >+300:
ball.dx *=-1
if ball.xcor() <-300:
ball.dx *=-1
One last thing, keep in mind that technically, just because two balls collide does not mean both of their direction-vector components (both x and y) need to be / should be flipped. Imagine a scenario where two balls are traveling in the same direction but at slightly different speeds, where the ball in-front is slower than the one behind it (which is catching up) - once they collide, it would be incorrect to flip both direction-vector components.
Upvotes: 1