I'm trying to do collision detection over the members of a list of turtles, so that a variable returns a Boolean to break a while true loop. It works with ONE of the turtles, but not the others. It also only prints the distance (for debug) for the one that collision works with. What's confusing is the exact same syntax used in the collision function works just fine in the ball/wall collision detection loops.
import turtle
from turtle import Screen
import random
from random import randrange
screen = turtle.Screen()
screen.setup(500, 500)
screen.title('Balls Bounce')
friend = turtle.Turtle()
friend.shape('turtle')
friend.color('green')
friend.turtlesize(3)
def friendUp():
friend.setheading(90)
friend.forward(45)
def friendDown():
friend.setheading(270)
friend.forward(45)
def friendRight():
friend.setheading(0)
friend.forward(45)
def friendLeft():
friend.setheading(180)
friend.forward(45)
screen.onkey(friendUp, "Up")
screen.onkey(friendLeft, "Left")
screen.onkey(friendRight, "Right")
screen.onkey(friendDown, "Down")
screen.listen()
def createBall():
ball = turtle.Turtle(shape="circle")
ball.penup()
ball.color("Red")
ball.turtlesize(3)
ball.setheading(randrange(361))
ball.forward(5)
return ball
balls = []
for i in range(5):
b = createBall()
balls.append(b)
bodySize = 80
halfBodySize = bodySize / 2
collisionDistance = 5
isCollision = False
def collision(balls, friend):
distance = 0
for b in balls:
distance = b.distance(friend)
print(distance // 1)
if distance < halfBodySize:
print('Collision!')
global isCollision
isCollision = True
break
return isCollision
while True:
collision(balls, friend)
if isCollision == True:
break
for b in balls:
b.forward(5)
xPos = b.position()[0]
yPos = b.position()[1]
if xPos > 250 or xPos < -250:
b.forward(-5)
b.setheading(randrange(361))
if yPos > 250 or yPos < -250:
b.forward(-5)
b.setheading(randrange(361))
screen.mainloop()
I've tried using map and enumerate, but they keep returning issues that I can't seem to figure out. For instance, using enumerate returns tuple issues, and I'm not sure I set up the syntax for it correctly. Map has similar issues.
return isCollision
needs to be indented one level further, inside the if
that detects collision:
def collision(balls, friend):
for b in balls:
distance = b.distance(friend)
print(distance // 1)
if distance < halfBodySize:
print('Collision!')
return True
return False
If it's outside the if
, it'll always return whatever the first ball-friend collision result was and never make it past the first iteration of the loop.
No need for global
--just return the value and use it directly in the caller:
if collision(balls, friend):
# there was a collision between a ball and friend
You can also remove your print()
calls, at which point consider using any
:
def collision(balls, friend):
return any(b.distance(friend) < halfBodySize for b in balls)
Here's a minimal proof of correctness:
from random import randint
from turtle import Screen, Turtle
def check_collision(ball, balls):
return any(b.distance(ball) < HALF_BODY_SIZE for b in balls)
def reposition():
for t in turtles:
t.goto(randint(-w, w), randint(-h, h))
friend.color("green")
if check_collision(friend, balls):
friend.color("red")
screen.update()
HALF_BODY_SIZE = 60
screen = Screen()
w, h = screen.screensize()
screen.tracer(0)
turtles = []
for _ in range(35):
t = Turtle(shape="circle")
turtles.append(t)
t.shapesize(3)
t.penup()
friend, balls = turtles[-1], turtles[:-1]
reposition()
screen.onclick(lambda *_: reposition())
screen.mainloop()
As an aside, use snake_case
for functions and variables, not camelCase
, per PEP-8 convention.
Also prefer from turtle import Screen, Turtle
to avoid confusing the functional and OOP interfaces. Your current code never uses from turtle import Screen
, which can be caught with a linter like flake 8.