Turtle.onkeypress isn't firing - python

I'm very new to Python and have made a couple small games during a Python Learning course but never at home. So recently I began making a game, but after just 10 minutes I stumbled upon a problem:
Nothing happened when I pressed "W" although I had writen onkeypress in the code.
See for your self:
(It's designed for full screen)
import turtle
s = turtle.Screen()
g = turtle.Turtle()
t = turtle.Turtle()
#Ground
t.speed(0)
t.up()
t.goto(-1000,-200)
t.down()
t.goto(1000,-200)
#Player
g.speed(0)
PlayerX = -600
def moveX():
g.clear()
global PlayerX
g.up()
g.goto(PlayerX,-99)
g.down()
g.color("Slate Gray")
g.begin_fill()
g.circle(-50)
g.end_fill()
PlayerX = PlayerX - 1
turtle.onkeypress(moveX, "w")
moveX()
I'm fully aware I haven't made a go backwards button.

Along with #doctorlove's spot on correction (+1) of adding listen() to allow the window to receive keyboard events, a couple of comments:
First, click on the window with your mouse to make it active otherwise it won't respond to the keyboard. Second, it can be helpful to deactivate the event handler while in the event hander, and reactivate it on the way out, to avoid problems if someone repeatedly presses the key very fast.
Here's the second comment along with some other code suggestions:
from turtle import Turtle, Screen
screen = Screen()
screen.setup(1200, 500)
# Ground
ground = Turtle()
ground.speed('fastest')
ground.penup()
ground.goto(-1000, -200)
ground.pendown()
ground.forward(2000)
# Player
player = Turtle()
player.speed('fastest')
PlayerX = -600
def moveX():
global PlayerX
screen.onkeypress(None, "w") # disable handler in handler
player.clear()
player.penup()
player.goto(PlayerX, -99)
player.pendown()
player.color("Slate Gray")
player.begin_fill()
player.circle(-50)
player.end_fill()
PlayerX -= 1
screen.onkeypress(moveX, "w") # reenable handler
screen.listen()
moveX()
screen.mainloop() # change import & use turtle.mainloop() if Python 2
mainloop() isn't required to run but the program will exit after your initial moveX() call without it. mainloop() turns control over to the Tk event handler so some events may not fire without it.
You'll need to change onkeypress() to onkey() if this is Python 2 as well as change the way that mainloop() is invoked.

I think it's called onkey not onkeypress.
Also I think you need to listen (and add a mainloop if you want it to run):
turtle.onkey(moveX, "w")
turtle.listen()
moveX() # draw things first
turtle.mainloop()
You may need to revisit the numbers you are using to make sure the shape is on the window.

with my version of python none of the others are actually correct, here is the modified code that works for me:
from turtle import Turtle, Screen, setpos, hideturtle
screen = Screen()
screen.setup(500, 500)
#Ground
t = Turtle()
t.speed(0)
t.up()
t.goto(-1000,-200)
t.down()
t.goto(1000,-200)
#Player
player = Turtle()
hideturtle()
player.speed(0)
setpos(0,0)
PlayerX = 0
def moveX():
player.clear()
global PlayerX
player.up()
player.goto(PlayerX,0)
player.down()
player.color("Slate Gray")
player.begin_fill()
player.circle(-50)
player.end_fill()
PlayerX = PlayerX - 1
screen.onkey(moveX, "w")
screen.listen()
(this can definitely be improved on)

Not sure if the change is with Python3. But onkey function seems to be dragged under Screen().
turtle.Screen().onkey(movex, "w")

#This is a short code I made using space as down and w as up, feel free to
#extract from it what you can.
import turtle
player = turtle.Turtle()
y = 0
wn = turtle.Screen()
def p_up():
global y,up
up = True
while(up==True):
y += 10
player.sety(y)
def p_down():
global y,down
down = True
while(down==True):
y -= 10
player.sety(y)
def up_stop():
global up
up = False
def down_stop():
global down
down = False
wn.listen()
wn.onkeypress(p_up,"w")
wn.onkeypress(p_down,"space")
wn.onkeyrelease(up_stop,"w")
wn.onkeyrelease(down_stop,"space")
wn.mainloop()

Related

How to properly write an .onclick() in Python?

I am attempting to create a counter that updates every time the button is clicked. (This is my first solo program.) There seems to be a problem with the .onclick() function I have written, and I am unsure of what's wrong.
Here is my code for reference. . .
Thank you for your help.
import turtle
wn = turtle.Screen()
wn.title("Button Counting")
wn.bgcolor("red")
wn.setup(width=800, height=600)
wn.tracer(0)
written = 0
def click(x, y):
wn.update()
x = 0
y = 0
written += 1
pen.clear()
pen.write(" {} ".format(written), align="center")
button = turtle.Turtle()
button.penup()
button.color("white")
button.shape("square")
button.shapesize(stretch_wid=5, stretch_len=5)
button.onclick(click)
pen = turtle.Turtle()
pen.speed(0)
pen.color("black")
pen.penup()
pen.hideturtle()
pen.goto(0, 0)
pen.write(" 0 ")
The explanation is down below.
This is one solution:
import turtle
written=0
wn=turtle.Screen()
wn.title("Button Counting")
wn.bgcolor("red")
wn.setup(width=800,height=800)
button=turtle.Turtle()
button.penup()
button.color("white")
button.shape("square")
button.shapesize(stretch_wid=5, stretch_len=5)
pen = turtle.Turtle()
pen.speed(0)
pen.color("black")
pen.penup()
pen.hideturtle()
pen.goto(0, 0)
pen.write(" 0 ")
def click(x,y):
global written
wn.update()
written += 1
x=0
y=0
pen.clear()
pen.write(" {} ".format(written), align="center")
turtle.listen()
button.onclick(click)
turtle.mainloop()
When using turtle.onkey/turtle.onclicketc you need to make the turtle first listen by doing turtle.listen() and then you add button.onclick(click)then turtle.mainloop() because if you don't add it, the program would immediately stop.
The changes I made were removingwd.tracer(0)because then it would hide the "button" adding turtle.listen() button.onclick(click) turtle.mainloop() and adding global written to the define click because you are changing the value of written in the define .If you have any questions just ask!
I agree with #JonathanDrukker that two key issues are your misuse of tracer() and update(), which aren't even needed in this program, and the missing global declaration for written. However, his initial issue with respect to listen() is a red herring as it only applies to keystrokes, not mouse clicks. The necessity for mainloop() depends on your Python environment.
I'd go with a simpler implementation as adding spaces to a centered string does little of value, and speed() is of no real use in this context:
from turtle import Screen, Turtle
written = 0
def click(x, y):
global written
written += 1
pen.clear()
pen.write(written, align='center')
screen = Screen()
screen.setup(width=800, height=600)
screen.title("Button Counting")
screen.bgcolor('red')
button = Turtle()
button.shape('square')
button.shapesize(5)
button.color('white')
button.onclick(click)
pen = Turtle()
pen.hideturtle()
pen.write(written, align='center')
screen.mainloop()
However, this program, like your original and Jonathan's example, is fragile as it writes in the same place that a turtle is visible. Slight changes to the code could cause it to stop working if the turtle is on top. I would move the pen to somewhere like (100, 100) so that it isn't in the middle of another turtle.

having trouble registering collision

I've been having trouble with getting my bullet (named asteroid) and my zombie. more specifically, I'm having trouble getting my game to register collision between these two turtles, it gets even weirder when you reverse the lesser than symbol into a greater than symbol. I do not know what is up with my code, any help is appreciated.(I have included the entirety of my code, since I am unsure of the source of the problem, I just know which part isn't working, I would recommend starting there.)
#the bullet that doesn't hit it's target
#Turtle Graphics game
import turtle
import random
import time
#set up screen
wn = turtle.Screen()
wn.bgcolor("grey")
finish= False
def randor1():
rand=random.randint(-280,280)
def randor2():
rand=random.randint(50,280)
def check_target_pos():
#side boundary checking
if zombie.xcor() > 280 or zombie.xcor() <- 280:
zombie.right(180)
#top/bottom boundary checking
if zombie.ycor() > 280 or zombie.ycor() <- 280:
zombie.right(180)
def check_turtle_pos():
#side boundary checking
if asteroid.xcor() > 280 or asteroid.xcor() <- 280:
asteroid.right(180)
#top/bottom boundary checking
if asteroid.ycor() > 280 or asteroid.ycor() <- 280:
asteroid.right(180)
def new_asteroid():# the turtle bullet,will change the name later on
for i in range(50):
asteroid.forward(10)
asteroid.goto(0,0)
def k2():#turn turtle left
asteroid.left(45)
def k3():#turn turtle right
asteroid.right(45)
#Draw border for arena
mypen = turtle.Turtle()
mypen.penup()
mypen.setposition(-300,-300)
mypen.pendown()
mypen.pensize(3)
for side in range(4):
mypen.forward(600)
mypen.left(90)
mypen.hideturtle()
#create turtle turtle, again will change name later
asteroid = turtle.Turtle()
asteroid.color("green")
asteroid.shape("turtle")
asteroid.penup()
asteroid.speed(0)
#create turtle zombie
def zombies():
global zombie
zombie= turtle.Turtle()
zombie.hideturtle()
zombie.color("green")
zombie.shape("circle")
zombie.penup()
zombie.speed(0)
x= random.randint(-280,280)
y= random.randint(50,280)
zombie.goto(x,y)
zombie.showturtle()
zombies()
while (finish!= True):
check_target_pos()
check_turtle_pos()
zombie.forward(1)
def end():
finish==True
wn.bye()
if asteroid.distance(zombie)<40: #problem area
end()
wn.onkey(new_asteroid, "space")#shoot button.
wn.onkey(k2, "Left")#turn left button
wn.onkey(k3, "Right") #turn right button
wn.onkey(end, "e")#exit
wn.listen()#so all the on key functions above work
Your code is somewhat a mess and doesn't facilitate the event-driven nature of turtle. There is no place for while True: loops nor time.sleep() or such in this environment. You also seem to be confusing your gun with your bullet (you're flinging your gun into space to kill the zombie, not its bullet!)
I've rewritten your code below using an event-based model with ontimer(). This is a single bullet implementation (you can't fire again until your bullet hits something or disappears into the distance):
from turtle import Screen, Turtle
from random import randint
def check_bullet_position():
if bullet.distance(turtle) > 240:
bullet.hideturtle()
if bullet.distance(zombie) < 20:
bullet.hideturtle()
reset_zombie()
def shoot_bullet():
if bullet.isvisible():
return
bullet.setheading(turtle.heading())
bullet.setposition(turtle.position())
bullet.forward(15)
bullet.showturtle()
def turn_left():
turtle.left(20)
def turn_right():
turtle.right(20)
def reset_zombie():
while zombie.distance(turtle) < 240:
x = randint(-280, 280)
y = randint(-280, 280)
zombie.goto(x, y)
zombie.setheading(zombie.towards(turtle))
def move():
zombie.forward(1)
if bullet.isvisible():
bullet.forward(2)
check_bullet_position()
screen.update()
if turtle.distance(zombie) > 20:
screen.ontimer(move)
# Set up screen
screen = Screen()
screen.tracer(False)
screen.bgcolor('grey')
# Draw border for arena
pen = Turtle()
pen.hideturtle()
pen.pensize(3)
pen.penup()
pen.setposition(-300, -300)
pen.pendown()
for _ in range(4):
pen.forward(600)
pen.left(90)
# Create turtle turtle
turtle = Turtle()
turtle.shape('turtle')
turtle.color('green')
turtle.penup()
# Create turtle bullet
bullet = Turtle()
bullet.hideturtle()
bullet.shape('circle')
bullet.shapesize(0.5)
bullet.color('yellow')
bullet.penup()
# Create turtle zombie
zombie = Turtle()
zombie.shape('circle')
zombie.color('red')
zombie.penup()
reset_zombie()
screen.onkey(shoot_bullet, 'space')
screen.onkey(turn_left, 'Left')
screen.onkey(turn_right, 'Right')
screen.onkey(screen.bye, 'e') # exit
screen.listen() # enable onkey() functions above
move()
screen.mainloop()
This should give you a starting point for building the game you envision. It is possible to write a multiple bullet implementation, it just take a little more thought and design work.
Your searching for a collision outside of the loop where you shot the projectile.
The easiest way to make your code work is to add the collision detection to the new_asteroid() function. But I don't think that's the right way.
Your technique is what is referred to as blocking. while you're projectile is moving, no other code is running. hence your collision detection is not working. change Line 38 to this.
def new_asteroid():# the turtle bullet,will change the name later on
for i in range(50):
asteroid.forward(10)
if asteroid.distance(zombie) < 40: # problem area
end()
asteroid.goto(0,0)
To create a non-blocking version of this function you would have to divide the for loop up to increments called by your main loop. so that your projectile moves once per iteration, instead of all at once.
Your program also crashes on exit. May I recommend re organizing your code into functions, then execution. it makes it much more readable. Happy gaming.

