pythonturtle-graphicspython-turtlepong

Why is the bat/board not moving up or down in the Pong Game, when the screen.listen() is called using the Turtle module in Python?


Using the turtle module to make a pong game. For this part, when I press the up/down keys the board doesn't respond to the onkey listen function.

I created the board/bat (or bat specs function from turtle called

bat_specs_p2()

) and set the positions of the board/bat (x and y) on the screen. All is fine at this point. I know that the y-coord will have to change once the up/down keys are pressed. Testing with print shows that the y-coords are been updated everytime up/down is pressed, but the board/bat is not moving on the screen. I think it is still holding the starting position of the board/bat, somewhere the y-coord is not updating

Bat class

from turtle import Turtle

P1_x = -200
P1_y = 0
P2_x = 200
P2_y = 0


class Bat:

    def __init__(self):

        self.p2_position = (P2_x, P2_y)
        self.p1_position = (P1_x, P1_y)

    def bat_specs_p2(self):
        global P2_y
        bat = Turtle()
        bat.penup()
        bat.shape("square")
        bat.color("white")
        bat.shapesize(stretch_wid=4, stretch_len=1)
        bat.setposition(self.p2_position)


    def p2move_up(self):
        global P2_y
        P2_y += 20
        self.bat_specs_p2()

        print(P2_x, P2_y)

    def p2move_down(self):
        global P2_y
        P2_y -= 20
        self.bat_specs_p2()

        print(P2_x, P2_y)

main.py

from turtle import Screen
import time
from bat import Bat

screen = Screen()
screen.setup(width=600, height=600)
screen.bgcolor("black")
screen.title("Pong Game")

screen.tracer(0)  

bat = Bat()

bat.bat_specs_p2()

screen.listen()
screen.onkey(bat.p2move_up, "Up")
screen.onkey(bat.p2move_down, "Down")


screen.update() 

screen.exitonclick()

Solution

  • You have two correctness problems:

    1. If you use tracer(0) to disable turtle's control of the rendering loop, you need to call screen.update() whenever you want to perform a redraw of the canvas.
    2. You're changing variables, but those variables aren't associated with any turtle object, so they're meaningless as far as turtle is concerned. Turtles have internal position variables, so you don't need to track them separately. bat.setposition(self.p2_position) is an attempt at connecting them, but self.p2_position = (P2_x, P2_y) copies the values of P2_x and P2_y, so when they change globally, the tuple doesn't update (tuples are immutable and primitives are not pass-by-reference).

    Beyond that, your design is a little unusual. Avoid global, especially when you're trying to write a class that should have instance-specific state. The Bat class should only be responsible for one player's bat. Make separate instances of the class for each bat, along with self. for all dynamic state.

    Here's a simple example:

    from turtle import Screen, Turtle
    
    
    class Bat:
        def __init__(self, y=0, x=0, speed=20):
            self.speed = speed
            self.t = t = Turtle()
            t.penup()
            t.shape("square")
            t.color("white")
            t.shapesize(stretch_wid=4, stretch_len=1)
            t.setposition((x, y))
    
        def move_up(self):
            self.t.sety(self.t.ycor() + self.speed)
    
        def move_down(self):
            self.t.sety(self.t.ycor() - self.speed)
    
    
    screen = Screen()
    screen.tracer(0)  
    screen.setup(width=600, height=600)
    screen.bgcolor("black")
    screen.title("Pong Game")
    screen.listen()
    p1 = Bat(x=200)
    p2 = Bat(x=-200)
    
    def with_update(action):
        def update():
            screen.update()
            action()
    
        return update
    
    screen.onkey(with_update(p1.move_up), "Up")
    screen.onkey(with_update(p1.move_down), "Down")
    screen.onkey(with_update(p2.move_up), "w")
    screen.onkey(with_update(p2.move_down), "s")
    screen.update()
    screen.exitonclick()
    

    Now, this isn't great for real-time movement. If you need smooth movement and/or support for multiple key presses at once, you'll want to use ontimer as described in How to bind several key presses together in turtle graphics? to run an update loop independent of the key handlers. Applying those techniques to pong, see Using 2 onkeypress-es (with a thread/process) in Python Turtle.