Pygame: how to display an image until a key is pressed - python

I'm trying to build a game in Pygame where if a player moves onto a red square, the player loses. When this happens, I want to display a picture of an explosion where the player lost until the user presses any key on the keyboard. When the user presses a key, I'll call the function new_game() to start a new game. The issue is that my code seems to skip over the line where I blit the explosion, and instead just starts a new game right away.
I've tried using something like this, but I'm not sure what to put in the while loop (I want it to wait until there is a keypress):
while event != KEYDOWN:
# Not sure what to put here
If I put time.sleep() in the while loop, the whole program seems to freeze and no image is blitted.
Here's me loading the image into Pygame:
explosionpic = pygame.image.load('C:/Users/rohan/Desktop/explosion.png')
And here's where I call it/determine if a player has lost (program seems to skip over the screen.blit line because I don't even see the image at all):
if get_color(p1.x + p1_velocity_x, p1.y + p1_velocity_y) == red: # If player lands on a red box
screen.blit(explosionpic, (p1.x, p1.y))
# Bunch of other code goes here, like changing the score, etc.
new_game()
It's supposed to display the image, then when the user presses a key, call the new_game() function.
I'd appreciate any help.

The simplest solution which comes to my mind is to write a small independent function which delay the execution of the code. Something like:
def wait_for_key_press():
wait = True
while wait:
for event in pygame.event.get():
if event.type == KEYDOWN:
wait = False
break
This function will halt the execution until a KEYDOWN signal is catch by the event system.
So your code would be:
if get_color(p1.x + p1_velocity_x, p1.y + p1_velocity_y) == red: # If player lands on a red box
screen.blit(explosionpic, (p1.x, p1.y))
pygame.display.update() #needed to show the effect of the blit
# Bunch of other code goes here, like changing the score, etc.
wait_for_key_press()
new_game()

Add a state to the game, which indicates if the game is running, the explsoion happens or a ne game has to be started. Define the states RUN, EXPLODE and NEWGAME. Initialize the state game_state:
RUN = 1
EXPLODE = 2
NEWGAME = 3
game_state = RUN
If the explosion happens, the set the state EXPLODE
if get_color(p1.x + p1_velocity_x, p1.y + p1_velocity_y) == red: # If player lands on a red box
game_state = EXPLODE
When a key is pressed then switch to the state NEWGAME:
if game_state == EXPLODE and event.type == pygame.KEYDOWN:
game_state = NEWGAME
When newgame() was executed, then set game_state = RUN:
newgame()
game_state = RUN
Implement a separated case in the main loop for each state of the game. With this solution not any "sleep" is needed:
e.g.
ENDGAME = 0
RUN = 1
EXPLODE = 2
NEWGAME = 3
game_state = RUN
while game_state != ENDGAME:
for event in pygame.event.get():
if event.type == pygame.QUIT:
game_state = ENDGAME
if game_state == EXPLODE and event.type == pygame.KEYDOWN:
game_state = NEWGAME
if game_state == RUN:
# [...]
if get_color(p1.x + p1_velocity_x, p1.y + p1_velocity_y) == red: # If player lands on a red box
game_state = EXPLODE
# [...]
elif game_state == EXPLODE:
screen.blit(explosionpic, (p1.x, p1.y))
elif game_state == NEWGAME:
newgame()
game_state = RUN
pygame.display.flip()

Related

How to restart my program with a button press 'r'?

Okay I've created Space Invaders in Python 3.7.4 and Pygame 1.9.6. I've got everything working no bugs or anything. I've even got the paused button to work great. It's just when the screen puts up 'GAME OVER' text to signify you lost. I would like to pop up a window to ask play again after the text goes away. But I just don't know where to begin at. I've looked at How do I restart a program based on user input? for help, but I couldn't understand where to implement it at or where to look at. This was my first real project/game I created in Pygame.
Code:
paused = False
running = True
while running:
# RGB = Red, Green, Blue
screen.fill((0, 0, 0))
# Background Image
screen.blit(background, (0, 0))
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_p: # Pausing
paused = True
if event.key == pygame.K_u: # Unpausing
paused = False
if not paused:
'''The rest of the code, with the movement key presses, etc.'''
Since everything above the 'while running loop' is constantly being loaded or for data retrieval like the images the coordinates for where the images should be, the background music, etc. So I wanted it to be like the pausing/unpausing part of the code when I press down on say 'r'. I want to replay the program without exiting it. So I would be very grateful for help by people. Thank you all.
You have to create function or methods in object to reset data and use it before every game. You can use external while-loop for this.
# executed only once
pygame.init()
screen = ...
# .. load data ...
player = ...
enemy = ...
gameing = True
while gameing:
# reset data before every game
player.reset()
enemy.reset()
score = 0
paused = False
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT: # exit game
running = False
gameing = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_r: # restart game (but don't exit)
running = False
#gameing = True
if event.key == pygame.K_x: # exit game and program
running = False
gameing = False
if game_over: # restart game (but don't exit)
running = False
#gameing = True
You can also organize it as #Starbuck5 mentioned in commen - but then you should use running to exit program but not to exit game on game over
def reset():
global score, paused, running
player.reset()
enemy.reset()
score = 0
paused = False
running = True
# executed only once
pygame.init()
screen = ...
# .. load data ...
player = ...
enemy = ...
# (re)set data before first game
reset()
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT: # exit game
running = False
gameing = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_r: # restart game (but don't exit)
#running = False # DON'T DO THIS
reset()
if event.key == pygame.K_x: # exit game and program
running = False
if game_over: # restart game (but don't exit)
#running = False # DON'T DO THIS
reset()
This need use global for every variable which you have to reset so it can be more useful when you have code in classes and you can use self. instead of global

