So I'm coding a tron game in turtle and the last part I need to do is to code the collisions between the turtles themselves, out of bound collisions, and collisions between the line made by the a turtle and the other turtle. I coded the out of bound collisions and they didn't work, and I also tried coding the player against player collisions which didn't work either. I've got no clue how to do the other one (where it outputs something when the turtle collides with the line generated by the other turtle). I thought of using an array in some way but I'm not sure how those work. Can anyone help me out?
Code for the out of bound collisions:
#Width and height of the screen
width = turtle.window_width()
height = turtle.window_height()
def collisions():
while True:
if(math.isclose(blue_player.xcor(), red_player.xcor(), abs_tol=1e-10) and
math.isclose(blue_player.ycor(), red_player.ycor(), abs_tol=1e-10)):
TurtleCrash = turtle.Turtle(visible=False)
TurtleCrash.color("white")
style = ('Arial', 25, 'italic')
TurtleCrash.write("Red and Blue Crashed!\nGame over!", font=style, align='center')
break
if blue_player.xcor() > width or blue_player.xcor() < -1*width:
BlueTurtleOut = turtle.Turtle(visible=False)
BlueTurtleOut.color("white")
style = ('Arial', 25, 'italic')
BlueTurtleOut.write("Red went out of bounds./nBlue wins!", font=style, align='center')
break
if blue_player.ycor() > height or blue_player.ycor() < -1*height:
BlueTurtleOut2 = turtle.Turtle(visible=False)
BlueTurtleOut2.color("white")
style = ('Arial', 25, 'italic')
BlueTurtleOut2.write("Red went out of bounds./nBlue wins!", font=style, align='center')
break
if red_player.xcor() > width or red_player.xcor() < -1*width:
RedTurtleOut = turtle.Turtle(visible=False)
RedTurtleOut.color("white")
style = ('Arial', 25, 'italic')
RedTurtleOut.write("Red went out of bounds./nBlue wins!", font=style, align='center')
break
if red_player.ycor() > height or red_player.ycor() < -1*height:
RedTurtleOut2 = turtle.Turtle(visible=False)
RedTurtleOut2.color("white")
style = ('Arial', 25, 'italic')
RedTurtleOut2.write("Red went out of bounds./nBlue wins!", font=style, align='center')
break
gameScreen()
gameScreen is a function called earlier in my code by the way, it's not the issue, although it could be in the wrong place in the function. I'm not sure.
Code for (attempted) collision between the turtles where nobody wins.
collision = turtle.Turtle()
collision.hideturtle()
if blue_player.pos() == red_player.pos():
collision.goto(0,0)
collision.showturtle()
collision.write("You collided with each other!\nIt's a tie!")
Here's the full code:
#Width and height of the screen
width = turtle.window_width()
height = turtle.window_height()
#Variables for tron game coded later on
red_direction = None
blue_direction = None
def TronGame():
#Border
#box = Turtle()
#box.ht()
#box.color('purple')
#box.speed('fastest')
#box.pensize(10)
# box.pu()
# box.goto(-1*height, -1*width)
# box.pd()
# for i in range(4):
# box.forward(height)
# box.left(90)
# box.forward(width)
# box.left(90)
#Blue Player movements
def blue_up():
global blue_direction
blue_direction = 'up'
def blue_down():
global blue_direction
blue_direction = 'down'
def blue_left():
global blue_direction
blue_direction = 'left'
def blue_right():
global blue_direction
blue_direction = 'right'
#Red player Movemnts
def red_up():
global red_direction
red_direction = 'up'
def red_down():
global red_direction
red_direction = 'down'
def red_left():
global red_direction
red_direction = 'left'
def red_right():
global red_direction
red_direction = 'right'
#Player movements
def move_player(player, direction):
if direction == 'up':
player.setheading(90)
player.forward(5)
elif direction == 'down':
player.setheading(270)
player.forward(5)
elif direction == 'left':
player.setheading(180)
player.forward(5)
elif direction == 'right':
player.setheading(0)
player.forward(5)
def gameloop():
move_player(red_player, red_direction)
move_player(blue_player, blue_direction)
#Repeat after 10ms (0.01s) (1000ms/10ms = 100 FPS)
screen.ontimer(gameloop, 10)
def collisions():
while True:
if(math.isclose(blue_player.xcor(), red_player.xcor(), abs_tol=1e-10) and
math.isclose(blue_player.ycor(), red_player.ycor(), abs_tol=1e-10)):
TurtleCrash = turtle.Turtle(visible=False)
TurtleCrash.color("white")
style = ('Arial', 25, 'italic')
TurtleCrash.write("Red and Blue Crashed!\nGame over!", font=style, align='center')
break
if blue_player.xcor() > width or blue_player.xcor() < -1*width:
BlueTurtleOut = turtle.Turtle(visible=False)
BlueTurtleOut.color("white")
style = ('Arial', 25, 'italic')
BlueTurtleOut.write("Red went out of bounds./nBlue wins!", font=style, align='center')
break
if blue_player.ycor() > height or blue_player.ycor() < -1*height:
BlueTurtleOut2 = turtle.Turtle(visible=False)
BlueTurtleOut2.color("white")
style = ('Arial', 25, 'italic')
BlueTurtleOut2.write("Red went out of bounds./nBlue wins!", font=style, align='center')
break
if red_player.xcor() > width or red_player.xcor() < -1*width:
RedTurtleOut = turtle.Turtle(visible=False)
RedTurtleOut.color("white")
style = ('Arial', 25, 'italic')
RedTurtleOut.write("Red went out of bounds./nBlue wins!", font=style, align='center')
break
if red_player.ycor() > height or red_player.ycor() < -1*height:
RedTurtleOut2 = turtle.Turtle(visible=False)
RedTurtleOut2.color("white")
style = ('Arial', 25, 'italic')
RedTurtleOut2.write("Red went out of bounds./nBlue wins!", font=style, align='center')
break
def MainTron():
global screen
global blue_player
global red_player
screen = turtle.Screen()
screen.setup(width, height)
screen.bgpic('TronBg.png')
screen.bgcolor('black')
screen.addshape('BlueBike.gif')
screen.addshape('RedBike.gif')
blue_player = turtle.Turtle()
blue_player.shape('BlueBike.gif')
blue_player.pencolor("blue")
blue_player.pensize(3)
blue_player.pu()
blue_player.goto(-1*(width)/3, height/8)
blue_player.pd()
red_player = turtle.Turtle()
red_player.shape('RedBike.gif')
red_player.pencolor("red")
red_player.pensize(3)
red_player.pu()
red_player.goto(width/3, height/8)
red_player.pd()
for x in range(10):
my_turtle = turtle.Turtle(visible=False)
my_turtle.color("white")
style = ('Arial', 25, 'italic')
my_turtle.write(10-x, font=style, align='center')
time.sleep(1)
my_turtle.undo()
screen.listen()
#collisions()
screen.onkey(red_up, "w")
screen.onkey(red_down, "s")
screen.onkey(red_left, "a")
screen.onkey(red_right, "d")
screen.onkey(blue_up, "Up")
screen.onkey(blue_down, "Down")
screen.onkey(blue_left, "Left")
screen.onkey(blue_right, "Right")
screen.ontimer(gameloop, 250)
#collision = turtle.Turtle()
#collision.hideturtle()
#if blue_player.pos() == red_player.pos():
# collision.goto(0,0)
# collision.showturtle()
# collision.write("You collided with each other!\nIt's a tie!")
screen.mainloop()
MainTron()
As Johnny Mopp already mentioned, there are lot of places to improve the code. Below a for sure incomplete list describing only part of the many changes done to the original code along with a short explanation what do the changes improve along with description of Python features making these changes possible:
bool("") is False
). This allows to use the string as a flag indicating found collisionmin < x < max
instead of min < x and x < max
I decided to use it in detecting collisions, but not as a check if the player is outside of the screen area (because the expression max < x < min doesn't work) but if he is within. If the player is no more within, there is a collision..distance()
function for detecting collision between the player turtles instead of evaluation of their coordinates.What could be further done to this code in order to improve it even more? The most urgent improvement would be to speed up the code in the collision detection part by replacing cloning of the turtles by another way of storing the turtle path and another way of detecting collision with the path.
Below the background image the after the image following code will try to load from the same directory from which the code is running:
import time, turtle
S_WIDTH, S_HEIGHT = 1280, 850 # size of created Screen (window)
PLAYER_MIN_DISTANCE = 15.0 # for collision between players
X_MIN, X_MAX = -S_WIDTH/2, S_WIDTH/2 # for collision of players with ...
Y_MIN, Y_MAX = -S_HEIGHT/2, S_HEIGHT/2 # ... sides of the screen
# For displaying of messages to the user about game status (win/loss):
message_turtle = turtle.Turtle(visible=False)
message_turtle.color("white")
red_clones = []
blue_clones = []
CLONE_DIST = PLAYER_MIN_DISTANCE/1.1
def check_collisions():
global red_clones, blue_clones, game_running
msg = "" # msg as not empty string will indicate collision
if not any( blue_player.distance(blue_clone) < CLONE_DIST for blue_clone in blue_clones):
bpc = blue_player.clone(); bpc.hideturtle(); blue_clones.append( bpc )
if not any( red_player.distance( red_clone ) < CLONE_DIST for red_clone in red_clones):
rpc = red_player.clone(); rpc.hideturtle(); red_clones.append( rpc )
for red_clone in red_clones:
if blue_player.distance(red_clone) < PLAYER_MIN_DISTANCE:
msg = "Blue crashed in Red's path.\nRed wins!"
for blue_clone in blue_clones:
if red_player.distance(blue_clone) < PLAYER_MIN_DISTANCE:
msg = "Red crashed in Blue's path.\nBlue wins!"
if( blue_player.distance(red_player) < PLAYER_MIN_DISTANCE ):
msg = "Red and Blue Crashed!\nGame over!"
if not ( X_MIN < blue_player.xcor() < X_MAX and Y_MIN < blue_player.ycor() < Y_MAX ):
msg = "Blue went out of bounds.\nRed wins!"
if not ( X_MIN < red_player.xcor() < X_MAX and Y_MIN < red_player.ycor() < Y_MAX ):
msg = "Red went out of bounds.\nBlue wins!"
if msg:
message_turtle.write( msg, font=('Arial', 25, 'italic'), align='center')
pause_game()
game_running = False
del red_clones[:] ; del blue_clones[:]
time.sleep(5)
message_turtle.undo()
blue_player.clear(); red_player.clear()
blue_player.is_moving=False; red_player.is_moving=False
blue_player.pu();blue_player.goto(-1*S_WIDTH/3, S_HEIGHT/8);blue_player.pd()
red_player.pu() ; red_player.goto( S_WIDTH/3, S_HEIGHT/8); red_player.pd()
blue_player.setheading( 0); red_player.setheading(180)
message_turtle.write( "Press SPACE for a NEW GAME", font=('Arial', 25, 'italic'), align='center')
countdown()
if not game_paused:
message_turtle.undo()
game_running = True
else:
screen.bye()
# Setting up the display area and making shapes available for later use
screen = turtle.Screen()
screen.setup(S_WIDTH, S_HEIGHT)
#
screen.bgpic( 'TronBg.png' )
screen.bgcolor( 'green' )
# screen.addshape( 'BlueBike.gif')
# screen.addshape( 'RedBike.gif' )
# Create the Blue Player Turtle and its controls
blue_player = turtle.Turtle("turtle")
blue_player.shapesize(stretch_wid=3, stretch_len=3)
blue_player.setheading( 0)
# blue_player.shape('BlueBike.gif')
blue_player.pencolor("blue")
blue_player.pensize(15)
blue_player.pu()
blue_player.goto(-1*S_WIDTH/3, S_HEIGHT/8)
blue_player.pd()
blue_player.is_moving = False
def blue_up() : blue_player.setheading( 90);blue_player.is_moving=True
def blue_down() : blue_player.setheading(270);blue_player.is_moving=True
def blue_left() : blue_player.setheading(180);blue_player.is_moving=True
def blue_right(): blue_player.setheading( 0);blue_player.is_moving=True
screen.onkey(blue_up, "Up")
screen.onkey(blue_down, "Down")
screen.onkey(blue_left, "Left")
screen.onkey(blue_right, "Right")
# Create the Red Player Turtle and its controls
red_player = turtle.Turtle("turtle")
red_player.shapesize(stretch_wid=3, stretch_len=3)
red_player.setheading(180)
# red_player.shape('RedBike.gif')
red_player.pencolor("red")
red_player.pensize(15)
red_player.pu()
red_player.goto(S_WIDTH/3, S_HEIGHT/8)
red_player.pd()
red_player.is_moving = False
def red_up() : red_player.setheading( 90); red_player.is_moving=True
def red_down() : red_player.setheading(270); red_player.is_moving=True
def red_left() : red_player.setheading(180); red_player.is_moving=True
def red_right() : red_player.setheading( 0); red_player.is_moving=True
screen.onkey(red_up, "w")
screen.onkey(red_down, "s")
screen.onkey(red_left, "a")
screen.onkey(red_right, "d")
# Allow pausing the game by pressing space
game_paused = False
def pause_game():
global game_paused
if game_paused: game_paused = False
else: game_paused = True
screen.onkey(pause_game, "space")
# Countdown:
countdown_turtle = turtle.Turtle(visible=False)
countdown_turtle.color("white")
countdown_turtle.goto(0, -50)
def countdown():
for i in range(3,-1,-1):
countdown_turtle.write(i, font=('Arial', 25, 'italic'), align='center')
time.sleep(1)
countdown_turtle.undo()
countdown()
# Establishing a screen.ontimer() loop driving the turtles movement
game_running = True
def gameloop():
if not game_paused and game_running:
if blue_player.is_moving: blue_player.forward(3)
if red_player.is_moving: red_player.forward(3)
check_collisions()
screen.ontimer(gameloop, 30) # 10ms (0.01s) (1000ms/10ms = 100 FPS)
gameloop()
# Start processing of key-presses
screen.listen()
screen.mainloop()