Why is my pause system not working? (Pygame)

2020-02-16 03:41发布

问题:

Here's my check_for_pause() function:

#Check if the user is trying to pause the game
def check_for_pause():
   keys=pygame.key.get_pressed() #Get status of all keys
   if keys[K_SPACE]: #The space bar is held down
       global paused #Make global so it can be edited
       if paused==True: #It was paused, so unpause it
           paused=False
       elif paused==False: #It was playing, so pause it
           paused=True

        #Don't let the main loop continue until the space bar has been released again, otherwise the variable will flicker between True and False where the loop runs so fast!
        space_bar_pressed=keys[K_SPACE]
        while space_bar_pressed: #Repeat this loop until space_bar_pressed is False
           keys=pygame.key.get_pressed()
           if not keys[K_SPACE]: #Space bar has been released so set space_bar_pressed to False
              space_bar_pressed=False

however this keeps making my program become unresponsive whenever I try to pause it! Basically, I want the variable "paused" to be either True or False. When the space bar is pressed, it should change to whichever one it isn't currently. Because I'm using check_for_pause() in another never-ending loop, I need to make it so that the function only stops executing when the space bar is released, otherwise if the user holds the space bar for more than a split second it will keep switching between True and False.

Any ideas why my program becomes unresponsive when I run this? I know it's to do with the bit that waits until the space bar's been released because when I remove that bit of the code then my program runs fine (but obviously the pause feature then doesn't work).

回答1:

You have a main loop that probably looks something like this...

while True:
    check_for_pause()
    # Update the game
    # Draw the game

When you check for pause, and the space key is pressed down, paused gets set to True. Then, you have this loop...

space_bar_pressed=keys[K_SPACE]
while space_bar_pressed: #Repeat this loop until space_bar_pressed is False
   keys=pygame.key.get_pressed()
   if not keys[K_SPACE]: #Space bar has been released so set space_bar_pressed to False
      space_bar_pressed=False

The problem with this loop is that you assume that pygame.key.get_pressed() will continue to return up-to-date information. However, looking at the pygame source code, it appears that it uses SDL_GetKeyState, which says as part of its documentation..

Note: Use SDL_PumpEvents to update the state array.

In other words, repeatedly calling pygame.key.get_pressed() will NOT give you the updated key state if you are not additionally calling something like pygame.event.pump(), which actually updates pygame with the new key states. So, you could quickly fix this by introducing that pump function into the loop, as currently it is just running forever.

That being said: DON'T DO THIS. If you do it this way, your game will not be able to do ANYTHING when paused: this includes showing a "paused" screen, continuing to play the music in the background, etc. What you want to do instead is keep track of if the game is paused, and if so, change how the game is updating.

In the part of your game where you update things, some items should only happen if the game is paused. Something like...

paused = False
while True:
    # This function should just return True or False, not have a loop inside of it.
    paused = check_for_paused()

    if not paused:
        # This function moves your enemies, do physics, etc.
        update_game()

    draw_game()

In this case, the main loop will still happen, the game will continue to be drawn, and the input will continue to be handled. However, the enemies and players will not be moving, therefore the game can be said to be "paused".

Finally, there is also the fact that you're relying on get_key_pressed(), which you probably don't want to do. See this other similar answer I've given for reasons why you should be instead using the event queue.



回答2:

You should never stop running your game loop in a game even if it paused. Also pauses are generally handled through events. For example look at this code:

import pygame, sys
from pygame.locals import *
pygame.init()
pygame.display.set_mode((400,400))

paused = False # global

while True:
    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            sys.exit()
        elif event.type == KEYDOWN:
            if event.key == K_SPACE:
                paused = not paused

    if paused:
        continue # skip this iteration if paused

    # add your game code here
    print 'game code running'

In the above code, I toggle paused every time I press spacebar. If you want to pause only while holding spacebar do this:

while True:
    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            sys.exit()
        elif event.type in (KEYDOWN, KEYUP): # checks membership in tuple
            if event.key == K_SPACE:
                paused = not paused

    if paused:
        continue # skip this iteration if paused

    # add your game code here
    print 'game code running'

As a general note, you should always be handling events from your event queue or Pygame will just cry and whine and do nothing (be unresponsive). Thus, never stop spinning on your game loop even if you have paused the game.

EDIT: Alternatively, as abarnert pointed out in the comments you can do a trick with equality comparisons to assure that you never get conflicts between KEYDOWN and KEYUP events:

paused = event.type == KEYDOWN

This way you won't have "syncing problems" where the code accidentally sets paused to True whenever you actually release the spacebar. This can happen if 2 KEYDOWN events happen in a row or if 2 KEYUP events happen in a row (instead of a smooth alternating sequence like KEYDOWN, KEYUP, KEYDOWN, KEYUP, etc.). It's best to not assume that all event queues feed events that are 100 percent accurate.



标签: python pygame