Not detecting key press in pygame - python

I am currently working on a game where I wish to give the player an option of four characters to play. Here is my current code to do this:
running = 1
charactersChoice = ['char.png', 'char2.png', 'char3.png', 'char4.png']
choice = ''
while choice == '':
screen.fill((47, 79, 79))
screen.blit(pygame.image.load(charactersChoice[0]), (100,100))
screen.blit(pygame.image.load(charactersChoice[1]), (700,100))
screen.blit(pygame.image.load(charactersChoice[2]), (100,600))
screen.blit(pygame.image.load(charactersChoice[3]), (700,600))
keys = pygame.key.get_pressed()
#Choose character
if keys[pygame.K_1]:
choice = charactersChoice[0]
if keys[pygame.K_2]:
choice = charactersChoice[1]
if keys[pygame.K_3]:
choice = charactersChoice[2]
if keys[pygame.K_4]:
choice = charactersChoice[3]
pygame.display.flip()
while running == 1:
#rest of code for game here
As you can see, I blit the four different character profiles onto the screen and then check if the player has pressed the keys 1-4 to select their option. After selecting their option, it should move onto the main loop. Currently, if I press the key '1' while the code is running, it will not change the variable choice to what is intended.
Hope I have explained my problem well enough.

You have to call pygame.event.pump() at least once before using pygame.key.get_pressed() so that pygame can check what keys have been pressed.

Related

Some inputs are being missed in pygame.event.get()

NVM FIXED: It was my computer palm rejection that prevented a left click from being registered when I was clicking other keyboard keys. So check that!
ORIGINAL QUESTION:
I am calling pygame.event.get() in my core game loop once per frame, and it seems to be get my WASD inputs just fine with almost zero delay, but left mouse click inputs are often missed. It almost seems like all other inputs except WASD feel very delayed?
Here is the relevant code:
def processInput(self):
events = pygame.event.get()
if len(events) != 0:
print(events)
for event in events:
match event.type:
case pygame.QUIT:
self.running = False
break
case pygame.KEYDOWN:
self.inputs.append(event.key)
case pygame.KEYUP:
self.inputs.remove(event.key)
case pygame.MOUSEBUTTONDOWN:
print("button down!")
b = event.button
a = -1
match(b):
case 1:
a = pygame.BUTTON_LEFT
case 2:
a = pygame.BUTTON_MIDDLE
case 3:
a = pygame.BUTTON_RIGHT
case _:
pass
self.inputs.append(a)
case pygame.MOUSEBUTTONUP:
b = event.button
r = -1
match(b):
case 1:
r = pygame.BUTTON_LEFT
case 2:
r = pygame.BUTTON_MIDDLE
case 3:
r = pygame.BUTTON_RIGHT
case _:
pass
self.inputs.remove(r)
case _:
pass
The print statement in the fourth case is not being printed, so I know it's not with my input processing later down the loop. Any help or insight would be appreciated!
I've tried rebinding the action to another keyboard key instead of left click and it feels delayed too. Only the WASD keys seem to be working with no problems. So it does not seem like an issue with MOUSEBUTTONDOWN specifically with pygame, but then I have no idea what it is then.
It was my computer palm rejection that prevented a left click from being registered when I was clicking other keyboard keys. So check that!

Window freeze for single thread in while loop of Pygame

