pythonpython-turtlepong

pong ball is moving in a weird manner, suddenly speeds up


The ball is moving but on a sudden the velocity increases, watch at 0:01.
link for the video:

https://streamable.com/qmtqaq

Additionally, at the edge of the paddle the ball transgress inside , doesn't collide and bounce back. Also, when the ball collide at the top of the paddle ball moves speedy at horizontally

https://streamable.com/9geekj

https://streamable.com/qs5fkq

#main.py
from turtle import Screen
from pong_setup import Paddle
from ball import Ball
import time
screen = Screen()
ball = Ball()

screen.bgcolor("black")
screen.setup(width=800, height=600)
screen.title("Pong")
screen.tracer(0)
screen.listen()

computer = Paddle(x=350, y=0)
user = Paddle(x=-350, y=0)
screen.onkey(fun=computer.mv_fd,key="w")
screen.onkey(fun=computer.mv_bk,key="s")

while True:   
   screen.update()
   ball.move()

   # collisions 
   if ball.ycor() >= 290 or ball.ycor() < -290:
      ball.bounce_y()
   if (ball.distance(computer) < 20 and ball.xcor() > 340):
      ball.bounce_x()
screen.exitonclick()
#ball.py
import time
from turtle import Turtle

class Ball(Turtle):
   def __init__(self):
      super().__init__()
      self.shape("circle")
      self.color("white")
      self.penup()
      self.x_move = 0.05
      self.y_move = 0.05
   
   def move(self):
      new_x = self.xcor() + self.x_move
      new_y = self.ycor() + self.y_move
      self.goto(new_x, new_y)


   def bounce_y(self):
     self.y_move *= -1
   def bounce_x(self):
     self.x_move *= -1
#paddle.py
from turtle import Turtle

class Paddle(Turtle):
   def __init__(self,x,y):
      super().__init__()
      # self.speed(0)
      self.shape("square")
      self.penup()
      self.color("white")
      self.turtlesize(5,1)
      self.goto(x,y)

   def mv_fd(self):
      self.goto(350,self.ycor()+30)
   def mv_bk(self):
      self.goto(350,self.ycor()-30)

If the ball collides at the middle of the paddle then it bounce off(works fine). I tried to change the speed of ball. The result was still buggy for the cases mentioned above


Solution

  • As everyone has mentioned, a while loop runs at varaible speed, this is because it goes as fast as possible, but depending on what's occuring on your computer at any moment this will vary.

    To fix this we need to measure the amount of real time that has occurred, if lots of time has passed since the last update, we need to move the ball a lot, if not, then we don't move it a lot. This is what measuring a delta time between updates and then multiplying by it does.

    Here is some python code which does this using the library pygame which you will have to install to run this demo. Feel free to re-implement this using turtle and built in time libraries in python.

    import pygame
    import sys
    
    pygame.init()
    
    screen_width = 800
    screen_height = 600
    screen = pygame.display.set_mode((screen_width, screen_height))
    pygame.display.set_caption("moving ball with controllable paddle")
    
    WHITE = (255, 255, 255)
    BLACK = (0, 0, 0)
    
    ball_radius = 20
    ball_x = screen_width // 2
    ball_y = screen_height // 2
    ball_speed_x = 300  # Pixels per second
    ball_speed_y = 300  # Pixels per second
    
    paddle_width = 100
    paddle_height = 20
    paddle_x = (screen_width - paddle_width) // 2
    paddle_y = screen_height - 50
    paddle_speed = 500
    
    prev_time = pygame.time.get_ticks()
    while True:
        # Calculate delta time
        current_time = pygame.time.get_ticks()
        dt = (current_time - prev_time) / 1000.0  # Convert to seconds
        prev_time = current_time
    
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()
    
        keys = pygame.key.get_pressed()
        if keys[pygame.K_LEFT]:
            paddle_x -= paddle_speed * dt
        if keys[pygame.K_RIGHT]:
            paddle_x += paddle_speed * dt
    
        if paddle_x < 0:
            paddle_x = 0
        elif paddle_x > screen_width - paddle_width:
            paddle_x = screen_width - paddle_width
    
        ball_x += ball_speed_x * dt
        ball_y += ball_speed_y * dt
    
        if ball_x + ball_radius >= screen_width or ball_x - ball_radius <= 0:
            ball_speed_x *= -1
        if ball_y + ball_radius >= screen_height or ball_y - ball_radius <= 0:        
            ball_speed_y *= -1
    
        if ball_y + ball_radius >= paddle_y and ball_x >= paddle_x and ball_x <= paddle_x + paddle_width:
            ball_speed_y *= -1
    
        screen.fill(BLACK)
    
        pygame.draw.circle(screen, WHITE, (int(ball_x), int(ball_y)), ball_radius)
        pygame.draw.rect(screen, WHITE, (paddle_x, paddle_y, paddle_width, paddle_height))
        pygame.display.flip()
    
        pygame.time.Clock().tick(60)
    
    

    See if you can then remake your example and add a secondary paddle using this as an example.