I could do with some help on how to make a lives counter in Python turtle graphics.
My code:
def draw_lives():
global lives
lives = turtle.Turtle()
lives.penup
lives.hideturtle
lives.goto(-200, 400)
while True:
lives.write("Lives: " + str(lives), font=("Arial", 50, "normal"))
if lives > 0:
lives.write("You have lost all lives. Try again.", font=("Arial", 50, "normal"))
break
I thought on making my lives counter a Turtle and not just a random counter somewhere (which would actually sound better).
Furthermore did I get the error for my if lives > 0: that the > is not supported between instances of Turtle and int.
Can someone help?
Your code is badly constructed -- let's look at specifics. The primary issue is that you use the same variable name, lives, for both the counter and the turtle that displays the counter. Name them differently. If you do so, you don't need:
global lives
The next problem is basic Python:
lives.penup
lives.hideturtle
These are method calls so they should be:
lives.penup()
lives.hideturtle()
Finally your while True: has no business here, or anywere in a turtle event-driven program. And you're missing a line of code or two in your if statement.
Let's rework your code so that it updates the value of the lives counter on the screen:
from turtle import Screen, Turtle
FONT = ("Arial", 24, "normal")
def draw_lives():
lives_pen.clear()
if lives > 0:
lives_pen.write("Lives: " + str(lives), font=FONT)
else:
lives_pen.write("You have lost all lives. Try again.", font=FONT)
lives = 5
lives_pen = Turtle() # this turtle is only used to update lives counter display
lives_pen.hideturtle()
lives_pen.penup()
lives_pen.goto(-200, 300)
lives_pen.write("Lives: " + str(lives), font=FONT)
if __name__ == '__main__':
from time import sleep
# test code
screen = Screen()
for _ in range(lives + 1):
draw_lives()
sleep(1)
lives -= 1
screen.exitonclick()
The __main__ section is just test code to confirm that draw_lives() works the way we want -- so toss it.
Utility turtles like lives_pen should only be created once, not every time you need to update the counter as they are global entities and are not garbage collected when the function exits.
It's a bad practice to use global in your code. Instead, you can create a custom attribute for your turtles.
It's super easy, barely an inconvenience:
from turtle import Turtle
pen = Turtle()
pen.lives = 5 # Here, the lives attribute is created
You may even do the same for the font, though it may be unnecessary:
pen.font = ("Arial", 30, "normal")
If loosing lives is the only situation where the life count will be updated, do not keep rewriting them in the loop
(unless, of course, the something gets in the way of the text, and you want the text to be displayed on top),
only rewrite it when a life is lost.
We can redraw and update the lives in a function like this:
def update_lives():
pen.clear()
if pen.lives:
pen.write(f"Lives: {pen.lives}", font=pen.font)
else:
pen.write("You have lost all lives. Try again.", font=pen.font)
pen.lives -= 1 # Remove this line and this becomes your typical text updater
To see this in action, I implemented a demo where a life is lost whenever the user presses the SPACE bar:
from turtle import Turtle, Screen
wn = Screen()
def update_lives():
pen.clear()
if pen.lives:
pen.write(f"Lives: {pen.lives}", font=pen.font)
else:
pen.write("You have lost all lives. Try again.", font=pen.font)
pen.lives -= 1
pen = Turtle(visible=False)
pen.penup()
pen.goto(-300, 200)
pen.lives = 5
pen.font = ("Arial", 30, "normal")
update_lives()
wn.listen()
wn.onkey(update_lives, 'space')
With the above code, when the user reaches 0, pressing SPACE again will make the function proceed to display negative values.
To fix that, for your main game loop, use while pen.lives to tell python to only keep looping until the number of lives remaining is greater than 0:
while pen.lives:
# Your game code here
wn.update()
Output:
Related
I can't figure out how to move sideways and jump at the same time, only one or the other.
I have tried asyncio and multithreading/multiprocessing and couldn't get it to work. I am all out of ideas and can't find anymore online. I also have another issue where I can jump and if I reach the apex of the jump and hold a or d I can move side to side floating.
class Player():
def __init__(self,health,boosts,height,width):
self.health = health
self.booosts = boosts
self.height = height
self.width = width
def jump():
global gravtest, press
press.remove("'w'")
gravtest = False
y = player[0].ycor()
for i in range (1, 10):
player[0].sety(y+(i*5))
time.sleep(0.05)
#while player[0]
gravtest = True
# def powers(self, boosts):
import turtle as t
import time, random
from pynput.keyboard import Key, Listener
t.ht()
press = []
gravtest = True
wn = t.Screen()
wn.title("Jump Man")
wn.bgcolor("white")
wn.screensize(250, 250)
wn.setup(width=1.0, height=1.0)
player = [t.Turtle(), Player(100, [], 25, 25)]
player[0].speed(0)
player[0].shapesize(0.5)
player[0].shape("square")
player[0].color("black")
player[0].up()
player[0].goto(0, 0)
floor = t.Turtle()
floor.speed(0)
floor.shape("square")
floor.shapesize(100)
floor.color("black")
floor.up()
floor.goto(0, -1150)
def gravity():
global gravtest
if gravtest == True:
grav = 0
while player[0].distance(floor) > 1007:
y = player[0].ycor()
player[0].sety(y + grav)
if grav > -5:
grav -= 1
player[0].sety(y + grav)
gravtest = False
if player[0].distance(floor) < 1045:
player[0].sety(-145)
def show(key):
global press
if not(format(key) in press):
press.append(format(key))
print(key)
def rem(key):
global press
if format(key) in press:
press.remove(format(key))
def move():
global press
while "'a'" in press:
player[0].setx(player[0].xcor()-2)
while "'d'" in press:
player[0].setx(player[0].xcor()+2)
if press == '\'s\'':
print()
gravity()
if "'w'" in press:
jump()
with Listener(on_press = show, on_release = rem) as listener:
while 1:
move()
Your problem with moving and jumping is that you have separate loops for each that try to handle one kind of movement of the movement in one place. That won't work properly if other stuff (like jumping while moving, or moving while falling under gravity) are supposed to be possible.
Instead, you need to have just one main loop. On each run of that loop, you should do one frame worth of movement of whatever kinds is appropriate (e.g. moving horizontally, falling or jumping). This may require some bookkeeping to keep track of how long the vertical acceleration from a jump lasts.
I'd make the main loop something like this:
on_the_ground = True
jumping = False
while True:
horizontal_movement() # this is always possible if the buttons are pressed
if on_the_ground or jumping:
jump() # can start or continue a jump if a button is pressed
else:
gravity() # fall, if you're not grounded or accelerating in a jump
handle_collisions_etc()
time.sleep(1/FRAMERATE)
Note, I've made some assumptions about the game logic you want. If you don't want to be able to move horizontally while in the air (as you can in many platformer games), you'll need to change this a little bit.
The jump function will need to keep track of how long you've been jumping for, since you probably want the player to be limited in how high they can go. I'm not exactly sure I understand the logic of your current code, so I'll leave it up to you to figure out exactly what to track.
A final suggestion is to move some of the global variables into the Player class. Even the turtle object you're using to draw things could be stored as an attribute on the instance you create.
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.
I am attempting to create a study game where a question will fall downward and you type in the answer, however I don't understand how to record key inputs without stopping the motion of the lowering question.
to put simply, I want to be able to lower the question and use my keyboard to input at the same time without stopping the motion.
text_letter = 0
def text_insert(answer):
global text_letter
print("hello")
text_letter += 1
def text_lower(question,answer):
global text_letter
text.penup()
text.goto(random.randint(-250,250),355)
text.pendown()
text.color("white")
text.write("Start", font=("Arial", 20, "normal"))
x,y = text.pos()
delay = .01
wn.textinput("Answer", "Answer:")
turtle.listen()
turtle.onkey(text_insert(answer),answer[text_letter])
while y > -355:
time.sleep(delay)
y -= 1
text.goto(x,y)
text.write(question, font=("Arial", 20, "normal"))
text.clear()
This may be a more complicated answer than you anticipated: If you leave off the second, key, argument to the turtle's onkeypress() function, it will call your key press handler code when any key is pressed. It simply won't tell you which key!
We can work around this poor design by rewriting the underlying code to pass tkinter's event.char to the turtle's event handler in the case where no key has been set.
Once that's done, we can use a turtle timed event to lower the question from the top of the window while the user's typed input shows up at the bottom of the window.
Here's my one question simulation of such to help get you started:
from turtle import Screen, Turtle
from functools import partial
FONT_SIZE = 20
FONT = ("Arial", FONT_SIZE, "normal")
def text_lower(question):
question_turtle.forward(1)
question_turtle.clear()
question_turtle.write(question, align="center", font=FONT)
screen.update()
if question_turtle.ycor() - answer_turtle.ycor() > FONT_SIZE:
screen.ontimer(lambda: text_lower(question), 15)
else:
question_turtle.clear()
def _onkeypress(self, fun, key=None):
if fun is None:
if key is None:
self.cv.unbind("<KeyPress>", None)
else:
self.cv.unbind("<KeyPress-%s>" % key, None)
else:
if key is None:
def eventfun(event):
fun(event.char)
self.cv.bind("<KeyPress>", eventfun)
else:
def eventfun(event):
fun()
self.cv.bind("<KeyPress-%s>" % key, eventfun)
def display_character(character):
global answer
if not character:
return
if ord(character) == 13:
answer_turtle.clear()
answer_turtle.setx(0)
# do something with answer and then:
answer = ""
else:
answer += character
answer_turtle.write(character, move=True, font=FONT)
screen.update()
screen = Screen()
screen.tracer(False)
screen._onkeypress = partial(_onkeypress, screen)
question_turtle = Turtle(visible=False)
question_turtle.penup()
question_turtle.setheading(270)
question_turtle.sety(screen.window_height()/2 - FONT_SIZE)
answer_turtle = Turtle(visible=False)
answer_turtle.penup()
answer_turtle.sety(FONT_SIZE - screen.window_height()/2)
answer = ""
screen.onkeypress(display_character)
screen.listen()
text_lower("What is the air-speed velocity of an unladen swallow?") # A: An African or European swallow?
screen.mainloop()
I am trying to create a basic Turtle Race. My only requirement is that the turtles must move with each click on the screen. I am trying to use onscreenclick to call a function that moves the turtles forward a random amount and then checks their x-coordinates to determine if someone has won.
import turtle
import random
import time
def raceRound():
firstRacer.forward(random.randint(1, 10))
secondRacer.forward(random.randint(1, 10))
checkWinner()
def firstRacerSetup():
# setup turtle
def secondRacerSetup():
# set up second turtle
def screenSetup():
# create screen
def checkWinner():
if firstRacer.xcor() >= 80:
announcer.write("Congratualtions! You won!")
if secondRacer.xcor() >= 80:
announcer.write("Sorry... You lost")
wn = turtle.Screen()
racerName = wn.textinput("Name Entry", "What would you like your turtle's name to be?")
screenSetup()
announcer = turtle.Turtle()
announcer.hideturtle()
announcer.penup()
announcer.goto(0, -140)
announcer.write("Welcome to the race!", align='center')
time.sleep(3)
firstRacerSetup()
secondRacerSetup()
announcer.write("Click to start the race!", align='center')
wn.onscreenclick(raceRound)
turtle.done()
I'm just not sure where I am going wrong and some assistance would be greatly appreciated
In a program I am writing, turtle's onkey() method doesn't do exactly what I need. I know there are also onkeypress and onkeyrelease but neither do what I need. Is there a method for continuously running a function while a key is held? Such as
import turtle
num = 0
def add():
global num
num += 1
print(num)
turtle.onkey(add, "Up")
turtle.listen()
turtle.mainloop()
If you did something like this, for taking the keyboard input, onkey only responds once, and it only responds on the release of the key. Is there a method that would continuously run the function while it is being held down?
Below's a rough example that does what you describe using onkeypress() and onkeyrelease() in combination. Once you press 'Up' it will start counting whole seconds until you release that key. Then it will write to the screen the number of whole seconds the key was held:
from turtle import Turtle, Screen
FONT = ('Arial', 36, 'normal')
def add_start():
global seconds
print('pressed')
if seconds < 0:
turtle.undo() # remove previous time
seconds = 0
screen.ontimer(add, 1000)
def add():
global seconds
if seconds >= 0:
seconds += 1
screen.ontimer(add, 1000)
def add_stop():
global seconds
print('released')
turtle.write(seconds, align='center', font=FONT)
seconds = -1
screen = Screen()
turtle = Turtle(visible=False)
turtle.write(0, align='center', font=FONT)
seconds = -1
screen.onkeypress(add_start, 'Up')
screen.onkeyrelease(add_stop, 'Up')
screen.listen()
screen.mainloop()
Here's the catch: you need to turn off key repeat at the operating system level on your system. For example, On my Mac, in System Preferences / Keyboard, I'd do:
Otherwise the system will generate press and release events as part of standard key repeat.