I am writing a short program to display cards in a round. I suspect that it is the length of the code which prevents the final 'OK' submit on P3 (the last player's submission) from executing properly: at which point the program sometimes will evaluate the winner and clear the round, but most of the time instead will freeze.
I have tried clock.tick(low fps), pygame.event.pump(), and pygame.event.clear(). Any leads would be much appreciated.
# Round loop begins. Finish until all hands are empty.
while not self.game.get_is_last_round():
player = self.game.get_player(self.game.get_player_turn())
hand = player.order_hand(player.get_hand(),
self.game.get_round_level(),
self.game.get_round_trump_suit())
ok_clicked_2 = False
pygame.event.pump()
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.deal_running = False
self.is_running = False
pygame.display.quit()
pygame.quit()
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN:
play = player.get_play()
click = pygame.mouse.get_pressed(num_buttons=3)
pos = pygame.mouse.get_pos()
# Used DeMorgan's law to resolve error
ok_clicked_2 = (OK1_X < pos[0] < OK1_X + B_W) and (OK1_Y < pos[1] < OK1_Y + B_H) and click[0]
b1, card = self.check_hand(pos, player)
b2, play_card = self.check_play(pos, player)
if b1:
hand.remove(card)
play.append(card)
player.set_play(
player.order_hand(play, self.game.get_round_level(),
self.game.get_round_trump_suit()))
player.set_hand(
player.order_hand(hand, self.game.get_round_level(),
self.game.get_round_trump_suit()))
if b2:
play.remove(play_card)
hand.append(play_card)
player.set_play(
player.order_hand(play, self.game.get_round_level(),
self.game.get_round_trump_suit()))
player.set_hand(player.order_hand(hand, self.game.get_round_level(),
self.game.get_round_trump_suit()))
clock.tick(100)
surface.blit(background, (0, 0))
if len(self.game.get_player(0).get_hand()) == 25:
self.game.set_is_first_round(True)
else:
self.game.set_is_first_round(False)
if len(self.game.get_player(0).get_hand()) == 0:
self.game.set_is_last_round(True)
else:
self.game.set_is_last_round(False)
if self.game.get_play_in_turn() != NUM_PLAYERS:
pygame.event.pump()
clock.tick(100)
if len(hand) <= 1:
width = 0
x = (BG_WIDTH - CARD_WIDTH) // 2
elif len(hand) >= 8:
width = (BG_WIDTH - SIDE_W - CARD_WIDTH) // (len(hand) - 1)
x = BG_WIDTH // 2 - (CARD_WIDTH + (width * (len(hand) - 1))) // 2
else:
width = CARD_WIDTH
x = (BG_WIDTH - (CARD_WIDTH * len(hand))) // 2
surface.blit(background, (0, 0))
self.blit_backs()
self.blit_round()
self.show_ok()
self.show_hand(x, ROW3h, width, hand)
self.show_hand(CARD_POSITIONS[0][0], CARD_POSITIONS[0][1], SLIM_WIDTH, play)
if ok_clicked_2:
for card in play:
hand.append(card)
player.set_hand(player.order_hand(hand, self.game.get_round_level(),
self.game.get_round_trump_suit()))
# If player is first to start a round, he/she has a different validity check.
# (Sets the pattern for the cycle)
if player.get_begins_cycle():
valid = self.game.check_validity(True) # is_first
else:
valid = self.game.check_validity(False) # Is not first to play in the round
if not valid: # Clear holding if invalid
if (play == []) or (player.get_play() == []):
print("\nYou must make a play.\n")
else:
print("Invalid play. Try again.")
if not player.get_begins_cycle():
valid_plays = player.get_valid_plays(self.game.get_pattern(),
self.game.get_round_trump_suit())
print("Valid plays: \n")
for temp_play_idx in range(len(valid_plays)):
temp_play = valid_plays[temp_play_idx]
print("[", end='')
for temp_card_idx in range(len(temp_play)):
valid_plays[temp_play_idx][temp_card_idx].show_card("", '')
if temp_card_idx != len(temp_play) - 1:
print(", ", end='')
print("]")
# Clear the current player's selection and restore hand to its original content
cycle_order = self.game.get_cycle_order()
cycle = self.game.get_cycle()
for player_order in range(len(cycle_order)):
if player == cycle_order[player_order]:
cycle_order.remove(player)
cycle.pop()
self.game.set_cycle_order(cycle_order)
self.game.set_cycle(cycle)
else: # Valid play on submit
# Special case for HIGH_SUIT play, play lowest card if another player has greater
play = self.game.check_high_suit(play)
# If friend card played, establish and print teammates
# TODO: auto-designate friends if the last round
# has been reached (friends buried in treasure case)
# TODO: determine whether friend is "dead"
self.game.check_for_friends()
cycle = self.game.get_cycle()
cycle.append(play)
self.game.set_cycle(cycle)
cycle_order = self.game.get_cycle_order()
cycle_order.append(player)
self.game.set_cycle_order(cycle_order)
# self.clear_positions()
for card in play:
hand.remove(card)
player.set_hand(
player.order_hand(hand, self.game.get_round_level(),
self.game.get_round_trump_suit()))
self.game.next_player_turn()
self.game.set_play_in_turn(self.game.get_play_in_turn() + 1)
print(self.game.get_play_in_turn())
play = []
else:
self.game.set_play_in_turn(0)
# Distribute any points in the round to round winner
self.update_cycle_points()
for p in self.game.get_players():
for card in p.get_play():
discard = self.game.get_discard()
discard.append(card)
p.set_play([])
pygame.event.clear()
clock.tick(100)
pygame.display.update()
I think it's time for a code-cleanup, then your issue will go away (or you'll find it).
Currently the main loop is a big mix-up of event handling, screen-painting and game engine. Try to separate these parts out.
Move some of the in-loop processing out to functions - like the block after if ok_clicked_2:. It may help to make a data structure in which you store the game-state, then have the events change that game state. When it comes time to draw the game to the screen, the painting code can query the state, acting accordingly.
In terms of your actual lockup, if self.game.get_play_in_turn() == NUM_PLAYERS nothing is painted to the screen. Is this intentional? Add some print()s to your code so you can know the execution flow (or learn to use the python debugger).
I think the biggest step forward would be to move all the screen painting to one section of the main loop, something like:
# Render the screen
print( "Rendering Screen" )
surface.blit(background, (0, 0))
self.blit_backs()
self.blit_round()
# etc. for all other things, score, buttons, ...
clock.tick(60)
pygame.display.update()
You seem to be handling the events OK, so it would probably be better to remove the calls to pygame.event.pump() and pygame.event.clear(). You don't need these.
Following Kingsley's advice, I organized the code by function: rendering screen, game engine, and event handling. I would provide MRE as Random Davis suggests, but that would include 5 integrated files which would take too long to pare down.
It turns out that the problem lay in a piece of code which is called separately: "update_cycle_points()". Within, there is a while loop which does not contain an event handler. The solution was to change it to a for loop, which Pygame seems to process without error (does not freeze because it does not expect event handling there).
I also removed pygame.event.clear() and pump() functions without problems.

