I'm just adding some finishing touches to my tank game, but there's this slight thing that's been bothering me. It's just that, at the menu screen, when the user presses a different set of keys to access different things such as the instruction screen, game screen, or custom username input screen, it takes two presses of the button to respond and not the desired one single press. Here's the little excerpt of code:
menu = True
instruct = False
run = False
name1 = False
name2 = False
while menu:
pygame.event.get()
theKey = pygame.key.get_pressed()
if theKey[pygame.K_RETURN]:
menu = False
run = False
instruct = True
name1 = False
if theKey[pygame.K_LSHIFT] or theKey[pygame.K_RSHIFT]:
menu = False
run = True
begin = time.time()
if theKey[pygame.K_BACKSPACE]:
menu = False
run = False
instruct = False
name1 = True
user_input = ''
menu_screen()
FONT = pygame.font.Font(None, 40) # A font object which allows you to render text.
BG_COLOR = pygame.Color('gray12')
BLUE = pygame.Color('dodgerblue1')
USEFONT = pygame.font.Font(None, 70)
yourText1 = "Player 1, Enter Your Name: "
yourText2 = "Player 2, Enter Your Name: "
userNamePrompt = USEFONT.render(yourText1, True, BLUE)
userNamePrompt2 = USEFONT.render(yourText2, True, BLUE)
while name1:
theKey = pygame.key.get_pressed()
for event in pygame.event.get():
if event.type == pygame.QUIT:
name1 = True
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_BACKSPACE:
user_input = user_input[:-1]
elif event.key == pygame.K_RETURN:
name1 = False
name2 = True
user2_input = ''
else:
user_input += event.unicode
screen.fill(BG_COLOR)
# Create the text surface.
text = FONT.render(user_input, True, BLUE)
# And blit it onto the screen.
screen.blit(userNamePrompt, (20,20))
screen.blit(text, (20, 300))
pygame.display.flip()
clock.tick(30)
while name2:
theKey = pygame.key.get_pressed()
for event in pygame.event.get():
if event.type == pygame.QUIT:
name1 = True
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_BACKSPACE:
user2_input = user2_input[:-1]
elif event.key == pygame.K_RETURN:
name2 = False
run = True
begin = time.time()
else:
user2_input += event.unicode
screen.fill(BG_COLOR)
# Create the text surface.
text = FONT.render(user2_input, True, BLUE)
# And blit it onto the screen.
screen.blit(userNamePrompt2, (20,20))
screen.blit(text, (20, 300))
pygame.display.flip()
clock.tick(30)
playName1 = user_input
playName2 = user2_input
while instruct:
pygame.event.get()
theKey = pygame.key.get_pressed()
if theKey[pygame.K_BACKSPACE]:
instruct = False
run = False
menu = True
if theKey[pygame.K_LSHIFT] or theKey[pygame.K_RSHIFT]:
instruct = False
run = True
begin = time.time()
instruct_screen()
In this case, the code takes two taps of the "return" or enter button to get the instruction screen and two taps of the shift key to show the game screen - and I've tried to fix it, but to no avail. Does anyone know why this may be occurring and what/how the code needs to be modified in order for it to work as desired?
It's because you're calling pygame.key.get_pressed() twice, once in the menu loop and once in the name1 loop. Each time you call it, it's popping the keypress event off the stack, so the second time it will wait for a second keypress. You should store the result the first time, and use that in the second loop.
Related
The intention of the function is to ask the user for an input between two options to change a variable. When the function is called pygame opens but it just displays black and crashes. Without the function the code works fine.
def pick_board():
global bo
global bo1
global bo2
while 1:
question = font.render('Which board would you like to solve? \n\n 1 or 2:', True, pg.Color('black'))
screen.fill('white')
screen.blit(question, (20, 20))
events = pg.event.get()
for event in events:
if event.type == pg.KEYDOWN:
if event.key == pg.K_1:
bo = bo1
break
if event.key == pg.K_2:
bo = bo2
break
The break statement in your code breaks the for-loop (event loop), but not the while-loop (input loop). Add a wait_for_input state variable and change the state when the key is pressed.
Additionally you have to update the display by calling either pygame.display.update() or pygame.display.flip().
def pick_board():
global bo
global bo1
global bo2
clock = pygame.time.Clock()
question = font.render('Which board would you like to solve? \n\n 1 or 2:', True, pg.Color('black'))
wait_for_input = True
while wait_for_input:
clock.tick(100)
events = pg.event.get()
for event in events:
if event.type == pg.KEYDOWN:
if event.key == pg.K_1:
bo = bo1
wait_for_input = False
if event.key == pg.K_2:
bo = bo2
wait_for_input = False
screen.fill('white')
screen.blit(question, (20, 20))
pygame.display.flip()
I want the code to display text for an options screen on my computer science project. The problem is that when I change to the options screen the text is not visible. I have found that switching quickly between the screens shows it briefly sometimes. I have put code for the text to display in 2 places either when you click on the settings in the top right of the main screen or when you press enter on the keypad.
import pygame
import sys
import time
from pygame.locals import *
displaytext = False
fadedout = False
timechange = 0
played = False
musicUp = pygame.K_o
musicDown = pygame.K_l
pygame.init() # initialize pygame
pygame.font.init()
myfont = pygame.font.SysFont('Comic Sans MS', 30)
pygame.mixer.music.set_volume(0.50)
clock = pygame.time.Clock()
screen = pygame.display.set_mode((1600,800))
pygame.mouse.set_cursor(*pygame.cursors.tri_left)
currentBack = 'welcome'
bg = pygame.image.load("welcome1600new.jpg")
def callText():
textsurface = myfont.render('Some Text', True, (10, 10, 10))
textpos = textsurface.get_rect()
screen.blit(textsurface,textpos)
def options():
pausePos = play_time()/1000
pygame.mouse.set_cursor(*pygame.cursors.arrow)
bg = pygame.image.load("Optionsback.jpg")
displaytext = True
return bg,displaytext
def play_time():
playTime = pygame.mixer.music.get_pos()
return playTime
while True:
clock.tick(60)
screen.blit(bg, (0,0))
pygame.display.update()
for event in pygame.event.get():
pygame.mixer.init()
if (pygame.mixer.music.get_busy() == False) and (fadedout == False):
pygame.mixer.music.load("dududududu.ogg")
pygame.mixer.music.play(-1,0.0)
if (displaytext) == True:
textsurface = myfont.render('Some Text', 1, (10, 10, 10))
textpos = textsurface.get_rect()
screen.blit(textsurface,textpos)
if ((currentBack == 'welcome') and (event.type ==
pygame.MOUSEBUTTONUP) and (pygame.mouse.get_pos()[0] >= 1540) and
(pygame.mouse.get_pos()[0] <= 1600) and (pygame.mouse.get_pos()[1] >= 0) and
(pygame.mouse.get_pos()[1] <= 70)):
currentBack = 'options'
bg, displaytext = options()
if event.type == KEYDOWN:
if ((event.key == pygame.K_h) and (currentBack == 'welcome')):
pausePos = play_time()/1000
pygame.mouse.set_cursor(*pygame.cursors.arrow)
bg = pygame.image.load("Help1600.jpg")
currentBack = 'help'
#pygame.mixer.pause()
pygame.mixer.music.fadeout(1000)
pygame.display.update()
fadedout = True
displaytext = False
if event.key == musicUp:
if (pygame.mixer.music.get_volume()<=0.90) and
timechange+0.25<time.time():
timechange = time.time()
pygame.mixer.music.set_volume(pygame.mixer.music.get_volume()+.10)
pygame.display.update()
displaytext = False
if event.key == musicDown:
if pygame.mixer.music.get_volume()>=.10 and
timechange+0.25<time.time():
timechange = time.time()
pygame.mixer.music.set_volume(pygame.mixer.music.get_volume()-.10)
pygame.display.update()
displaytext = False
if (event.key == pygame.K_ESCAPE):
pygame.mouse.set_cursor(*pygame.cursors.tri_left)
bg = pygame.image.load("welcome1600new.jpg")
currentBack = 'welcome'
pygame.mixer.music.play(-1, pausePos)
pygame.display.update()
fadedout = False
displaytext = False
if event.key == (pygame.K_KP_ENTER):
callText()
bg, displaytext = options()
currentBack = 'options'
There are several strange things there.
What causes the specific behavior you are complaining about is that you only draw your text inside the for loop that checks for events. If there are no events, such as keypresses or mouse movement in a frame, the block that blits your text is not run at all.
Another thing that might bring you closer to a working thing, is to make one and only single call to pygame.display.update() or the equivalent .flip() in each frame.
And work the code around it so that it makes sense. Each time you call ...display.update() above, you had redrawn the whole screen. And whatever text was there will be gone.
The for event in pygame.event.get() loop is not a magic entity from outer space. It is simply a way for you to check for the pygame events that happen at each frame.
Every single thing in there that is not testing the event variable have to go out.
Also pygame.mixer.init() should be called one single time, at the beginning of the program - while you put it being called multiple times each frame. If you have to reset the mixer, to restart the music, check the docs for another call to do that.
All in all, more or less the code bellow. I did not try to run it, but I just unmangled some of the stuff you have around.
import pygame
import sys
import time
from pygame.locals import *
def init():
global clock, screen
pygame.init() # initialize pygame
screen = pygame.display.set_mode((1600,800))
pygame.font.init()
pygame.mixer.init()
pygame.mixer.music.set_volume(0.50)
pygame.mouse.set_cursor(*pygame.cursors.tri_left)
clock = pygame.time.Clock()
# put stuff inside a function.
def main():
myfont = pygame.font.SysFont('Comic Sans MS', 30)
displaytext = False
fadedout = False
timechange = 0
played = False
musicUp = pygame.K_o
musicDown = pygame.K_l
currentBack = 'welcome'
bg_image = pygame.image.load("Help1600.jpg")
bg = img_welcome = pygame.image.load("welcome1600new.jpg")
while True:
screen.blit(bg, (0,0))
if (pygame.mixer.music.get_busy() == False) and (fadedout == False):
pygame.mixer.music.load("dududududu.ogg")
pygame.mixer.music.play(-1,0.0)
if (displaytext) == True:
textsurface = myfont.render('Some Text', 1, (10, 10, 10))
textpos = textsurface.get_rect()
screen.blit(textsurface,textpos)
# Use a rectangle and Rect.collidepoint instead of this mess:
# https://www.pygame.org/docs/ref/rect.html#pygame.Rect.collidepoint
if ((currentBack == 'welcome') and
(event.type == pygame.MOUSEBUTTONUP) and
(pygame.mouse.get_pos()[0] >= 1540) and
(pygame.mouse.get_pos()[0] <= 1600) and
(pygame.mouse.get_pos()[1] >= 0) and
(pygame.mouse.get_pos()[1] <= 70)
):
currentBack = 'options'
bg, displaytext = options()
for event in pygame.event.get():
# just check for _events_ inside here.
if event.type == KEYDOWN:
if ((event.key == pygame.K_h) and (currentBack == 'welcome')):
pausePos = play_time()/1000
pygame.mouse.set_cursor(*pygame.cursors.arrow)
# DOn't load images with pygame.image.load inside the game loop!!
bg = bg_image
currentBack = 'help'
#pygame.mixer.pause()
pygame.mixer.music.fadeout(1000)
fadedout = True
displaytext = False
if event.key == musicUp:
if (pygame.mixer.music.get_volume()<=0.90) and timechange+0.25<time.time():
timechange = time.time()
pygame.mixer.music.set_volume(pygame.mixer.music.get_volume()+.10)
displaytext = False
if event.key == musicDown:
if pygame.mixer.music.get_volume()>=.10 and timechange+0.25<time.time():
timechange = time.time()
pygame.mixer.music.set_volume(pygame.mixer.music.get_volume()-.10)
displaytext = False
if (event.key == pygame.K_ESCAPE):
pygame.mouse.set_cursor(*pygame.cursors.tri_left)
bg = img_welcome
currentBack = 'welcome'
pygame.mixer.music.play(-1, pausePos)
fadedout = False
displaytext = False
if event.key == (pygame.K_KP_ENTER):
callText()
bg, displaytext = options()
currentBack = 'options'
# single call to update:
pygame.display.update()
# after you display the image, you can pause
# until the next frame:
clock.tick(60)
init()
main()
This question already has answers here:
Pygame level/menu states
(2 answers)
Closed 4 years ago.
I have a GAME OVER screen that is functioning when your health is depleted. This GAME OVER screen is executed with the show_go_screen. I tried to just duplicate the method for the show_go_screen into my show_winning() function for the other screen. I rigged this show winning function by setting your health back to normal when going back to the game. However once you have gone to the YOU WIN screen and back to the game you cannot go to the YOU WIN screen again. I have included the code for my player shield below (which determine when there is a GAME OVER/YOUWIN event) I have also included the methods for the show_go_screen (which is for GAME OVER) and the code for show_winning() method (which is for the YOU WIN screen). If someone can tell me how to best modify my code to get the desired result.
if TrumpHits:
self.trump.shield -= 25
if self.trump.shield <= 0:
self.show_winning()
self.winning = True
if hits:
self.player.shield -= 20
if flyby:
self.player.shield -= 30
if self.player.shield <= 0:
self.playing = False
here are the show_go_screen() and the show_winning() methods
def show_go_screen(self):
# game over/continue
if not self.running:
return
bg = pg.image.load("GAMEOVERslimeCOVERAGE.png")
self.screen.blit(bg,(0,0))
self.draw_text("Press space bar to play again", 22, WHITE, WIDTH / 2, HEIGHT * 7 / 8)
HEIGHT / 2 + 40)
pg.display.flip()
waiting = True
while waiting:
self.clock.tick(FPS)
for event in pg.event.get():
if event.type == pg.QUIT:
waiting = False
self.running = False
if event.type == pg.KEYDOWN:
if event.key == pg.K_SPACE:
waiting = False
self.PlayMusic()
def show_winning(self):
# game over/continue
if self.winning:
return
bg = pg.image.load("TRUMPyouwin3d.png")
self.screen.blit(bg,(0,0))
self.draw_text("Press any key to play again", 22, WHITE, WIDTH / 2, HEIGHT * 7 / 8)
pg.display.flip()
waiting = True
while waiting:
self.clock.tick(FPS)
for event in pg.event.get():
if event.type == pg.QUIT:
waiting = False
self.running = False
if event.type == pg.KEYDOWN:
if event.key == pg.K_SPACE:
waiting = False
self.PlayMusic()
self.player.shield = 100
self.trump.shield = 100
def wait_for_key(self):
waiting = True
while waiting:
self.clock.tick(FPS)
for event in pg.event.get():
if event.type == pg.QUIT:
waiting = False
self.running = False
if event.type == pg.KEYUP:
waiting = False
If the player or the enemy have less than zero health, set self.playing = False which stops the game's while loop. Then the show_go_screen method of the game instance g is called in the outermost while loop.
while g.running:
g.new()
g.show_go_screen()
In the show_go_screen method, blit the background image and the text (I created the BACKGROUND_IMG and the FONT object globally), flip the display and start another while loop with an event loop in which you check if the user wants to quit or continue. If the user quits, you set self.running to False so that the outer while loop will stop as well, because its condition is while g.running:. If space is pressed, you can just return from this show_go_screen method and the outer while loop calls g.new() what resets the game.
def show_go_screen(self):
self.screen.blit(BACKGROUND_IMG, (0, 0))
text_surface = FONT.render("Press space bar to play again", True, WHITE)
self.screen.blit(text_surface, (WIDTH / 2, HEIGHT * 7 / 8))
pg.display.flip()
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
self.running = False
done = True
if event.type == pg.KEYDOWN:
if event.key == pg.K_SPACE:
done = True
self.clock.tick(FPS)
If you want to make more complex scenes/states, I recommend to use a finite state machine like this. It's actually just a scene manager class which allows you to switch between the different scenes/states of the game.
I'm having some trouble getting a reaction from pressing the D key in my program. I will show and then explain. (Irrelevant things omitted)
Main.py
while True:
process(Cursor,movie,music)
Effects.List.draw(screen)
pygame.display.flip()
Classes.py
class BaseClass(pygame.sprite.Sprite):
allsprites = pygame.sprite.Group()
def __init__(self,x,y,image_string):
pygame.sprite.Sprite.__init__(self)
BaseClass.allsprites.add(self)
self.image = pygame.image.load(image_string)
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
def destroy(self, ClassName):
ClassName.List.remove(self)
BaseClass.allsprites.remove(self)
del self
class Effects(BaseClass):
List = pygame.sprite.Group()
def __init__(self,x,y,image_string):
BaseClass.__init__(self,x,y,image_string)
Effects.List.add(self)
Process.py
def process(Cursor,movie,music):
for event in pygame.event.get():
Stage = True
Stage2 = False
keys = pygame.key.get_pressed()
if Stage:
if Cursor.rect.collidepoint(370,340): #Start
if keys[pygame.K_RETURN]:
Stage2 = True
if Stage2:
Stage = False
hitkeys = HitKeys(65,600,"Images/Hit Keys.png") #520
lane = KeyLane(50,0,"Images/4k lane.png")
movie.play()
pygame.mixer.music.play()
if keys[pygame.K_d]:
effect1 = Effects(55,0,"Images/Effect.png")
I am not experienced in programming so my methods to achieve what I want are very roundabout. As you can see, I want the Effect.png to appear when I press D but to only appear during Stage2. It just doesn't happen. It would work if I dedented it one part but that would mean it would appear during Stage. Not what I want.
Would appreciate if you could help me determine why it isn't showing up in Stage2.
In for event in pygame.event.get(): you set
Stage = True
Stage2 = False
so every time you run process() you go to Stage (Stage = True)
You have to set
Stage = True
Stage2 = False
at the beginning of game (before while True:)
BTW: you will have to use Stage and Stage2 in
process(Cursor,movie,music, Stage, Stage2)
Maybe better use one Stage and assign stage number 1, 2
BTW: there are python rules how to name functions and variables (PEP8) - use lowercase (and _) for variable names (stage, stage1, all_sprites) and use upper letter in class names. Event Stackover use that rules and it use light blue color for class names.
EDIT:
I made script to test keyboard. Check what you get.
keys = pygame.key.get_pressed() works but only when screen/window exists.
import pygame
import pygame.locals
pygame.init()
screen = pygame.display.set_mode((800,600))
d_pressed = False
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
running = False
elif event.key == pygame.K_d:
d_pressed = True
elif event.type == pygame.KEYUP:
if event.key == pygame.K_d:
d_pressed = False
keys = pygame.key.get_pressed()
if d_pressed:
print '> d_pressed <'
if keys[pygame.K_RETURN]:
print '> K_RETURN <'
if keys[pygame.K_d]:
print '> K_d <'
EDIT: sending stages as list - not (separated) values
stages = [True, False]
while True:
process(Cursor, movie, music, stages)
Effects.List.draw(screen)
pygame.display.flip()
-
while True
def process(Cursor,movie,music, stages):
for event in pygame.event.get():
keys = pygame.key.get_pressed()
if stages[0]:
if Cursor.rect.collidepoint(370,340): #Start
if keys[pygame.K_RETURN]:
stages[0] = False
stages[1] = True
if stage[1]:
hitkeys = HitKeys(65,600,"Images/Hit Keys.png") #520
lane = KeyLane(50,0,"Images/4k lane.png")
movie.play()
pygame.mixer.music.play()
if keys[pygame.K_d]:
effect1 = Effects(55,0,"Images/Effect.png")
And I don't know why??? Everything else seems to be working just fine.. but when you click the wrong "button" in the game the error sounds works and it resets the pattern, but the lives that I have put up (which is 3) doesn't go down at all. Wondering if anyone could help me out with this please? (I won't be pasting my entire code, but this is where the command happens and all). Also if you couldn't tell, I'm creating a memory game. If anyone could lead me into the right direction then that could be a big help for me. Thanks in advance!
def main():
global FPSCLOCK, DISPLAYSURF, BASICFONT, BEEP1, BEEP2, BEEP3, BEEP4
pygame.init()
FPSCLOCK = pygame.time.Clock()
DISPLAYSURF = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption('Simulate')
# font
BASICFONT = pygame.font.Font(None, 30)
# load the sound files
BEEP1 = pygame.mixer.Sound('beep1.wav')
BEEP2 = pygame.mixer.Sound('beep2.wav')
BEEP3 = pygame.mixer.Sound('beep3.wav')
BEEP4 = pygame.mixer.Sound('beep4.wav')
SOUNDTRACK = pygame.mixer.Sound('soundtrack.wav')
ERROR = pygame.mixer.Sound('error.wav')
# initialize some variables for a new game
pattern = [] # stores the pattern of colors
currentStep = 0 # the color the player must push next
lastClickTime = 0 # timestamp of the player's last button push
score = 0
# plays the soundtrack music
SOUNDTRACK.play(-1, 0, 1000)
# start-up screen
text = BASICFONT.render('Press enter to play!', 1, WHITE)
textRect = text.get_rect()
textRect.centerx = DISPLAYSURF.get_rect().centerx
textRect.y = 150
DISPLAYSURF.blit(text, textRect)
# update the screen
pygame.display.update()
# the "press enter" command
waiting = True
while waiting:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_RETURN:
waiting = False
# when False, the pattern is playing. when True, waiting for the player to click a colored button:
waitingForInput = False
while True: # main game loop
clickedButton = None # button that was clicked (set to YELLOW, RED, GREEN, or BLUE)
DISPLAYSURF.fill(bgColor)
drawButtons()
# amount of lives
lives = 3
img = BASICFONT.render('I'*lives, 1, WHITE)
livesRect = img.get_rect()
livesRect.topleft = (10, 10)
DISPLAYSURF.blit(img, livesRect)
scoreSurf = BASICFONT.render('Score: ' + str(score), 1, WHITE)
scoreRect = scoreSurf.get_rect()
scoreRect.topleft = (WIDTH - 100, 10)
DISPLAYSURF.blit(scoreSurf, scoreRect)
checkForQuit()
for event in pygame.event.get(): # event handling loop
if event.type == MOUSEBUTTONUP:
mousex, mousey = event.pos
clickedButton = getButtonClicked(mousex, mousey)
if not waitingForInput:
# play the pattern
pygame.display.update()
pygame.time.wait(1000)
pattern.append(random.choice((YELLOW, BLUE, RED, GREEN)))
for button in pattern:
flashButtonAnimation(button)
pygame.time.wait(FLASHDELAY)
waitingForInput = True
else:
# wait for the player to enter buttons
if clickedButton and clickedButton == pattern[currentStep]:
# pushed the correct button
flashButtonAnimation(clickedButton)
currentStep += 1
lastClickTime = time.time()
if currentStep == len(pattern):
# pushed the last button in the pattern
score += 1
waitingForInput = False
currentStep = 0 # reset back to first step
elif (clickedButton and clickedButton != pattern[currentStep]) or (currentStep != 0 and time.time() - TIMEOUT > lastClickTime):
# pushed the incorrect button, or has timed out
pattern = []
currentStep = 0
waitingForInput = False
lives = lives - 1
SOUNDTRACK.stop()
ERROR.play()
pygame.time.wait(1000)
SOUNDTRACK.play(-1, 0, 1000)
pygame.display.update()
if lives < 1:
gameOverAnimation()
# reset the variables for a new game:
pattern = []
currentStep = 0
waitingForInput = False
score = 0
pygame.time.wait(1000)
pygame.display.update()
FPSCLOCK.tick(FPS)
You set lives = 3 inside your main loop, instead of before it.
This means that you reset it to 3 on every iteration of the loop.