I am attempting to make pong using Turtle, I have most things working, however, I would like to implement the ability to hold a key to move the bumpers up and down, rather than having to tap the key multiple times. These are my functions for movement so far.
def lbump_move_up():
x = lbump.ycor()
x += bumpspeed
if x > 240:
x = 240
lbump.sety(x)
def lbump_move_down():
x = lbump.ycor()
x -= bumpspeed
if x < -240:
x = -240
lbump.sety(x)
def rbump_move_up():
x = rbump.ycor()
x += bumpspeed
if x > 240:
x = 240
rbump.sety(x)
def rbump_move_down():
x = rbump.ycor()
x -= bumpspeed
if x < -240:
x = -240
rbump.sety(x)
turtle.listen()
turtle.onkey(lbump_move_up,'w')
turtle.onkey(rbump_move_up,'Up')
turtle.onkey(lbump_move_down,'s')
turtle.onkey(rbump_move_down,'Down')
turtle.onkey(ball_move,'Return')
I don't have a complete answer to this, as I came here looking for one myself. However, I have some progress on it that someone else could finish...
You can create a new class that calls like a recursive function as follows:
class function2:
def __init__(self,fun,args=None):
self.fun=fun
self.args=args
def begin_loop(self):
self.looping=True
self.loop()
def loop(self):
self.fun(self.args)
if self.looping: self.loop()
def end_loop(self):
self.looping=False
Now to tie it to your example, you can convert your functions into function2s and thus call them onkeypress as follows:
l_up=function2(lbump_move_up)
r_up=function2(rbump_move_up)
l_down=function2(lbump_move_down)
r_down=function2(rbump_move_down)
Wn=turtle.Screen()
Wn.onkeypress(l_up.begin_loop,'w')
Wn.onkeypress(r_up.begin_loop,'Up')
Wn.onkeypress(l_down.begin_loop,'s')
Wn.onkeypress(r_down.begin_loop,'Down')
Wn.onkeyrelease(l_up.end_loop,'w')
Wn.onkeyrelease(r_up.end_loop,'Up')
Wn.onkeyrelease(l_down.end_loop,'s')
Wn.onkeyrelease(r_down.end_loop,'Down')
Wn.listen()
So to the issue: RecursionErrors
If you want your function to cause a small change, then you'll need to hold the button for a long time and that causes RecursionErrors - which at least in this case don't seem to be affected by try/except statements.
Once again, sorry for the incomplete solution, but I feel like you can probably get away with larger movements in pong.
I had the same issue, but found the "onkeypress" function in the Turtle documentation (https://docs.python.org/3/library/turtle.html#turtle.onkeypress), which resolved the issue for me.
turtle.onkeypress(fun, key=None)
"onkeypress", will move the turtle as long as the key is pressed, while "onkey" will move it once when the key is released.
Example:
from turtle import Turtle, Screen
turtle = Turtle("turtle")
turtle.penup()
screen = Screen()
screen.listen()
def move_forward():
turtle.forward(20)
def move_backward():
turtle.backward(20)
screen.onkeypress(move_forward, "w")
screen.onkey(move_backward, "s")
screen.exitonclick()
Related
The code is as follows:
import turtle
width = 400
length = 300
wn = turtle.Screen()
wn.bgcolor("black")
wn.title("x")
drawer = turtle.Turtle()
drawer.speed(3)
drawer.begin_fill()
drawer.color("blue", "yellow")
def drawern():
drawer.seth(90)
drawer.fd(1)
def drawerw():
drawer.seth(180)
drawer.fd(1)
def drawers():
drawer.seth(270)
drawer.fd(1)
def drawere():
drawer.seth(0)
drawer.fd(1)
wn.onkeypress(drawern, "w")
wn.onkeypress(drawerw, "a")
wn.onkeypress(drawers, "s")
wn.onkeypress(drawere, "d")
wn.listen()
wn.mainloop()
It gives a stack overflow error. Does anyone know why this issue persists? It doesn't happen when i let go of it once in a while.
I believe the stack overflow error is due to repeated assigning of angle to the stack. To prevent this, you can introduce a debounce. We will name our debounce as move.
A debounce, in simple terms, is fail-safe to prevent an event for triggering again and again while keeping the rest of the code running.
Define the variable in global space:
move = False
As for the function:
def drawern():
global move #To let the function know the variable is from global scope
if not move: #The key is pressed first time
drawer.seth(90)
move = True #Sets our debounce to True, it will not activate until the key is released
drawer.fd(1)
wn.onkeypress(drawern, "w")
We need to have another function with an event to reset our debounce:
def reset_db():
global move
move = False #Resets the debounce, referencing the key has been released
wn.onkeyrelease(reset_db, "w") #Connects the event
I have demonstrated for w key here only. You can duplicate it for the rest of the keys too.
When I try to fire my knife in my game, it doesn't work. I am not very experienced in pygame. This is my first game that I'm making. It worked the first time, but now I try to call a new knife in a function and it doesn't work. I have tried a lot but it won't work. Here's my is my code:
# first I do this
realknifeImg = pygame.image.load('realknife.png')
Realknife_state = "ready"
# then this
def fire_realknife(x, y):
global Realknife_state
Realknife_state = "fire"
screen.blit(realknifeImg, (x, y))
# and finally this
def outro():
RealknifeX = 750
RealknifeY = 400
fire_realknife(RealknifeX, RealknifeY)
if Realknife_state == "fire":
fire_realknife(RealknifeX, RealknifeY)
RealknifeX += 1
The position of the knife is continuously initialized in the outro. Set to position before the function and use the global statement to change the position in the function:
RealknifeX = 750
RealknifeY = 400
def outro():
global RealknifeX, RealknifeY
fire_realknife(RealknifeX, RealknifeY)
if Realknife_state == "fire":
RealknifeX += 1
You need to use a rect for this. It is the standard way of moving
images in pygame.
Instead of having all these variables you can just say:
r_knife_rect = realKnifeImg.get_rect()
r_knife_rect.x = 750
r_knife_rect.y = 400
Then whenever you want to move it call:
r_knife_rect.x += 1
Pygame wont like you using a tuple of (x,y) in your blit, it likes
rect style objects instead. To blit just say:
screen.blit(realKnifeImg, real_knife_rect)
Edit:
Using this also means your knife has a premade hitbox.
I tried myself at making the Pong project using the turtle package on Python.
I know how to make the code work (using 4 different functions taking no arguments, for each paddle and directions), but my question is if and how can I do it with this idea.
Here's the code I'm attempting to make work :
def move_paddle_up(paddle):
y = paddle.ycor()
y += 20
paddle.sety(y)
def move_paddle_down(paddle):
y = paddle.ycor()
y -= 20
paddle.sety(y)
#Keyboard binding movement
screen.listen()
screen.onkeypress(move_paddle_up(left_pad), "w")
screen.onkeypress(move_paddle_down(left_pad), "s")
screen.onkeypress(move_paddle_up(right_pad), "Up")
screen.onkeypress(move_paddle_down(right_pad), "Down")
When the screen is launched, the paddles won't move if I press the associated keys.
With the way of using the 4 different functions, it works.
It's just that I am curious to know how I should call or define the arguments in such a function.
I see then that is it quite more work than just creating 4 functions.
I think you got the wrong take home message. #BehdadAbdollahiMoghadam's use of functools.partial() is spot on, but we can fit it into your existing code with less overhead:
from functools import partial
# ...
def move_paddle_up(paddle):
paddle.sety(paddle.ycor() + 20)
def move_paddle_down(paddle):
paddle.sety(paddle.ycor() - 20)
# Keyboard binding movement
screen.onkeypress(partial(move_paddle_up, left_pad), "w")
screen.onkeypress(partial(move_paddle_down, left_pad), "s")
screen.onkeypress(partial(move_paddle_up, right_pad), "Up")
screen.onkeypress(partial(move_paddle_down, right_pad), "Down")
screen.listen()
# ...
The onkeypress method doesn't allow to pass a function with arguments, however you can use functools.partial for this kind of situations.
You can create a function which handles all type of keyboard presses, and pass that function as a partial function.
Let's create that function first:
def move_paddle(paddle, key_pressed):
def move_paddle_up(paddle):
y = paddle.ycor()
y += 20
paddle.sety(y)
def move_paddle_down(paddle):
y = paddle.ycor()
y -= 20
paddle.sety(y)
if paddle == left_pad:
if key_pressed == "w":
move_paddle_up(paddle)
elif key_pressed == "s":
move_paddle_down(paddle)
elif paddle == right_pad:
if key_pressed == "Up":
move_paddle_up(paddle)
elif key_pressed == "Down":
move_paddle_down(paddle)
Now we can use this function with functools.partial:
import functools
[...]
screen.onkeypress(functools.partial(move_paddle,[left_pad,"w"]), key="w")
screen.onkeypress(functools.partial(move_paddle,[left_pad,"s"]), key="s")
screen.onkeypress(functools.partial(move_paddle,[right_pad,"Up"]), key="Up")
screen.onkeypress(functools.partial(move_paddle,[right_pad,"Down"]), key="Down")
The way functools.partial works:
It takes a function with some arguments (*args, it's actually an unpacked list), and then creates a new function which is the previous function that gets those args as input:
def partial(func, /, *args, **keywords):
def newfunc(*fargs, **fkeywords):
newkeywords = {**keywords, **fkeywords}
return func(*args, *fargs, **newkeywords)
newfunc.func = func
newfunc.args = args
newfunc.keywords = keywords
return newfunc
So I'm trying to monkey patch the onkey function in turtle so it calls a function with the button hit instead of just calling it with no arguments. I'm using the string "tester" to see if it works, but it looks like the original functions never got changes. Can someone explain what I'm doing wrong?
from threading import Thread
from time import sleep
from turtle import *
def NEWonkeypress(self, fun, key=None):
if fun is None:
if key in self._keys:
self._keys.remove(key)
elif key is not None and key not in self._keys:
self._keys.append(key)
self._onkeypress(fun, key)
def _NEWonkeypress(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:
def eventfun(event):
fun("tester")
if key is None:
self.cv.bind("<KeyPress>", eventfun)
else:
self.cv.bind("<KeyPress-%s>" % key, eventfun)
Turtle.onkeypress = NEWonkeypress
Turtle._onkeypress = _NEWonkeypress
board = Turtle()
screen = board.getscreen()
screen.tracer(0, 0)
temp = Turtle()
def textinput(testing):
print(testing)
def getroomname(option):
global temp
global board
#Box
temp.fillcolor("White")
temp.width(10)
temp.goto(-150, -60)
temp.down()
temp.begin_fill()
for x in range(2):
temp.forward(300)
temp.left(90)
temp.forward(120)
temp.left(90)
temp.end_fill()
temp.up()
temp.goto(0, 100)
screen.update()
#Box
temp.goto(0, -60)
screen.onkeypress(textinput)
listen()
getroomname(0)
mainloop()
(This is just a snippet of the main code, so don't worry about the random square it draws in space)
It's actually simpler than you're making it, we just need to go about it a little differently. When you run the code below, any key you type should get passed through turtle's event system and printed to the console:
from functools import partial
from turtle import Screen, Turtle
def getroomname():
temp.penup()
temp.goto(-150, -60)
temp.pendown()
temp.begin_fill()
for _ in range(2):
temp.forward(300)
temp.left(90)
temp.forward(120)
temp.left(90)
temp.end_fill()
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:
def eventfun(event):
fun(event.char)
if key is None:
self.cv.bind("<KeyPress>", eventfun)
else:
self.cv.bind("<KeyPress-%s>" % key, eventfun)
def keyinput(key):
print(key)
screen = Screen()
screen._onkeypress = partial(_onkeypress, screen) # monkey patch (protected access)
temp = Turtle()
temp.speed('fastest')
temp.fillcolor("white")
temp.width(10)
getroomname()
screen.onkeypress(keyinput)
screen.listen()
screen.mainloop()
I simplified your unrelated code. However, you're not using global correctly so I suggest you review a tutorial about that before you run into trouble. Also, you did from turtle import * but then did def textinput(...) where textinput is also method of Python 3 turtle -- so avoid doing from turtle import * to avoid even more trouble.
So i have this platforming game that im programming in pygame. I have a jump method and i want to add a dely between jumps. That way i dont keep jumping into the ceiling. Heres the code
def jump(self):
if (self.onGround == True):
return
self.velocity = 10
self.onGround = False
This is what calls it in the main game loop:
if (event.key==pygame.K_UP):
player.jump()
Keep track of the last time you jumped, or of the next time you're allowed to jump (with pygame.time):
def __init__(self, …):
# ...
self.next_jump = 0
def jump(self):
if pygame.time.get_ticks() < self.next_jump:
return
# ...
self.next_jump = pygame.time.getticks() + 500
Depending on which type of loop you've gone with (traditional event loop, frame-rate-limited loop, unlimited-frame-rate loop) there may be better answers involving, e.g., setting a timer or using a Clock, but this will work with any type.