How to edit a variable in a different file from where it was declared?

I'm currently working on the Alien Invasion project within the book Python Crash Course. The issue is that the picture of my ship is not moving whenever I press the right arrow key (I removed the code for the left arrow key temporarily).
I know that my function to check for key-press events works as it prints out the expected values whenever the key is pressed and when it's not.
When it's being held down, the flag movingRight is changed from its default state of false to true. What should happen then is the variable 'center' is changed by adding a predetermined movement factor to it, then using that to print the ship image in its new position.
However, when I print the state of movingRight in the ship.py file, it shows that movingRight is still False even during a KEY_DOWN event.
Here is the loop that handles the events and changing the ship state. This is within the main file called alien_invasion.py:
#Create a new Ship
ship = Ship(aiSettings, screen)
#start the main loop for the game
while True:
#Check for events that affect the ship
gf.checkEvents(ship)
#Update the status of the ship based upon checkEvents()
ship.update()
#Update the screen after pulling data from ship.update()
gf.updateScreen(aiSettings, screen, ship)
Below is the checkEvents() function in a separate file called game_functions.py:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
print("Moving Right")
ship.moveRight = True
elif event.type == pygame.KEYUP:
if event.key == pygame.K_RIGHT:
print("Done moving right")
ship.moveRight = False
Lastly, below is the code that edits the position of the ship. This is within ship.py:
def update(self):
print("movingRight state is: " + self.movingRight) #movingRight is false during KEY_DOWN event
#Check for a right move
if self.movingRight:
#Update the ship's center to the right x amount of pixels
self.center += self.aiSettings.shipSpeedFactor
self.rect.centerx = self.center
I understand that this book is a few years old now, so could it be that the method I'm using is outdated?
From what I can see in your code, it looks like you're using different names for the moving right variables. In your second code snip, you set ship.moveRight to True, but in your third code snip, you're checking if self.movingRight is True. Try changing it so both use .movingRight or both use .moveRight

A good way to switch to a menu using PyGame

