Search code examples
pythonturtle-graphicsevent-listenerpython-turtle

Using tracer delays the eventlistener response in turtle library, python


so I am trying to create a snake game using the turtle GUI library in python. However when I am using the tracer function of the turtle library and screen class for the smooth animations, my eventlistener's(keyboard inputs) response are appearing a lot delayed. Whenever I turn the tracer off, eventlistener's response again are immediate. Can anyone help me and explain why this is happening also what can I do for the immediate keyboard input to be appropriately dealt with on the screen. Thanks in advance! Here is the program for your reference.

import turtle
from snake import Snake
import time

turtle.mode("standard")
window = turtle.Screen()
window.setup(width=600, height=600)
window.bgcolor("black")
window.title("Snake game")
window.tracer(n=0)
window.listen()
snake = Snake()
window.onkey(fun=snake.goup, key="Up")
window.onkey(fun=snake.godown, key="Down")
window.onkey(fun=snake.goleft, key="Left")
window.onkey(fun=snake.goright, key="Right")
window.onkey(fun=snake.goup, key="w")
window.onkey(fun=snake.godown, key="s")
window.onkey(fun=snake.goleft, key="a")
window.onkey(fun=snake.goright, key="d")

game_is_on = True
while game_is_on:
    window.update()
    time.sleep(1)
    snake.move()
    window.update()

window.exitonclick()

Solution

  • Turning tracer on hands animation control over to turtle. With tracer enabled, movements are smooth, but difficult to control. This mode is generally best for debugging drawings. For most games and real-time animation, you'll want to disable tracer and use update() as you're doing, running 30 or 60 frames per second (FPS).

    Although common, this is a suboptimal event loop:

    game_is_on = True
    while game_is_on:
        window.update()
        time.sleep(1)
        snake.move()
        window.update()
    

    time.sleep(1) means you're running at 1 frame per second (or thereabouts). But the sleeping uses up some time, so the framerate isn't terribly consistent. Also, call update() once per frame, at the end.

    Here's a better event loop using ontimer:

    def tick():
        snake.move()
    
        # keep these at the end of the func and only call them once:
        window.update()
        window.ontimer(tick, 1000 // 60)  # 60 FPS
    
    
    tick()
    window.exitonclick()
    

    If the snake runs too fast or too slow, adjust to taste. Some snake games use "jerky" step-based movement rather than smooth movement, so if this is the case in your game, you might want to share your Snake class so I can update the answer to match. Slowing down the framerate or using a second timer is useful.

    Also, it's generally best to only process keyboard-triggered movement and user interactions inside the update loop. Instead of moving the snake as soon as a key is pressed, register that the key is pressed and let the update loop handle it on the next iteration. This avoids OS-specific keyboard retriggering problems.

    See How to bind several key presses together in turtle graphics? for my suggested basic real-time turtle setup, with the correct key handling approach.