I made velocity changing to make dash but want to add here some dash-lenght limit

so I made my character(player) to change its velocity faster if I press 'L shift' while pressing 'left key' or 'right key' at the same time.
The problem is, I wanna make this 'dash' to stop when it reached to the limit I set. I want my character not to dash more than 400 at once. Is there any possible method I can do with..? because I tried many but I still couldn't find anything works.
Here's part of my mainloop where the dash is set. char is defined before the loop.
while run:
clock.tick(20)
for event in pygame.event.get():
keys = pygame.key.get_pressed()
mods = pygame.key.get_mods()
if event.type == pygame.QUIT:
run = False
elif keys[pygame.K_LEFT] and mods & pygame.KMOD_LSHIFT or keys[pygame.K_RIGHT] and mods & pygame.KMOD_LSHIFT:
print("pressed: SHIFT")
char.vel = 20
#I wanna set dash limit to 400px but evry try sitll is all failled..
else:
char.vel = 5
It's fairly easy to use a real-time millisecond limit to a dash. Then you can calibrate the time to however longer you wish the dash to be.
In the code below I've set this time limit to DASH_TIME_LIMIT. The player char has a new member variable named char.dash_finish. When the dash starts, we put the time limit for the dash in here. Then in the main loop we're checking each frame to see if the current time is after this time, which indicates the time limit has expired.
To start a dash, first we check that they aren't dashing already. Then the dash_finish time is simply "now" plus some milliseconds in the future.
DASH_TIME_LIMIT = 700 # milliseconds
for event in pygame.event.get():
keys = pygame.key.get_pressed()
mods = pygame.key.get_mods()
if event.type == pygame.QUIT:
run = False
elif keys[pygame.K_LEFT] and mods & pygame.KMOD_LSHIFT or keys[pygame.K_RIGHT] and mods & pygame.KMOD_LSHIFT:
print("pressed: SHIFT")
if ( char.dash_finish == None ): # Not already dashing?
char.vel = 20
char.dash_finish = pygame.time.get_ticks() + DASH_TIME_LIMIT
# else:
# char.vel = 5
# has the dash-time expired?
time_now = pygame.time.get_ticks()
if ( char.dash_finish == None ):
char.vel = 5
elif ( time_now > char.dash_finish ):
# dash has now finished
char.dash_finish = None
char.vel = 5
clock.tick(20)
Using a time limit is easier than counting the number of pixels traversed by the player each frame.
Instead of measuring pixels you can make a variable called dash_count and set it to zero at first and make it increase by one wheneverthe character dashes. This is possible with a while loop:
while dash_count <= 400:
pass
# make your character dash over here

Pygame : force playing next song in queue even if actual song isn't finished? (aka "Next" button)

