Elyad K
Elyad K

Reputation: 77

How to move turtles in Python 3 with arrow keys

I am having trouble getting my turtle to be able to follow the arrow keys, any help on how to do so would be greatly appreciated. I'm sure this question has been asked before, though I can't seem to find it, and the ones I do find are for older versions.

import turtle
#screen
wn=turtle.Screen()
wn.bgcolor("lightblue")

I plan on this being a spaceship game
#Turtle Player
spaceship= turtle.Turtle()
spaceship.color("red")
spaceship.penup()
speed=1

This is where I am stuck, I don't know how to make the turtle follow the arrow keys

#keyboard bindings

while True:
    spaceship.forward(speed)

Upvotes: 6

Views: 48218

Answers (3)

Red
Red

Reputation: 27547

First, we got to understand the basics. In order for the user to be able to interact with the turtle through key presses, we need to let the window listen for key presses. Since your screen is named wn, that can be done simply by calling wn.listen().

Now that the turtle graphics are listening for key presses, how will you tell the program to do something through key presses? Functions! Let's say you want a new turtle to be created every time a key is pressed; you will need to define a function like so (you can use lambda for the function, but for now, let's stick to def):

def create_new_turtle():
    new_turtle = turtle.Turtle()

Keep in mind that you shall not pass any positional arguments into the brackets when defining the function, as you will not be able to pass your arguments in when you use the function, resulting in an TypeError.

Now, let's get into how we can actually call these function during run-time when a key is pressed. With wn.listen() initiated, now all you'll need is wn.onkey, or wn.onkeypress. Taking the function above, if you want a new turtle to be created every time the user presses the SPACE key:

wn.onkey(create_new_turtle, 'space')

Do you see why we can't pass positional arguments into the function? As you can see, when using the function inside wn.onkey, we do not call it (as in, we did not add brackets on the right side of the function); wn.onkey does it for us.

Taking what we've learned, let's see them in action:

import turtle

#Screen
wn = turtle.Screen()
wn.bgcolor("lightblue")

#Turtle Player
spaceship = turtle.Turtle()
spaceship.color("red")
spaceship.penup()

#Constant
speed = 1

def up():
    spaceship.setheading(90)

def down():
    spaceship.setheading(270)
    
def left():
    spaceship.setheading(180)

def right():
    spaceship.setheading(0)

wn.listen()
wn.onkey(up, 'Up')
wn.onkey(down, 'Down')
wn.onkey(left, 'Left')
wn.onkey(right, 'Right')

while True:
    spaceship.forward(speed)

Can you guess what this does? It's pretty obvious; when the user hits the 'Up' arrow, the up function defined above will be called, when the user hits the 'Down' arrow, the down function defined above will be called, an so on.

Defining a whole function for a single command doesn't seem right, and we can't just do

wn.onkey(spaceship.setheading(90), 'Up')
wn.onkey(spaceship.setheading(270), 'Down')
wn.onkey(spaceship.setheading(180), 'Left')
wn.onkey(spaceship.setheading(0), 'Right')

Like in the most upvoted answer, the solution is to use lambda, where the error causing code right above can be corrected to

wn.onkey(lambda: spaceship.setheading(90), 'Up')
wn.onkey(lambda: spaceship.setheading(270), 'Down')
wn.onkey(lambda: spaceship.setheading(180), 'Left')
wn.onkey(lambda: spaceship.setheading(0), 'Right')

Lastly, if you want your turtle to turn 90 degrees maximum on each turn, you can avoid 180 degree turn with if statements in the functions (which, as the functions get more advanced, it is better to use def to define the functions instead of using lambda):

import turtle

#Screen
wn = turtle.Screen()
wn.bgcolor("lightblue")

#Turtle Player
spaceship = turtle.Turtle()
spaceship.color("red")
spaceship.penup()

#Constant
speed = 1

def up():
    if spaceship.heading() != 270:
        spaceship.setheading(90)

def down():
    if spaceship.heading() != 90:
        spaceship.setheading(270)
    
def left():
    if spaceship.heading() != 0:
        spaceship.setheading(180)

def right():
    if spaceship.heading() != 180:
        spaceship.setheading(0)