How to make multiple clones run at the same time in python turtle

I am trying to make a code where you can press the spacebar and an object will move forwards constantly. I am hoping to be able to have multiple of these objects moving at once without having to code hundreds of them separately.
This is my current code:
Bullet:
bullet = turtle.Turtle()
bullet.speed(0)
bullet.shape("circle")
bullet.color("red")
bullet.shapesize(stretch_wid=0.5, stretch_len=0.5)
bullet.penup()
bullet.goto(-200, -200)
bullet.hideturtle()
Movement:
def shoot_bullet():
stop = False
bullet2 = bullet.clone()
bullet2.showturtle()
while stop == False:
y = bullet2.ycor()
bullet2.sety(y + 20)
wn.update()
time.sleep(0.5)
...
onkeypress(shoot_bullet, "space")
This works until I press space again and the bullet just stops as 'bullet2' has been redefined as the new bullet I create when I press space. Is there a way to create multiple clones which can run on top of each other?
Your while stop == False: loop and time.sleep(0.5) have no place in an event-driven environment like turtle. Instead, as we fire each bullet, the below code attaches a timer event that moves it along until it disappears. At which point the bullet is recycled.
This simplified example just shoots bullets in random directions from the center of the screen. You can keep hitting the space bar to generate simultaneous bullets that all move in their own direction until they get far enough away:
from turtle import Screen, Turtle
from random import randrange
def move_bullet(bullet):
bullet.forward(15)
if bullet.distance((0, 0)) > 400:
bullet.hideturtle()
bullets.append(bullet)
else:
screen.ontimer(lambda b=bullet: move_bullet(b), 50)
screen.update()
def shoot_bullet():
screen.onkey(None, 'space') # disable handler inside hander
bullet = bullets.pop() if bullets else bullet_prototype.clone()
bullet.home()
bullet.setheading(randrange(0, 360))
bullet.showturtle()
move_bullet(bullet)
screen.onkey(shoot_bullet, 'space') # reenable handler on exit
bullet_prototype = Turtle('circle')
bullet_prototype.hideturtle()
bullet_prototype.dot(10) # just for this example, not for actual code
bullet_prototype.shapesize(0.5)
bullet_prototype.color('red')
bullet_prototype.penup()
bullets = []
screen = Screen()
screen.tracer(False)
screen.onkey(shoot_bullet, 'space')
screen.listen()
screen.mainloop()