I want to make with pygame the same functionalities as a walkman : play, pause , queuing is ok.But how to do the previous/next buttons?
How can I ,with pygame, force the play of the next song which has been queued (and pass the ong which is actually playing?)
Have a list of song titles, and keep track of where you are in the list with a variable. Whether you are using pygame.mixer.music or pygame.mixer.Sound, when the "next" button is clicked, just have the variable change by one, and then stop the song, and have song the variable corresponds to play instead.
Code example for pygame.mixer.Sound:
#setup pygame above this
#load sounds
sound1 = pygame.mixer.Sound("soundone.ogg")
sound2 = pygame.mixer.Sound("soundtwo.ogg")
queue = [sound1, sound2] #note that the list holds the sounds, not strings
var = 0
sound1.play()
while 1:
if next(): #whatever the next button trigger is
queue[var].stop() # stop current song
if var == len(queue - 1): # if it's the last song
var = 0 # set the var to represent the first song
else:
var += 1 # else, next song
queue[var].play() # play the song var corresponds to
This was an example i got working for myself to wrap my head around the behavior of the program.
from os import environ
environ['PYGAME_HIDE_SUPPORT_PROMPT'] = '1' # noqa This is used to hide the default pygame import print statement.
import pygame
import time
# Credit: https://forums.raspberrypi.com/viewtopic.php?t=102008
pygame.mixer.init()
pygame.display.init()
screen = pygame.display.set_mode((420, 240)) # Shows PyGame Window.
playlist = list()
playlist.append('/Music/Tom MacDonald - Angels (Explicit).mp3')
playlist.append('/Music/Falling In Reverse - Bad Girls Club (Explicit).mp3')
pygame.mixer.music.load(playlist.pop()) # Get the first track from the playlist
pygame.mixer.music.queue(playlist.pop()) # Queue the 2nd song
pygame.mixer.music.set_endevent(pygame.USEREVENT) # Setup the end track event
pygame.mixer.music.play() # Play the music
running = True
while running:
time.sleep(.1)
for event in pygame.event.get():
if event.type == pygame.USEREVENT: # A track has ended
if len(playlist) > 0: # If there are more tracks in the queue...
pygame.mixer.music.queue(playlist.pop()) # Queue the next one in the list
elif event.type == pygame.QUIT: # Create a way to exit the program.
running = False

How do I get the circles to appear on the pygame screen

Below is a small piece of my program. Currently, the program takes the user's input and converts it into a binary number in the Python Shell. I am trying to use that input so that I can graphically display the binary number. At the moment, I am unable to get anything to appear in the pygame screen. It is just white, no circles, no text. I am not sure why it is not working. I was advised by my teacher to turn this piece of code into a procedure, see if I have any luck, then get back to him. I was hoping somebody could pick out what is wrong with my procedure and either point it out to me or correct it. Any help would be much appreciated. I apologize if my formatting for the question is not spectacular, this is my first post.
from pygame import*
font.init()
comicFont=font.SysFont("ComicSansMS",12)
screen = display.set_mode((500,500))
binaryWord = str(100101)
binaryDigits = len(binaryWord)
binaryBlit = range(0,10)
binaryGraphicX = 0
color = (0,0,0)
color2 = (125,125,125)
pos = (binaryGraphicX,200)
radius = 15
width = 0
while True:
for binaryDigit in range (0,binaryDigits):
TxtPic = []
binaryGraphicX = binaryGraphicX + 25
if binaryWord[binaryDigit] == 1:
running=True
while running:
for evnt in event.get():
if evnt.type==QUIT:
running = False
event.get()
draw.circle(screen,color,pos,radius)
display.flip()
TxtPic[binaryDigit]=comicFont.render(str(2**binaryBlit),True,(0,0,0))
screen.blit(TxtPic[binaryDigit],(binaryGraphicX,220))
elif binaryWord[binaryDigit] == 0:
running=True
while running:
for evnt in event.get():
if evnt.type==QUIT:
running = False
event.get()
draw.circle(screen,color2,pos,radius)
display.flip()
TxtPic[binaryDigit]=comicFont.render(str(2**binaryBlit),True,(0,0,0))
screen.blit(TxtPic[binaryDigit],(binaryGraphicX,220))
quit()
Below is where I call on the procedure
running=True
while running:
for evnt in event.get():
if evnt.type==QUIT:
running = False
event.get()
screen.fill((255,255,255))
TxtPic1=comicFont.render(str(solution),True,(255,255,255))
screen.blit(TxtPic1,(200,200))
binaryGraphics(binaryNumber)
display.flip()
display.quit()
I have been looking through your code and the first obvious problem I found was that you are comparing a character/string to an int in your if statements.
ie.
String:
binaryWord = str(100101)
String to Int comparison: (would return false and skip)
if binaryWord[binaryDigit] == 1:
Try this instead:
if binaryWord[binaryDigit] == "1":
Your program never has a chance to get to your code that draws text/circles.

Categories