I'm trying to make a connect-the-dot python game. I want the game to register 2 button presses. Example: if the user presses Up and Right arrow key, the turtle goes 45 degrees north east.
here is my code:
import turtle
flynn=turtle.Turtle()
win=turtle.Screen()
win.bgcolor("LightBlue")
flynn.pensize(7)
flynn.pencolor("lightBlue")
win.listen()
def Up():
flynn.setheading(90)
flynn.forward(25)
def Down():
flynn.setheading(270)
flynn.forward(20)
def Left():
flynn.setheading(180)
flynn.forward(20)
def Right():
flynn.setheading(0)
flynn.forward(20)
def upright():
flynn.setheading(45)
flynn.forward(20)
win.onkey(Up, "Up")
win.onkey(Down,"Down")
win.onkey(Left,"Left")
win.onkey(Right,"Right")
cdlane has an awesome idea here of using ontimer and a set of currently-pressed keys, but I thought I'd try to extend and refine it a bit.
The problem with a secondary loop with ontimer is that it seems to fight the main turtle loop, both in terms of computation and also in terms of thread safety/interleaving, where you can begin iterating the key pressed set and find that a handler has pulled a key out during iteration, raising an error.
The (seemingly poorly-named) tracer(0) function lets you disable turtle's loop so you can call it manually from within the hand-rolled ontimer loop using update(). This reduces some of the choppiness of the competing loops, although I imagine the timer resolution on rolling your own loop with repeated calls to ontimer is less precise than the built-in loop. But I haven't really looked at the source yet -- feel free to leave a comment if you have any insight.
Here's the proof of concept:
from turtle import Screen, Turtle
def tick():
for action in list(keys_pressed):
actions[action]()
win.update()
win.ontimer(tick, frame_delay_ms)
t = Turtle()
win = Screen()
win.tracer(0)
frame_delay_ms = 1000 // 30
step_speed = 10
actions = dict(
Left=lambda: t.left(step_speed),
Right=lambda: t.right(step_speed),
Up=lambda: t.forward(step_speed),
)
keys_pressed = set()
def bind(key):
win.onkeypress(lambda: keys_pressed.add(key), key)
win.onkeyrelease(lambda: keys_pressed.remove(key), key)
for key in actions:
bind(key)
win.listen()
tick()
win.exitonclick()
Ultimately, though, if you want to go much further into realtime graphics and games, Pygame is better equipped.
The approach here does not implement an optimal real-time event loop, just an improvement on the default turtle loop, sufficient for most casual projects.
cdlane has a few good posts on tracer: 1, 2, 3.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With