Binding Key Press Events Using Turtle

I'm new to Python and am trying a bunch of different projects to learn. I want to use Turtle to create a game and I found this guy on YouTube who walks through re-creating Space Invaders.
I'm using IDLE and Python 3. The screen and player are created, but nothing happens when I press a key. I have looked up this issue and tried a number of things, but I'm not sure what I'm doing wrong.
The other unusual thing is that each function is run once. I included a print statement in each function to discover this. Why is it running each key press event once, but not binding to my actual keyboard?
import turtle
#Screen setup
screen = turtle.Screen()
screen.bgcolor('black')
screen.title("Space Invaders")
#Create player
player = turtle.Turtle()
player.color('blue')
player.shape('triangle')
player.penup()
player.speed(0)
player.setposition(0, -250)
player.setheading(90)
playerspeed = 15
#Move the player left and right
def move_left():
x = player.xcor()
x -= playerspeed
player.setx(x)
screen.listen()
print("Move left.") #for debugging
def move_right():
x = player.xcor()
x += playerspeed
player.setx(x)
screen.listen()
print("Move right.") #for debugging
#Create keyboard binding
screen.onkey(move_left(), 'Left')
screen.onkey(move_right(), 'Right')
screen.listen()
#Play game
screen.mainloop()
The problem is with these two lines of code:
screen.onkey(move_left(), 'Left')
screen.onkey(move_right(), 'Right')
You don't want to call move_left(), you want to pass move_left to be called by the event handler when the key is pressed:
screen.onkey(move_left, 'Left')
screen.onkey(move_right, 'Right')
By including the parentheses, you pass the return value of move_left() which is None, effectively disabling the event instead of enabling it!
Here's a rework of your code with the above fix plus another trick: space invader type games are perfect for taking advantage of the rarely used turtle.settiltangle() method. This method allows us to make the turtle appear to point vertically, while actually oriented horizontally. So we can simply used forward() and backward() to move it across the screen:
from turtle import Screen, Turtle
PLAYER_SPEED = 15
# Move the player left and right
def move_left():
player.backward(PLAYER_SPEED)
def move_right():
player.forward(PLAYER_SPEED)
# Screen setup
screen = Screen()
screen.bgcolor('black')
screen.title("Space Invaders")
# Create player
player = Turtle('triangle')
player.speed('fastest')
player.color('blue')
player.penup()
player.sety(-250)
player.settiltangle(90)
# Create keyboard binding
screen.onkey(move_left, 'Left')
screen.onkey(move_right, 'Right')
screen.listen()
# Play game
screen.mainloop()
Of course, you have to remember when you fire a projectile that your turtle is pointing right and redirect it accordingly!
I think the guy you found on YouTube might have been using python 2.7 rather than python 3 which would change the keypress commands.
Instead of
screen.onkey(move_left(), 'Left')
screen.onkey(move_right(), 'Right')
screen.listen()
You should use
screen.listen()
screen.onkeypress(move_left, 'Left')
screen.onkeypress(move_right, 'Right')