wn.listen()
wn.onkey(up, 'Up')
wn.onkey(down, 'Down')
wn.onkey(left, 'Left')
wn.onkey(right, 'Right')

while True:
    spaceship.forward(speed)

Test run:

enter image description here

Upvotes: 4

cdlane
cdlane

Reputation: 41872

Avoid using an infinite loop like while True: inside a turtle graphics program, it can keep some of your events from firing.

Below is the minimal code I could come up with to make your spaceship navigatible. You should be able to build on this:

from turtle import Turtle, Screen

wn = Screen()
wn.bgcolor('lightblue')

spaceship = Turtle()
spaceship.color('red')
spaceship.penup()

speed = 1

def travel():
    spaceship.forward(speed)
    wn.ontimer(travel, 10)

wn.onkey(lambda: spaceship.setheading(90), 'Up')
wn.onkey(lambda: spaceship.setheading(180), 'Left')
wn.onkey(lambda: spaceship.setheading(0), 'Right')
wn.onkey(lambda: spaceship.setheading(270), 'Down')

wn.listen()

travel()

wn.mainloop()

Click on the turtle graphics window before issuing keyboard commands to make sure it is listening. Also, there are other approaches to how the keys should work, I've used absolute motion here but you might want relative where each press incrementally modifies your direction.

Upvotes: 8

jedruniu
jedruniu

Reputation: 530

I have solution for you. The code is not ideal but it works and you can work on it. You have to be aware, that the turtle has deafult position, and you have to tune it. That is why I pointed in the setup method my turtle to look up.

Now, You have to remember, that right(deg) and left(deg) methods are saying "please turn around by such amount of degree in given direction".

So keep in mind, what was your last direction.

Key for understanding here is that You don't have access to any absolute here. You only can change something in relation to your current posisition. So, you can't turn left, but if you know previous direction, you know how many degree you should turn your turtle to actually turn left.

My working code for your task is:

import turtle
wn = turtle.Screen()

last_pressed = 'up'

def setup(col, x, y, w, s, shape):
  turtle.up()
  turtle.goto(x,y)
  turtle.width(w)
  turtle.turtlesize(s)
  turtle.color(col)
  turtle.shape(shape)
  turtle.lt(90)
  turtle.down()
  wn.onkey(up, "Up")
  wn.onkey(left, "Left")
  wn.onkey(right, "Right")
  wn.onkey(back, "Down")
  wn.onkey(quitTurtles, "Escape")
  wn.listen()
  wn.mainloop()




#Event handlers
def up():
  global last_pressed
  if last_pressed == 'left':
    turtle.rt(90)
    turtle.fd(10)
  elif last_pressed == 'right':
    turtle.lt(90)
    turtle.fd(10)
  elif last_pressed == 'up':
    turtle.fd(10)
  else:
    turtle.rt(180)
    turtle.fd(10)

  last_pressed = 'up'

def left():
  global last_pressed
  if last_pressed == 'left':
    turtle.fd(10)
  elif last_pressed == 'right':
    turtle.lt(180)
    turtle.fd(10)
  elif last_pressed == 'up':
    turtle.lt(90)
    turtle.fd(10)
  else:
    turtle.rt(90)
    turtle.fd(10)

  last_pressed = 'left'


def right():
  global last_pressed
  if last_pressed == 'left':
    turtle.rt(180)
    turtle.fd(10)
  elif last_pressed == 'right':
    turtle.fd(10)
  elif last_pressed == 'up':
    turtle.rt(90)
    turtle.fd(10)
  else:
    turtle.lt(90)
    turtle.fd(10)

  last_pressed = 'right'

def back():
  global last_pressed
  if last_pressed == 'left':
    turtle.lt(90)
    turtle.fd(10)
  elif last_pressed == 'right':
    turtle.rt(90)
    turtle.fd(10)
  elif last_pressed == 'up':
    turtle.rt(180)
    turtle.fd(10)
  else:
    turtle.fd(10)

  last_pressed = 'down'

def quitTurtles():
  wn.bye()

setup("blue",-200,200,2,2,"turtle")

Please bear in mind that it takes some time for the turtle to actually turn, so don't press keys, click them.

I think that You can go further with this.

Upvotes: 0

Related Questions