This question already has answers here:
How to efficiently hold a key in Pygame?
(7 answers)
Closed 8 years ago.
I am trying to write a Pong game in which I can move the paddle using the up and down arrows. I created a Paddle object that updates by a given amount (passed as the parameter "num" in the code below) each time the up key is pressed. What I want to do is allow the user to hold the up button down which would cause the paddle to continuously move in the up direction. I tried writing this event handling in a while loop, but I got stuck in an infinite loop. Here is the code below:
for event in PE.get():
if event.type == PG.KEYDOWN:
keystate = PG.key.get_pressed()
if event.key == PG.K_ESCAPE:
done = True
while keystate[PL.K_UP]:
rightPaddle.update(-20)
if event.type == PG.QUIT:
done = True
Below is the code for the Paddle object:
class Paddle(object):
def __init__(self, x, y):
self.topleftx = x
self.toplefty = y
self.width = 15
self.height = 100
def draw(self, surface):
rect = PR.Rect(self.topleftx, self.toplefty, \
self.width, self.height)
surface.fill(WHITE, rect)
def update(self, num):
self.toplefty += num
I checked other stackoverflow questions on this topic, which is how I found the PG.key.getPressed() method, but other answers suggest using "sprites" which I'm not sure how to use. Could anyone offer some advice on how to go about performing this task?
The problem with the while loop is that it's evaluating the one keydown event. That event will always be whatever it starts as, and there's no chance for it to change within the event handling code. What you want to catch is the PG.KEYUP event that follows it. So you'll have something (logically) like
if event.type == PG.KEYDOWN and keystate[PL.K_UP]:
key_up_pressed = True
elif event.type == PG.KEYUP and keystate[PL.K_UP]:
key_up_pressed = False
Obviously, you can factor that better, but the key_up_pressed state will get evaluated in your main loop once every so often (frame, millisecond, whatever), and move the paddle.
In this code:
while keystate[PL.K_UP]:
rightPaddle.update(-20)
… there is nothing that can change keystate. So, if keystate[PL.K_UP] is true once, it will be true forever. Hence the infinite loop.
You could fetch the current key information from pygame each time through the loop, which would at least make it not infinite—but it would still mean your entire program blocks up until the user releases the key.
Without knowing whether you intended to write a frame-rate-driven game or an event-loop-driven game, I can't tell you exactly how to fix it.
But either way, the key point is that you don't want to move over and over as long as the key is held down, you want to move at a certain speed, processing other events in between, as long as the key is held down. Either way, you'll use the KEYDOWN and KEYUP events to set some kind of flag that tells you to move the paddle. Then, once per frame, or using a timer event, or in some other way, you will use the current flag value to move the paddle.
Related
I'm making a Tetris game now. I want to implement it so that if I press the down key , the block falls quickly, and if I press the left and right keys, the block moves quickly. Pressing the key used the pygame.key.get_pressed() function and used pygame.time.set_timer() function to make speed change. The game speed was set to 600 for the interval of pygame.time.set_timer(), but if I press the down key, the block drops quickly because the interval was set to 150 to speed up the game so that the block drops quickly. The problem is to implement the function on the left and right direction keys. It is also possible to move the left and right keys quickly if I change the interval. The problem is that the pygame.time.set_timer() function changes the speed of the entire game, so the block falls quickly as well as the left and right movements of the block. Is there a way to speed up left and right movements without touching the speed of other things? I'd appreciate it if you let me know, thanks!
code
elif start:
for event in pygame.event.get():
attack_stack = 0
pos = pygame.mouse.get_pos()
if event.type == QUIT:
done = True
elif event.type == USEREVENT:
# Set speed
if not game_over:
keys_pressed = pygame.key.get_pressed()
# Soft drop
if keys_pressed[K_DOWN]:
pygame.time.set_timer(pygame.USEREVENT, 100)
elif keys_pressed[K_RIGHT]:
pygame.time.set_timer(pygame.USEREVENT, 100)
if not is_rightedge1(dx, dy, mino_en, rotation, matrix):
ui_variables.move_sound.play()
dx += 1
elif keys_pressed[K_LEFT]:
pygame.time.set_timer(pygame.USEREVENT, 100)
if not is_leftedge1(dx, dy, mino_en, rotation, matrix):
ui_variables.move_sound.play()
dx -= 1
else:
pygame.time.set_timer(pygame.USEREVENT, 600)
Waiting a long time (100 ms is already long) before reacting to user input feels bad, so you don’t want to wait a full piece-movement time before checking for input again just because there is none at the moment you check. Instead, poll for input at a steady pace of (say) 30 Hz; for simplicity, the usual approach is to just run the whole game at that frequency even if nothing needs to change on the screen for some (or even most) frames. (This technique also naturally allows smooth animations of or between piece movements.)
The usual implementation of “nothing” on a frame is to adjust the piece’s position every frame but integer-divide that position by some constant before using it for anything other than keeping track of its progress toward the next movement (like drawing it or checking for collisions). You might use pixels as the “invisible unit” when movement must be by whole tiles; games where sprites can be drawn at any pixel divide each into some convenient number of subpixels in the same fashion.
It looks to me that the reason your piece is dropping faster when you press a button is because if the user doesn't press a button the timer is set to 600ms. If they do press a button then the timer is set to 100ms.
As for how to fix this, you'll need to decouple the downward piece movement and user-input movement. I'd suggest making another event that only handles downward movement.
DROPEVENT = pygame.USEREVENT + 1
elif event.Type == DROPEVENT:
pygame.time.set_timer(DROPEVENT, 600)
# Your piece drop logic.
This snippet is not complete nor tested at all, but I hope it get the idea across. You don't seem to have posted the logic that handles dropping the piece, but you would need to move this into the new event.
This question already has answers here:
Pygame unresponsive display
(1 answer)
Pygame window not responding after a few seconds
(3 answers)
Closed 2 years ago.
my code:
def shop_button(self):
click = pygame.mouse.get_pressed()
if click[0] == 1:
self.open_shop() # draws shop
self.inshop = True
while self.inshop:
pygame.time.delay(10)
mousepos = pygame.mouse.get_pos()
if click[0] == 1 and b.mouse_on_button(mousepos[0], mousepos[1], b.escapex, b.escapex2, b.escapey, b.escapey2):
self.inshop = False
whenever you click on the shop button this function is run, however, while self.inshop is inside an if statement like such the game instantly stops responding and crashes. This could be a very simple solution as I am semi-new to pygame and the python language as a whole. However, with that being said I can't seem to find a solution anywhere else.
The game does not crash, it does not respond, because you have a loop inside the main application loop. The inner loop has no event handling and update of the display.
def shop_button(self):
# [...]
while self.inshop:
pygame.time.delay(10)
mousepos = pygame.mouse.get_pos()
# [...]
The position which is returned by pygame.mouse.get_pos() is read from an internal cache. The cache is updated when the events are handled by either pygame.event.pump() or pygame.event.get().
That means if you retrieve the position in a loop, without handling the events, the the position does not reflect the current mouse position, it is continuously the same position, the position at the point when pygame.event.get() called, before the loop.
Furthermore the display Surface won't change, without updating the display, by either pygame.display.flip() or pygame.display.update().
You have a main application loop, use it. The main application loop has to:
handle the events by either pygame.event.pump() or pygame.event.get().
update the game states and positions of objects dependent on the input events and time (respectively frames)
clear the entire display or draw the background
draw the entire scene (blit all the objects)
update the display by either pygame.display.update() or pygame.display.flip()
Never implemented nested application loops. You have to change the control flow and the design of your applcaition.
How do you make the code stop for a moment before checking for something else? (I'm new to code)
if BonusTime==True and Drop==True:
if event.type == pygame.MOUSEBUTTONDOWN:
window.blit(Fired,(mouseX-12,mouseY-12))
Cankill=True
#I want it to delay here
Cankill=False
There is a cross hair that follows the mouse and when I click it, it fires. The problem is, you can just hold the mouse down and leave the cross hair in one place. Whenever an enemy walks into the cross hair, they die. I need it so even when you hold it will only fire once. I plan to make it delay the if statement, to set "Cankill" to true and then wait a second, after waiting, it should set "Cankill" to false. I've already looked through a lot of other people's questions similar to this and haven't found something I can understand. So if you could please help me find out how to delay it in the way I need.
"Pausing" is not what you want - if you do that, your game will just freeze, and nothing will move (since you are usign the OS mouse pointer, maybe it could move).
Anyway, the function to "pause" inside a game is pygame.time.wait(1000) - the number is in miliseconds.
Waht you actually need is to mark down the time the click was made, continue with the game, and when 1 second has passed, reset the variable back to the other state.
Something along:
last_trigger = 0
while True:
# game updating code goes here (getting events, and such)
...
if Cankill and pygame.time.get_ticks() - last_trigger > 1000:
Cankill = False
if event.type == pygame.MOUSEBUTTONDOWN:
window.blit(Fired,(mouseX-12,mouseY-12))
Cankill=True
last_trigger = pygame.time.get_ticks()
The "get_ticks" call returns the number of miliseconds that passed since pygame was initialized - and is usefull for this time of timers.
This question already has answers here:
Pygame how to fix 'trailing pixels'?
(4 answers)
Closed 5 years ago.
I'm trying to develop a simple game in python using pygame and IDLE. I have, since yesterday, looked through a variety of sources in order to learn about the language (and programming in general), even so I have encountered some problems and misunderstandings of how it all works. So, if anyone could please advise me on how to proceed (or point me in the direction of some good learning material) then I would appreciate it greatly.
So far, I've got a small bit of code that forms the basis of my game idea, so I will post it here and list some of my problems.
import pygame
def main():
pygame.init()
logo = pygame.image.load("coolblack.jpg")
pygame.display.set_icon(logo)
pygame.display.set_caption("Battleship")
screenWidth = 800
screenHeight = 600
screen = pygame.display.set_mode((screenWidth, screenHeight))
bgd_image = pygame.image.load("grid.png")
#--------------------------------------------------------------------
#the image named 'image' should be above 'bgd_image' but below 'cv9'
#in fact, everything should be above bgd_image, especially 'cv9'
#--------------------------------------------------------------------
image = pygame.image.load("coolblack.jpg")
cv9 = pygame.image.load("ussessexcv9.gif").convert_alpha()
xposCv9 = 400
yposCv9 = 510
step_xCv9 = 1
step_yCv9 = 1
screen.blit(bgd_image, (0,0))
screen.blit(image, (400,300))
screen.blit(cv9, (xposCv9, yposCv9))
pygame.display.flip()
clock = pygame.time.Clock()
running = True
#---------------------------------------------
#I've got a pretty good idea (sort of) about
#what is happening in the section
#below this point, however it seems that
#the image 'cv9' creates a trail of itself
#every time it moves, so how could I make it
#so that it doesn't do so?
#---------------------------------------------
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
running = False
if xposCv9>screenWidth-64 or xposCv9<0:
step_xCv9 = -step_xCv9
if yposCv9>screenHeight-64 or yposCv9<0:
step_yCv9 = -step_yCv9
xposCv9 += step_xCv9
yposCv9 += step_yCv9
screen.blit(cv9, (xposCv9, yposCv9))
pygame.display.flip()
clock.tick(60)
if __name__=="__main__":
main()
The way that pygame works is that it has internally a representation of the screen which you are updating. So, it starts entirely black, then you do your first "blit". This will update the internal representation. Then when you call "pygame.display.flip" it shows that representation on the screen. However, this will not automatically "clear" the representation for you back to all black for your next frame. So, on the next frame, you blit again (slightly to the left, say), and the first blit remains, creating your "trail".
Therefore, for what you're doing, the best thing would be to in your loop, clear the internal representation of the screen before you start drawing the next frame. You can "clear" the screen by filling it with a single color, like so...
BLACK = (0,0,0)
....
screen.blit(cv9, (xposCv9, yposCv9))
pygame.display.flip()
clock.tick(60)
screen.fill(BLACK) # Add this to "clear" the screen.
Note that if you chose to go this route, this means you will need to redraw ALL of the elements every single frame (not just the ones that changed since the last frame).
BTW, in case you are wondering, there is a good reason for why the frame is not automatically cleared at the end. In some cases, it might be faster to only update the parts of the screen that update. This can cause performance speedups in some applications. However, it's probably best to start with clearing the screen as the example shows above.
Consider these lines in the pygame loop:
ev = pygame.event.poll()
ev.type == pygame.QUIT
From: http://openbookproject.net/thinkcs/python/english3e/pygame.html
From what I understand the function pygame.event.poll() creates an instance of the Event class in the event module of the pygame package.
I. Now ev.type is an attribute call(?) but how do I know which values it can have? How can you even tell from the pygame documentation that it has the possibility to equal pygame.QUIT?
II. What exactly is pygame.QUIT? How does it get a value?
III. help('pygame.QUIT') says pygame.QUIT = class int(object). How do you call this construction?
ev = pygame.event.poll()
is a call to a function that returns a single event from the event queue (basically, a list of things that have happened that your application might want to know about). It assigns that event (which is an Event object) to the variable ev.
ev.type
gets the value of the type attribute of that Event object, which is a numerical constant.
== pygame.QUIT
checks to see if it's equal the numerical constant defined as pygame.QUIT.
The possible event types are listed at http://www.pygame.org/docs/ref/event.html - I've copy-pasted the list here as well (which also lists the associated attributes passed with each event):
QUIT none
ACTIVEEVENT gain, state
KEYDOWN unicode, key, mod
KEYUP key, mod
MOUSEMOTION pos, rel, buttons
MOUSEBUTTONUP pos, button
MOUSEBUTTONDOWN pos, button
JOYAXISMOTION joy, axis, value
JOYBALLMOTION joy, ball, rel
JOYHATMOTION joy, hat, value
JOYBUTTONUP joy, button
JOYBUTTONDOWN joy, button
VIDEORESIZE size, w, h
VIDEOEXPOSE none
USEREVENT code
pygame.QUIT is sent when the user clicks the window's "X" button, or when the system 'asks' for the process to quit. If ignored, it can still be killed by the system. It lets you save, before quitting.
pygame.QUIT is just a constant int that happens to be defined inside the pygame module.
>>> import pygame
>>> pygame.QUIT
12
This is the relevant page in the documentation: http://www.pygame.org/docs/ref/event.html. You can see all the possible event types (just above the comments).
Im not entirely sure what pygame.event.poll() actually does, but you can detect when you window gets closed with for e in pygame.event.get(): if e.type == pygame.QUIT: print('the cross has been clicked', don't know if this is helpfull but it could be a workaround your problem