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()
CodePudding user response:
As Johnny Mopp already mentioned, there are lot of places to improve the code, so I have modified the code in the hope it will make things much easier to build upon. The result of this modifications is a huge reduction in the amount of code lines and a 'flat' code without nesting of functions.
Below a not really complete list of explanations of changes I have done to the code:
1. If a value returned by a function is used multiple times it's better to assign its value to a variable first 2. An empty string s="" is considered False in Python. This allows to use: `if s: print("s is not an empty string")` 3. On key-press the Turtle can directly get its direction assigned. No need for a forth and back between "up" and 90. 4. As in simple cases nesting of functions or using classes makes the code worse and harder to understand instead of better I have 'flattened' the code. 5. Have as good as possible eliminate exactly the same code repeating again and again 6. Have put things which belong together near to each other in code 7. Added the possibility to pause/restart the game by pressing the "space" button. 8. Set up the threshold for detecting collisions, so crashes are now detected 9. Set the width and height values to a half, so that collisions at screen edges get now detected
import math, time, turtle
width = turtle.window_width()
height = turtle.window_height()
def check_collisions():
x_blue, y_blue = blue_player.xcor(), blue_player.ycor()
x_red, y_red = red_player.xcor(), red_player.ycor()
message_turtle = turtle.Turtle(visible=False)
message_turtle.color("white")
collision_text = ""
if( math.isclose(x_blue, x_red, abs_tol=10.0) and
math.isclose(y_blue, y_red, abs_tol=10.0)):
collision_text = "Red and Blue Crashed!\nGame over!"
if x_blue > width/2 or x_blue < -1*width/2:
collision_text = "Blue went out of bounds./nRed wins!"
if y_blue > height/2 or y_blue < -1*height/2:
collision_text = "Blue went out of bounds./nRed wins!"
if x_red > width/2 or x_red < -1*width/2:
collision_text = "Red went out of bounds./nBlue wins!"
if y_red > height/2 or y_red < -1*height/2:
collision_text = "Red went out of bounds./nBlue wins!"
if collision_text:
message_turtle.write( collision_text,
font=('Arial', 25, 'italic'), align='center')
pause_game()
screen = turtle.Screen()
screen.setup( width, height)
screen.bgpic( 'TronBg.png' )
screen.bgcolor( 'black' )
screen.addshape( 'BlueBike.gif')
screen.addshape( 'RedBike.gif' )
# Create the Blue Player Turtle and its controls
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()
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()
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()
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")
for i in range(3,-1,-1):
countdown_turtle.write(i, font=('Arial', 25, 'italic'), align='center')
time.sleep(1)
countdown_turtle.undo()
screen.listen()
# Establishing a screen.ontimer() loop driving the turtles movement
def gameloop():
screen.ontimer(gameloop, 50) # 10ms (0.01s) (1000ms/10ms = 100 FPS)
if game_paused: return
if blue_player.is_moving: blue_player.forward(1)
if red_player.is_moving: red_player.forward(1)
check_collisions()
gameloop()
# Start processing of key-presses
screen.mainloop()
As turtle builds upon tkinter it should be possible to draw on tkinter Canvas what will make it easier to check collisions with lines drawn by the turtles. But this is sure another chapter with many quirks to overcome on the path to this goal.
CodePudding user response:
First, you should call collisions()
in the gameloop()
function so it checks for a collision each time the players move. After doing this, remove the while
from the function. Also, have the function return True
if there is a collision to end the game.
def gameloop():
move_player(red_player, red_direction)
move_player(blue_player, blue_direction)
# Check for a collision. End game if there is one
if collisions(): return
#Repeat after 10ms (0.01s) (1000ms/10ms = 100 FPS)
screen.ontimer(gameloop, 10)
# Helper function to print end of game message
def game_over(message):
TurtleCrash = turtle.Turtle(visible=False)
TurtleCrash.color("white")
style = ('Arial', 25, 'italic')
TurtleCrash.write(f"{message}\nGame over!", font=style, align='center')
left = -(width // 2)
right = -left
bottom = -(height // 2)
top = -bottom
def collisions():
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):
game_over("Red and Blue Crashed!")
return True
if blue_player.xcor() > right or blue_player.xcor() < left:
game_over("Blue went out of bounds.\nRed wins!")
return True
if blue_player.ycor() > top or blue_player.ycor() < bottom:
game_over("Blue went out of bounds.\nRed wins!")
return True
if red_player.xcor() > right or red_player.xcor() < left:
game_over("Red went out of bounds.\nBlue wins!")
return True
if red_player.ycor() > top or red_player.ycor() < bottom:
game_over("Red went out of bounds.\nBlue wins!")
return True
return False
As far as intersecting with a line, my initial suggestion would be to read the pixel at the position the turtle is moving to and if it is not black, then there is a collision. Unfortunately, as far as I can see, there's no easy way to read a pixel with Turtle. So you need to keep a list of all the positions a player has visited and use that. Or maybe used a more advanced engine like PyGame.
There are lots of places to improve the code. If you want to get some feedback, post the code over at https://codereview.stackexchange.com/.