I'm new to Python but I've coded in other languages, mainly for hardware. I made pong in Python using turtle but it's a little glitchy. I was wondering if any of you could check it out and give advice. I also want to add a start screen and end screen but am a little unsure how to. Also if you have any advice on how to improve collision detection, please let me know.
I'm pasting code below
import os
import math
import random
import time#set up screen
screen = turtle.Screen()
screen.bgcolor("green")
screen.title("Pong")# set up border
border_pen = turtle.Turtle()
border_pen.speed(0)
border_pen.color("white")
border_pen.penup()
border_pen.setposition(-300,-300)
border_pen.pendown()
border_pen.pensize(3)
for side in range(4):border_pen.fd(600)border_pen.lt(90)
border_pen.hideturtle()#set score to 0
score = 0#set time to zero
time = 0
seconds = 0#Draw score
score_pen = turtle.Turtle()
score_pen.speed(0)
score_pen.color("white")
score_pen.penup()
score_pen.setposition(-290, 310)
scorestring = "Score %s" %score
score_pen.write(scorestring, False, align="left", font= ("Arial", 14, "normal"))
score_pen.hideturtle()#Draw timer
time_pen = turtle.Turtle()
time_pen.speed(0)
time_pen.color("white")
time_pen.penup()
time_pen.setposition(260, 310)
timestring = "Time %s" %time
time_pen.write(timestring, False, align="left", font= ("Arial", 14, "normal"))
time_pen.hideturtle()#create the player turtle
player = turtle.Turtle()
player.color("blue")
player.shape("square")
player.shapesize(0.5, 4)
player.penup()
player.speed(0)
player.setposition(-280,-250)#(x,y)
player.setheading(90)
playerspeed = 15#create the AIplayer turtle
AIplayer = turtle.Turtle()
AIplayer.color("black")
AIplayer.shape("square")
AIplayer.shapesize(0.5, 4)
AIplayer.penup()
AIplayer.speed(0)
AIplayer.setposition(280,250)#(x,y)
AIplayer.setheading(90)
AIplayerspeed = 15#create the pong
pong = turtle.Turtle()
pong.color("red")
pong.shape("circle")
pong.shapesize(0.5, 0.5)
pong.penup()
pong.speed(10)
pong.setposition(0,0)#(x,y)
pongspeed = 15
pong.goto(0, 265)
pong.dy = -5
pong.dx = 5#Move player up and down
def move_up():y = player.ycor()y += playerspeedif y > 265:y = 260player.sety(y)def move_down():y = player.ycor()y -= playerspeedif y < -265:y = -260player.sety(y)#keyboard bindings
turtle.listen()
turtle.onkey(move_up, "Up")
turtle.onkey(move_down, "Down")
#turtle.onkey(fire_bullet, "space")def isCollision(t1, t2):distance = math.sqrt(math.pow(t1.xcor()- t2.xcor(),2)+math.pow(t1.ycor()-t2.ycor(),2))if distance < 20:return Trueelse:return False#main game loop
while True:#move pong ballpong.sety(pong.ycor() +pong.dy)pong.setx(pong.xcor() +pong.dx)#check for bounce and redirect itif pong.ycor() < -300:pong.dy *= -1if pong.ycor() > 300:pong.dy *= -1if pong.xcor() < -300:pong.dx *= -1print("Game Over")exit()if pong.xcor() > 300:pong.dx *= -1#move AI paddle (might speed up pong movement)y = pong.ycor()y += AIplayerspeedAIplayer.sety(y)if AIplayer.ycor() > 265:AIplayerspeed *= -1 if AIplayer.ycor() < -250:AIplayerspeed *= -1#collision pong and playerif isCollision(pong, player):pong.dy *= -1pong.dx *= -1#Update the scorescore += 10scorestring = "Score: %s" %scorescore_pen.clear()score_pen.write(scorestring, False, align="left", font=("Arial", 14, "normal"))#collision pong and AIplayerif isCollision(pong, AIplayer):pong.dy *= -1pong.dx *= -1#updates timer and increases ball speedif seconds > 29:pong.dy *= -2pong.dx *= -2if seconds > 59:pong.dy *= -3pong.dx *= -3#displays timer but makes game laggy
# seconds += 0.1
# time = seconds
# timestring = "Time: %s" %time
# time_pen.clear()
# time_pen.write(timestring, False, align="Left", font=("Arial", 14, "normal"))
Some issues I see:
You've stretched a 20 by 20 square to be 10 by 80:
AIplayer.shapesize(0.5, 4)
But your collision distance is only 20 from center, so your ball can
cross over the bottom or top of the paddle without actually
colliding.
Your isCollision()
function is mostly redundant with turtle's own
.distance()
method.
You shouldn't have while True:
in an event-driven world like turtle
as it potentially prevents some events from firing. Better to
replace it with a timer event.
Some of your collisions change both x and y deltas when they should
only change one of them.
You should avoid redundant queries of the turtle in your main loop as well as checking conditions that are negated by other logic clauses. I.e. do as little as you can get away with in the main loop.
Below is my rework of your game along the above lines, plus lots of other style and logic changes:
from turtle import Turtle, ScreenFONT = ("Arial", 16, "normal")def isCollision(t1, t2):return t1.distance(t2) < 15# set up screen
screen = Screen()
screen.bgcolor("darkgreen")
screen.title("Pong")# set up border
border_pen = Turtle(visible=False)
border_pen.speed('fastest')
border_pen.color('white')
border_pen.pensize(3)border_pen.penup()
border_pen.setposition(-300, -300)
border_pen.pendown()for _ in range(4):border_pen.forward(600)border_pen.left(90)# set score to 0
score = 0# set time to zero
seconds = 0# Draw score
score_pen = Turtle(visible=False)
score_pen.color("white")
score_pen.penup()
score_pen.setposition(-290, 310)score_pen.write("Score {}".format(score), False, align="left", font=FONT)# Draw timer
time_pen = Turtle(visible=False)
time_pen.color("white")
time_pen.penup()
time_pen.setposition(260, 310)time_pen.write("Time {}".format(int(seconds)), False, align="left", font=FONT)# create the player turtle
player = Turtle("square", visible=False)
player.shapesize(0.5, 3)
player.speed('fastest')
player.setheading(90)
player.color("blue")
player.penup()player.setposition(-280, -250) # (x,y)
player.showturtle()playerspeed = 15# create the AIplayer turtle
AIplayer = Turtle("square", visible=False)
AIplayer.shapesize(0.5, 3)
AIplayer.speed('fastest')
AIplayer.setheading(90)
AIplayer.color("black")
AIplayer.penup()AIplayer.setposition(280, 250) # (x,y)
AIplayer.showturtle()AIplayerspeed = 15# create the pong
pong = Turtle("circle", visible=False)
pong.shapesize(0.5, 0.5)
pong.speed('fast')
pong.color("red")
pong.penup()pong.sety(265)
pong.showturtle()pongspeed = 15
pong_dx, pong_dy = 5, -5# Move player up and down
def move_up():player.forward(playerspeed)y = player.ycor()if y > 265:y = 260player.sety(y)screen.update()def move_down():player.backward(playerspeed)y = player.ycor()if y < -265:y = -260player.sety(y)screen.update()# keyboard bindings
screen.onkey(move_up, "Up")
screen.onkey(move_down, "Down")screen.listen()# main game loop
def move():global pong_dx, pong_dy, AIplayerspeed, seconds, score# move pong ballx, y = pong.position()x += pong_dxy += pong_dypong.setposition(x, y)if isCollision(pong, player): # collision pong and playerpong_dx *= -1# Update the scorescore += 10score_pen.undo()score_pen.write("Score: {}".format(score), align="left", font=FONT)elif isCollision(pong, AIplayer): # collision pong and AIplayerpong_dx *= -1elif y < -300 or y > 300: # check for bounce and redirect itpong_dy *= -1elif x > 300:pong_dx *= -1elif x < -300:print("Game Over")screen.bye()return# move AI paddle (might speed up pong movement)AIplayer.forward(AIplayerspeed)y = AIplayer.ycor()if y < -250 or y > 250:AIplayerspeed *= -1# display timerseconds += 0.05time_pen.undo()time_pen.write("Time: {}".format(int(seconds)), False, align="Left", font=FONT)screen.ontimer(move, 50)screen.update()screen.tracer(False)
move()
screen.mainloop()
I couldn't make sense of your "increases ball speed" logic so I left it out. I used a darker background color as the original was difficult to look at on my screen.