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!
Related
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've been recently trying to code a quick game which involves binary to hex conversion. I've set up some of the basic structure of the code but I've stumbled upon a problem: My selection statement in the game's entry point doesn't work as intended even though the values should be passed on correctly
What I've tried:
I tried "debugging" the program by adding print statements in between the function and statements, eg:
if event.key == pygame.K_KP_ENTER:
print('enter')
print(key_value)
key_value = 1
print(key_value)
The values when printed are correct, which are used in the game's while loop point:
while running:
if EventHandler.get_key_pressed() == 1:
print('1')
elif EventHandler.get_key_pressed() == 2:
print('2')
Changed the conditions of the statement, still got the same results eg:
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_KP_ENTER:
print('enter')
key_value = 2
elif event.key == pygame.K_SPACE:
print('space')
key_value = 1
Changed the get_key_pressed() function to a non-static one, still had the same results.
Went back to basic python tutorials to make sure my indentation and structuring/usage of selection statements are correct.
From my observations, only the first if statement after the while loop in works, however I'm not sure as to why that happens when I think I formatted my code properly.
Code:
main.py
import pygame
from Include.src.event_handler import EventHandler
# other imports go here, not related to problem.
# Global Variables
running = True
# Object creations, not really related as well
game = Game() # initialise pygame, settings and assets
screen = game.get_screen() # get screen bounds/object
while running:
if EventHandler.get_key_pressed() == 1: # Issue here
print('1')
elif EventHandler.get_key_pressed() == 2:
print('2')
pygame.display.flip()
pygame.display.update()
event_handler.py
import pygame
class EventHandler:
#staticmethod
def get_key_pressed():
key_value = 0
for event in pygame.event.get():
if event.type == pygame.QUIT:
print('Thanks for debugging me or playing idk')
pygame.quit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_KP_ENTER:
print('enter')
key_value = 1
elif event.key == pygame.K_SPACE:
print('space')
key_value = 2
return key_value
Expected output
Person presses space, space and 2 is printed.
Person presses enter, enter and 1 is printed.
Actual output
Space pressed, only space is outputted.
Enter pressed, both enter and 1 is outputted.
Thank you so much!
For anyone looking back to this post, I've found the answer:
As #jasonharper said, the value was being thrown since the function was being called again
while running:
e = EventHandler.get_key_pressed() # make it constant
if e == 1:
print('1')
elif e == 2:
print('2')
I want to write a gomoku game with server and client. The terminal version works well, but pygame version just blocked and can't rend anything.
Here is the game execute function
First it start a socket connection
self._running = True in init, package get from server is like {"grid":GRID(strings with 0 and 1), "x":X, "y":Y, "player":LAST_PLAYER, "next_player":CURRENT_PLAYER, "gameover":IS_GAMEOVER}
In the the loop:
parse the package
check if gameover, if so show information about gameover and close the socket
if current player is me, call on_event and let user move( I suspect this step is wrong, so the parse package step blocked the main thread? But how should I fix this ) and then send the new move package to server
if current player is not me, then send a special package to server I am waiting(I know this is a bit stupid)
This is the loop like
while self._running:
data = self.client_thread.reply_q.get(True)
if data:
self.last_player = data["player"]
self.grid = self.grid_str_2_matrix(data["grid"])
self.lastPosition = [data["x"], data["y"]]
self.gomoku_board_init()
if data["gameover"] == -1:
if data["next_player"] != self.player:
self.client_thread.cmd_q.put(ClientCommand(ClientCommand.SEND, {"wait": True}))
print("waiting")
else:
for event in pygame.event.get():
self.on_event(event)
print("new move")
else:
print("game over")
self._running = False
if data["gameover"] == 0:
self.winner = 0
else:
self.winner = data["player"]
self.client_thread.cmd_q.put(ClientCommand(ClientCommand.CLOSE))
break
self.on_render()
self.on_cleanup()
and the on_event function be called in the middle to accepet user's next move
if data["gameover"] == -1:
if data["next_player"] != self.player:
...
else:
for event in pygame.event.get():
self.on_event(event)
code as this
def on_event(self, event):
print(event.type == pygame.MOUSEBUTTONUP)
if event.type == pygame.MOUSEBUTTONUP:
pos = pygame.mouse.get_pos()
r = (pos[0] - PADDING + WIDTH // 2) // (WIDTH + MARGIN)
c = (pos[1] - PADDING + WIDTH // 2) // (WIDTH + MARGIN)
print(r, c)
if 0 <= r < self.board_row and 0 <= c < self.board_column and self.grid[r][c] == 0:
self.grid[r][c] = self.player
data = {"grid":self.grid, "x":r, "y":c, "player":self.player}
self.client_thread.cmd_q.put(ClientCommand(ClientCommand.SEND, data))
What I've tried:
I added a print in on_event print(event.type == pygame.MOUSEBUTTONUP) and of course MOUSEBUTTONUP never happened(But I wonder why?)
So I decide just skip this using random input
code as follows
#for event in pygame.event.get():
# self.on_event(event)
x, y = self.random_position()
self.grid[x][y] = self.player
data = {"grid":self.grid, "x":x, "y":y, "player":self.player}
self.client_thread.cmd_q.put(ClientCommand(ClientCommand.SEND, data))
The result is package goes all right BUT GUI still blocking and even I add sleep in the while loop, it rended only when gameover
I am new in python multithread and also pygame with socket, I wrote a just pygame part it works good, so does terminal+socket.
You are reading from a reply queue with a param block=True. Which means that it is blocking whole while loop. And that on_render call is blocked as well by reply_q.get(True). So if you are not constantly feeding reply_q with messages then your screen is not going to be re-rendered. By the way, your events processing code is also gonna be blocked.
docs.python#Queue.get
Usually those kind of issues in Pygame and other GUI frameworks come from the special role the main thread has. Quoting from pygame's docs:
Pygame handles all its event messaging through an event queue. The routines in this module help you manage that event queue. The input queue is heavily dependent on the pygame.displaypygame module to control the display window and screen module. If the display has not been initialized and a video mode not set, the event queue may not work properly. The event subsystem should be called from the main thread. If you want to post events into the queue from other threads, please use the pygame.fasteventpygame module for interacting with events and queues module.
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.
I'm having some trouble with the keyboard events in my programme. Long story short I was using pygame.KEYDOWN events but then heard from pretty much everyone that get_pressed() is a better suited option. I changed my code accordingly but have run into a few problems
Firstly:
If I am holding two keys but then only release one, pygame for some reason thinks that I have released both. This means that diagonal movement is a pain to implement
Secondly:
Diagonal movement IS working but only in certain cases when:
I'm moving up and down and hold left or right
It does not (for some reason) work if I'm going left or right and hold up or down
Here is the code I've been using:
while done == False:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
keys = pygame.key.get_pressed()
if (keys[K_KP6]):
square.spd_x+=5
if square.spd_x > 5: # Put these speed limits in a function
square.spd_x = 5
elif (keys[K_KP4]):
square.spd_x -=5
if square.spd_x <-5:
square.spd_x = -5
elif (keys[K_KP8]):
square.spd_y -=5
if square.spd_y <-5:
square.spd_y = -5
elif (keys[K_KP2]):
square.spd_y +=5
if square.spd_y >5:
square.spd_y = 5
else:
square.spd_x = 0
square.spd_y = 0
If anyone could shed light on the issue I'd be extremely grateful and I thank you very much for attempting to answer
Thank you :D
I don't know if this will work, but it's worth a shot.
while done == False:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
keys = pygame.key.get_pressed()
if (keys[K_KP6]):
square.spd_x=5
else:
square.spd_x=0
if (keys[K_KP4]):
square.spd_x-=5
if (keys[K_KP8]):
square.spd_y=-5
else:
square.spd_y=0
if (keys[K_KP2]):
square.spd_y +=5
Let me know if it works.