Search code examples
pythonpython-3.xtkinterturtle-graphicspython-turtle

Pong game using python's turtle module: Why is ball moving way smoother than player paddles?


Got the ball to move smoother by setting ball move rate at 2.5 units instead of 10 and sleep time between each iteration of the loop to 0.125 instead if 0.05, increasing ball fps by 4 times. But to my understanding. it makes sense that the paddles should've sped up 4 times as well since i didn't reduce their 20 units move rate to 1/4th. Can anyone tell why paddles are way choppier than the ball? Thanks. Omitted out the scoreboard object and its functionality for ease of reading.

main game code:

from turtle import Screen
from paddle import Paddle
from ball import Ball
import time

screen = Screen()
screen.setup(width=800, height=600)
screen.bgcolor('black')
screen.title('Pong: First one to 7 wins!')
screen.listen()
screen.tracer(0)

r_paddle = Paddle((350,0))
l_paddle = Paddle((-350,0))
ball = Ball()


screen.onkey(fun=r_paddle.up, key='Up')
screen.onkey(fun=r_paddle.down, key='Down')
screen.onkey(fun=l_paddle.up, key='w')
screen.onkey(fun=l_paddle.down, key='s')


is_game_on = True

while is_game_on:
    ball.move()

    if ball.ycor() >= 285 or ball.ycor() <= -285:
        ball.wall_bounce()

    
    if ball.xcor() >= 330 and ball.xcor() < 350 and ball.distance(r_paddle) < 50:
        ball.paddle_bounce()
        while not ball.xcor() <  330:
            screen.update()
            time.sleep(0.0125)
            ball.move()

    elif ball.xcor() > 390:
        time.sleep(0.75)

    
    if ball.xcor() <= -330 and ball.xcor() > -350 and ball.distance(l_paddle) < 50:
        ball.paddle_bounce()
        while not ball.xcor() > -330:
            screen.update()
            time.sleep(0.0125)
            ball.move()
    elif ball.xcor() < -390:
        time.sleep(0.75)


    screen.update()
    time.sleep(0.0125)

screen.exitonclick()

ball code:

from turtle import Turtle
import random as r

class Ball(Turtle):

    def __init__(self):
        super().__init__()
        self.speed(0)
        self.color('white')
        self.pu()
        self.shape('circle')
        self.goto(0,0)
        self.rand_pos()

    def move(self):
        self.setx(self.xcor() + self.x_move)
        self.sety(self.ycor() + self.y_move)

    def rand_pos(self):
        self.x_move = 2.5* r.randrange(-1,2,2)
        self.y_move = 2.5* r.randrange(-1,2,2)

    def wall_bounce(self):
        self.y_move *= -1

    def paddle_bounce(self):
        self.x_move *= -1

    def reset(self):
        self.goto(0, 0)
        self.rand_pos()

paddle code:

from turtle import Turtle


MOVE_DISTANCE = 20

UP = 90

class Paddle(Turtle):

    def __init__(self, co_ordinates):
        super().__init__()
        self.speed(0)
        self.shape('square')
        self.pu()
        self.color('white')
        self.shapesize(stretch_len=5, stretch_wid=1)
        self.goto(co_ordinates)
        self.seth(UP)

    def up(self):
        self.fd(MOVE_DISTANCE)

    def down(self):
        self.bk(MOVE_DISTANCE) 

Tried changing the paddle movement values. Also tried putting this block of code from the main game code in the while loop:

screen.onkey(fun=r_paddle.up, key='Up')
screen.onkey(fun=r_paddle.down, key='Down')
screen.onkey(fun=l_paddle.up, key='w')
screen.onkey(fun=l_paddle.down, key='s')

didn't work.


Solution

  • The reason why the paddles movement is choppy is because of the way you handle keyboard inputs: in your code, the paddles move when you release a movement key. This means you need to press a key multiple times in order to get a longer movement.

    I'm guessing you want to move the paddle when the key is pressed. So, you need a way to get if the movement key is pressed, then move accordingly.

    You can solve this issue by using this class:

    class Key:
        def __init__(self, key):
            self.state = False
            screen.onkeypress(self.press, key)
            screen.onkeyrelease(self.release, key)
    
        def press(self): self.state = True
        def release(self): self.state = False
    
        def __bool__(self):
            return self.state
    

    And use it like this:

    keys = {key: Key(key) for key in ['Up', 'Down', 's', 'w']}
    

    What this will do is create Key objects that will record the states of the given keys. You will then be able to get a bool value corresponding to whether they are pressed or not. This way, you can use keys[key] to see if a key is pressed:

    while is_game_on:
        if keys['Up']: r_paddle.up()
        if keys['Down']: r_paddle.down()
        if keys['w']: l_paddle.up()
        if keys['s']: l_paddle.down()
    
        ...
    

    Other recommendations:

    • You should add ball.paddle-bounce() after each time.sleep(0.75) as the ball goes out of the screen (but since you said you had a scoring system I think this is already handled in the full game)
    • After implementing the fix, you should decrease MOVE_DISTANCE to something like 5