players not moving simultaneously

I am attempting to make a game in python with the turtle module, I have the square moving towards the player (the circle) and the aim is for the circle to jump over the square and not get hit.
The player can jump by pressing the spacebar,
but every time you hit the space bar to jump the player jumps, but the square stops moving and you are unable to jump over.
here is my code:
import turtle
import time
wn = turtle.Screen()
wn.bgcolor("white")
wn.title("dinosaur run")
wn.tracer(1,20)
floor = turtle.Turtle()
floor.fd(370)
floor.bk(370*2)
floor.ht()
player = turtle.Turtle()
player.shape("circle")
player.penup()
player.setpos(-370,14)
def jump():
player.lt(90)
player.fd(40)
time.sleep(0.5)
player.bk(40)
player.rt(90)
turtle.listen()
turtle.onkey(jump, "space")
class cactus(turtle.Turtle):
turtle.shape("square")
turtle.penup()
turtle.speed(0)
turtle.setpos(370,14)
cactusspeed = 2
while True:
x = turtle.xcor()
x -= cactusspeed
turtle.setx(x)
Thanks a lot,
all ideas welcome,
I've tried wn.update() at the end
As provided above, your code doesn't run at all as cactusspeed never gets defined. And your class cactus doesn't have a hope of working as currently laid out (reread about Python classes.) Finally, your while True: has no business in an event driven world like turtle.
Below is my rework of your code to use an ontimer() event to control the cactus independent of the player. I also eliminated the sleep() and simply made the player move slower and jump higher. I believe this should give you the dynamic you're looking for:
from turtle import Turtle, Screen
def jump():
player.forward(100)
player.backward(100)
def move():
if cactus.xcor() < -screen.window_width()/2:
cactus.hideturtle()
cactus.setx(370)
cactus.showturtle()
else:
cactus.forward(cactusspeed)
screen.ontimer(move, 40)
screen = Screen()
floor = Turtle(visible=False)
floor.speed('fastest')
floor.fd(370)
floor.bk(370 * 2)
player = Turtle("circle", visible=False)
player.penup()
player.setpos(-370, 14)
player.setheading(90)
player.speed('slowest')
player.showturtle()
cactusspeed = 4
cactus = Turtle("square", visible=False)
cactus.speed('fastest')
cactus.penup()
cactus.setpos(370, 14)
cactus.setheading(180)
cactus.showturtle()
screen.onkey(jump, "space")
screen.listen()
move()
screen.mainloop()

Categories

Resources