I am using PyGame to create a game, and can't figure a clean way to switch between scenes and menus. I don't know if I need to create a scene class for each thing with its own update and render loop or just use while statements and functions (which I don't like that much as it is a little messy for me) It doesn't matter about my code, just the way to switch between a menu and the main game.
There are different strategies and it depends on what functionality you want. One simple way is to have each scene in a function that returns what the next scene should be. Then create a simple if-statement that switches between them in the main game loop.
import pygame
pygame.init()
SCENE_MENU = 0
SCENE_GAME = 1
SCENE_SHOP = 2
def menu(screen):
# Initialize game variables as the player, enemies and such.
fps_cap = 30
options = ['continue', 'save', 'quit']
clock = pygame.time.Clock()
# Game loop.
while True:
# Time management.
clock.tick(fps_cap)
# Handle events.
for event in pygame.event.get():
if event.type == pygame.QUIT:
quit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_a: # Go to game if you press A.
return SCENE_GAME
elif event.key == pygame.K_b: # Go to shop if you press B.
return SCENE_SHOP
# Update the menu (like buttons, settings, ...).
print('Updating buttons:', *options)
# Draw the shop.
screen.fill((0, 0, 255)) # A green menu.
pygame.display.update()
def game(screen):
# Initialize game variables as the player, enemies and such.
fps_cap = 60
player = 'Ted'
clock = pygame.time.Clock()
# Game loop.
while True:
# Time management.
clock.tick(fps_cap)
# Handle events.
for event in pygame.event.get():
if event.type == pygame.QUIT:
quit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_a: # Go to menu if you press A.
return SCENE_MENU
elif event.key == pygame.K_b: # Go to shop if you press B.
return SCENE_SHOP
# Update the game.
print(f'Player {player} is playing!')
# Draw your game.
screen.fill((0, 255, 0)) # A blue game.
pygame.display.update()
def shop(screen):
# Initialize game variables as the player, enemies and such.
fps_cap = 30
items = ['sword', 'armor', 'potion']
clock = pygame.time.Clock()
# Game loop.
while True:
# Time management.
clock.tick(fps_cap)
# Handle events.
for event in pygame.event.get():
if event.type == pygame.QUIT:
quit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_a: # Go to game if you press A.
return SCENE_GAME
elif event.key == pygame.K_b: # Go to shop if you press B.
return SCENE_SHOP
# Update the shop (like buttons, money transfers, ...).
print('Looking at items:', *items)
# Draw the shop.
screen.fill((255, 0, 0)) # A red shop.
pygame.display.update()
def main():
screen = pygame.display.set_mode((100, 100))
scene = SCENE_MENU
while True:
if scene == SCENE_MENU:
scene = menu(screen)
elif scene == SCENE_SHOP:
scene = shop(screen)
elif scene == SCENE_GAME:
scene = game(screen)
main()
This is just a simple example, but should show the principle.

MOUSEBUTTON events in pygame

I have made a sort of cookie clicker like game in pygame, however when I click the mouse once loads of points are added to the score. I assume this is because of the game loop, however I would like to know how to make this stop, and make it add 1 to the score for every click, no matter how long the mouse button is held down.
Here's an example that only increments the score on mouse button down events:
import pygame
pygame.init()
pygame.font.init()
clock = pygame.time.Clock()
screen = pygame.display.set_mode([320,240])
sys_font = pygame.font.SysFont(pygame.font.get_default_font(), 18)
pygame.display.set_caption("Clicker")
clicks = 0 # initialise the score counter
done = False
while not done:
#Events
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
elif event.type == pygame.MOUSEBUTTONDOWN:
clicks += 1
#Graphics
screen.fill(pygame.color.Color("white"))
score_txt = sys_font.render(f"Clicks: {clicks}", True, pygame.color.Color("blue"))
screen.blit(score_txt, (20, 220))
#Frame Change
pygame.display.update()
clock.tick(60)
pygame.quit()
Use 2 variables like is_mouse_clicked and was_mouse_clicked_previously.
In the initialization of the game (even before the first loop), assign False to both of them.
At the beginning of the game loop assign value of is_mouse_clicked to was_mouse_clicked_previously
Then, load the information whether the mouse button is being pressed to is_mouse_clicked variable
Then, add the point if the values of is_mouse_clicked and was_mouse_clicked_previously differ.
Option 1: Adding points inside if is_mouse_clicked and not was_mouse_clicked_previously: will increase the score right away (the moment you start pressing the button)
Option 2: Adding points inside if not is_mouse_clicked and was_mouse_clicked_previously: will increase the score a bit later (the moment you release the button)

pygame mouse button down not working

I am making this multiple choice program, but I need the mouse event to only work after enter is pressed.
Here is my code:
for event in pygame.event.get(): # If user did something
if event.type == pygame.QUIT: # If user clicked close
done = True
elif event.type == pygame.KEYDOWN: # If user pressed a key
if event.key == pygame.K_RETURN: # If user pressed enter
# Makes the start screen go away
enter_pressed = True
# Increments question_number
question_number += 1
# Where I Draw the question screen
Then down below I have this:
for event in pygame.event.get(): # If user did something
if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
print("Derp")
Derp won't print when I press left mouse button.
However, when I have it indented like this:
for event in pygame.event.get(): # If user did something
if event.type == pygame.QUIT: # If user clicked close
done = True
elif event.type == pygame.KEYDOWN: # If user pressed a key
if event.key == pygame.K_RETURN: # If user pressed enter
# Makes the start screen go away
enter_pressed = True
# Increments question_number
question_number += 1
# Where I Draw the question screen
elif event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
print("Derp")
Derp does print when I press left mouse button
You could use a boolean indicating wheter or not enter was pressed.
for event in pygame.event.get(): # If user did something
enter_pressed = False
if event.type == pygame.QUIT: # If user clicked close
done = True
elif event.type == pygame.KEYDOWN: # If user pressed a key
if event.key == pygame.K_RETURN: # If user pressed enter
# Makes the start screen go away
enter_pressed = True
# Increments question_number
question_number += 1
# Where I Draw the question screen
elif event.type == pygame.MOUSEBUTTONDOWN and event.button == 1 and enter_pressed:
print("Derp")
I think that the problem is that you are iterating over all events, and inside that loop you are iterating over all events (again), and that causes some problems
It is hard to tell from what you said, but I have a couple of recommendations for you. First, make sure that the for loop with derp isn't in any if statements you don't want it to be in. Second, make sure you call pygame.event.get() once per game loop or the code will only give you events that happened between the two calls, which will not be all of them. If neither of these things work, try posting the whole code.

Categories