While using pygame.midi, Python consumes 20-25% of my CPU.
I guess it's because of the "While" loop which waits for MIDI input...
Any ideas? I'd appreciate any advice you might have...
Here is the loop:
going = True
while going:
events = event_get()
for e in events:
if e.type in [pg.QUIT]:
going = False
if e.type in [pg.KEYDOWN]:
going = False
if e.type in [pygame.midi.MIDIIN]:
if e.data2 == 127:
shortcuts(e.data1)
if i.poll():
midi_events = i.read(10)
# convert them into pygame events.
midi_evs = pygame.midi.midis2events(midi_events, i.device_id)
for m_e in midi_evs:
event_post(m_e)
You can limit the CPU usage with pygame.time.Clock.tick:
clock = pygame.time.Clock()
going = True
while going:
clock.tick(60)
# [...]
The method tick() of a pygame.time.Clock object, delays the game in that way, that every iteration of the loop consumes the same period of time.
Related
This question already has an answer here:
Spawning multiple instances of the same object concurrently in python
(1 answer)
Closed 1 year ago.
I'm new to programming and I need a way to limit the randomization of the variable food_choice so it doesn't print tons of images on my pygame screen.
Here is a section of my code. I believe the problem is because my function draw_food() is inside of the main loop, meaning that everytime the game runs with the determined tick, the function draw_food() is then run again, picking another food, causing an endless randomization. I need to way to limit this randomization to once per x seconds.
def draw_food():
food_choice = random.choice(food_types)
for object in object_list[:]:
screen.blit(food_choice, (foodx, foody))
object.deter -= 0.035
if object.deter < 1:
object_list.remove(object)
def draw_game_window():
screen.blit(bg, (0, 0))
draw_food()
cat.draw(screen)
pygame.display.update()
# main loop
cat = player(200, 355, 67, 65)
run = True
object_list = []
time_interval = 5000 # 200 milliseconds == 0.2 seconds
next_object_time = 0
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
draw_game_window()
current_time = pygame.time.get_ticks()
if current_time > next_object_time:
next_object_time += time_interval
object_list.append(Object())
pygame.quit()```
You can create a randomizeFood event every second like this.
randomizeFood = pygame.USEREVENT + 0
pygame.time.set_timer(randomizeFood, 1000) #time in ms
Then in your event loop, create random food when this event is generated.
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == randomizeFood:
food_choice = random.choice(food_types)
Eiter use a timer event as already explained in an answer, or use pygame.time.get_ticks. This function returns the number of milliseconds since the application was started.
Use global variables to store the time and the food. You have to use the global statement to change a global variable within a function
food_choice = None
next_choice_time = 0
def draw_food():
current_time = pygame.time.get_ticks()
if current_time >= next_choice_time:
next_choice_time = current_time + 1000 # in 1 second (1000 millisecodns)
food_choice = random.choice(food_types)
I want my program to cycle through each player, and this code does that. However, on the last player, it displays the info then instantly clears it. I want it to wait for a user to press a keydown (like space or enter) before clearing the screen. I tried implementing this with event = pygame.event.wait() but now my program just hangs when it reaches that declaration.
players = {}
for player in range(1, int(num_players)+1):
name = ask(DISPLAYSURF, "Player " + str(player) + "'s name")
player_roll = None
while player_roll is None:
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
pygame.event.clear()
while event.type != pygame.KEYDOWN:
event = pygame.event.wait()
DISPLAYSURF.fill(WHITE)
FIRST_DICE = roll_a_dice()
SECOND_DICE = roll_a_dice()
player_roll = FIRST_DICE + SECOND_DICE
players[name] = player_roll
display_dice(FIRST_DICE, SECOND_DICE)
our_roll(name)
My full code is here: https://github.com/Legitimate/Street-Dice/blob/master/main.py
Here is a video of the issue: https://youtu.be/ChxZq0bY4wk
It took a few minutes to understand what you meant, but after refactoring the list of players as the code below shows, the rest sort of rolled itself: https://github.cm/rebelclause/python_legs/blob/master/pygame/roll_the_dice.py. If it works for you, buy me a beer ;)
players = {'you': {'rolltotal': None, 'otherstuff': None }, 'me': {'rolltotal': None, 'otherstuff': None}}
def reviewvals():
print('Number of players: ', len(players)) # only counts the primary keys, the players
for player, attribdict in players.items():
for key, value in attribdict.items():
print(player, key, value)
reviewvals()
I haven't used Pygame before so I don't know if there is more efficient/ or right way of doing what you have asked for but anyway try this
Check this out
Pygame waiting the user to keypress a key
Also from docs -- this is why your program doesn't respond( which seem like it got hanged/ stuck but its not)..when it reaches event.wait()
https://www.pygame.org/docs/ref/event.html#comment_pygame_event_wait
pygame.event.wait()
wait for a single event from the queue
wait() -> EventType instance
Returns a single event from the queue. If the queue is empty this function will wait until one is created. The event is removed from the queue once it has been returned. While the program is waiting it will sleep in an idle state. This is important for programs that want to share the system with other applications.
Recently I've been working with a simple and straightforward RPG in python with pygame, but I'm having some problems delaying specific events. Running the code below, everything happens at once.
if event.key == pygame.K_SPACE and buttonHighlight == 0:
FireAnimation() #displays a fire image
#DELAY HERE
if player[6] == 'Magic': #you deal damage to the enemy
enemy[0] = enemy[0]-(((player[1])+((player[1])*1)-enemy[4]))
else:
enemy[0] = enemy[0]-(((player[1])+((player[1])*1)-enemy[3]))
#DELAY HERE
StarAnimation() #displays a star image
#DELAY HERE
if enemy[6] == 'Magic': #enemy deals damage to you
player[0] = player[0]-(((enemy[1])+((enemy[1])*1)-player[4]))
else:
player[0] = player[0]-(((enemy[1])+((enemy[1])*1)-player[3]))
The rest of the code isn't really relevant, I just wanted to point out where I want to delay. Running this, both images displays, the player and the enemy takes damage at the same time. Thanks!
EDIT: I forgot to mention that I already have tried pygame.time.delay/wait and time.sleep, but all those delays the whole operation! It simply pushes everything forward when I use it, so everything happens at the same time several seconds later
You can create two new events (FIRE_ANIMATION_START, STAR_ANIMATION_START) which you post to the event queue with a delay (with pygame.time.set_timer(eventid, milliseconds)). Then in your event loop you just check for it.
FIRE_ANIMATION_START = pygame.USEREVENT + 1
STAR_ANIMATION_START = pygame.USEREVENT + 2
# ... Your code ...
for event in pygame.event.get():
if event.key == pygame.K_SPACE and buttonHighlight == 0:
pygame.time.set_timer(FIRE_ANIMATION_START, 10) # Post the event every 10 ms.
pygame.time.set_timer(STAR_ANIMATION_START, 1000) # Post the event every 1000 ms.
elif event.code == FIRE_ANIMATION_START:
pygame.time.set_timer(FIRE_ANIMATION_START, 0) # Don't post the event anymore.
FireAnimation() #displays a fire image
if player[6] == 'Magic': #you deal damage to the enemy
enemy[0] = enemy[0]-(((player[1])+((player[1])*1)-enemy[4]))
else:
enemy[0] = enemy[0]-(((player[1])+((player[1])*1)-enemy[3]))
elif event.code == STAR_ANIMATION_START:
pygame.time.set_timer(STAR_ANIMATION_START, 0) # Don't post the event anymore.
StarAnimation() #displays a star image
if enemy[6] == 'Magic': #enemy deals damage to you
player[0] = player[0]-(((enemy[1])+((enemy[1])*1)-player[4]))
else:
player[0] = player[0]-(((enemy[1])+((enemy[1])*1)-player[3]))
Documentation for pygame.time.set_timer(eventid, milliseconds). Also, as the code is right now it has bugs in it. The attributes for the events differs between different event types, so always make sure to check whether an event is KEYDOWN or USEREVENT before accessing the attributes event.key or event.code. The different types and attributes can be found here.
If you know how much time you need, you can simply add:
from time import sleep
...
sleep(0.1)
This will add a 100 milliseconds delay
You can use
pygame.time.delay(n)
or
pygame.time.wait(n)
to pause the program for n milliseconds. delay is a little more accurate but wait frees the processor for other programs to use while pygame is waiting. More details in pygame docs.
I am currently making a game in Pygame, I have the basic game made as I followed a tutorial however know I want to improve it and make it so that after a certain amount of time (e.g. every 30 seconds or so) the game pauses and the user is asked a question and they must enter the right answer to continue playing the game. I have been looking on-line and experimenting a bit however struggling quite a lot! Here is the relevant code:
class question():
pressKeySurf, pressKeyRect = makeTextObjs('9+1.', BASICFONT, WHITE)
pressKeySurf, pressKeyRect = makeTextObjs('Press A for 10.', BASICFONT, WHITE)
pressKeySurf, pressKeyRect = makeTextObjs('Press B for 15.', BASICFONT, WHITE)
for event in pygame.event.get():
if (event.key==K_a):
showTextScreen('Correct answer') # pause until a key press
lastFallTime = time.time()
lastMoveDownTime = time.time()
lastMoveSidewaysTime = time.time()
else:
showTextScreen("Wrong")
pygame.time.set_timer(question,1000)
I have no idea if this is even going in the right direction at the moment as I can't get it to work because I think I understand the error and that I shouldn't be using a class in the timer, however not sure?
You want this to be a function, not a class. Replace class with def
The bigger problem is that your use of set_timer is incorrect. It is:
set_timer(eventid, timems)
where eventid and timems are both integers. set_timer puts an event into the event queue at the specified interval, it doesn't call any function. You have to put code in the main loop to check for that eventid and then execute the appropriate code.
Example code from this SO answer::
pygame.init()
pygame.time.set_timer(USEREVENT + 1, 100)
while True:
for event in pygame.event.get():
if event.type == USEREVENT + 1:
functionName()
if event.type == QUIT:
pygame.quite()
sys.exit()
I've found two related questions:
Pygame hold key down causes an infinite loop
pygame - on hold button down
But I want to be specific. How to?
while not done:
for e in event.get():
if e.type == KEYDOWN:
keys = key.get_pressed()
if e.type == QUIT or keys[K_ESCAPE]:
done = True
if keys[K_DOWN]:
print "DOWN"
When I press the down arrow, it prints, but it prints just once. If I want to print it another time, I need to press it again.
If I use the while keyword instead,
while keys[K_DOWN]:
print "DOWN"
I get an infinite loop for some obscure reason.
This logical alternative is also useless:
if ((e.type == KEYDOWN) and keys[K_DOWN]):
print "DOWN"
And there is this other one that somehow cleans the events and you can use while:
while not done:
for e in event.get():
if e.type == KEYDOWN:
keys = key.get_pressed()
if e.type == QUIT or keys[K_ESCAPE]:
done = True
while keys[K_DOWN]:
print "DOWN"
event.get()
keys = key.get_pressed()
But you press the down key for less than one second and it prints thousands of times. (Moving a player would be impossible, and adjusting clock for this does not seem to be the right way to deal with it (And I've tried and I've failed miserably.)).
To press and execute the block thousands of times is useless. What I want, is to press the key and keep going with the action while I don't release it, within the defined game clock speed.
Don't mix up event.get() and key.get_pressed().
If you press or release a key, and event is put into the event queue, which you query with event.get(). Do this if you're actually interested if a key was pressed down physically or released (these are the actual keyboard events. Note that KEYDOWN get's added multiple time to the queue depending on the key-repeat settings).
Also, there's no need to query the state of all keys while handling a KEYDOWN event, since you already know which key is pressed down by checking event.key
If you're interested in if a key is hold down (and ignoring the key-repeat, which you probably want), then you should simply use key.get_pressed(). Using a bunch of flags is just unnecessary and will clutter up your code.
So your code could simplified to:
while not done:
keys = key.get_pressed()
if keys[K_DOWN]:
print "DOWN"
for e in event.get():
pass # proceed other events.
# always call event.get() or event.poll() in the main loop
I am not familiar with Pygame, but, as I see, the program in it should have an event-based architecture. Unless you get the incoming events and process them, nothing happens. That's why your simple loop becomes infinite: it just does not process events.
while keys[K_DOWN]: # Nobody updates the keys, no events are processed
print "DOWN"
Then concerning the get_pressed() call. What it returns is a list of keys. So, you are trying to just loop until the key is released. That's a problem. According to this, pygame.event.get() returns immediately even if there are no events in the queue. The call to get() means: my code still has what to do, but I don't want to block the events, so please process the pending events before I continue. If your code is just waiting for an event, that means it has nothing to do.
The function to WAIT (without blocking the inner loop of Pygame) for an event is pygame.event.wait() (the logic is: I have nothing to do in my code until something happens). However, if you use it, you will have to get information about keys pressed or released from the event itself, not from get_pressed().
Here is an example from the comments to the doc page:
for event in pygame.event.get() :
if event.type == pygame.KEYDOWN :
if event.key == pygame.K_SPACE :
print "Space bar pressed down." #Here you should put you program in the mode associated with the pressed SPACE key
elif event.key == pygame.K_ESCAPE :
print "Escape key pressed down."
elif event.type == pygame.KEYUP :
if event.key == pygame.K_SPACE :
print "Space bar released."
elif event.key == pygame.K_ESCAPE :
print "Escape key released." #Time to change the mode again
I like to take a slightly different approach to this problem. Instead of checking if the key is pressed and taking some action when it is, set a flag on key down and unset it on key up. Then in the function to update the player's position, check the flag and update accordingly. The following pseudo-Python explains what I'm getting at:
if down_key_pressed:
down_flag = True
elif down_key_released:
down_flag = False
elif right_key_pressed:
etc...
This should be done in a separate loop that takes the player's input. Then in update_player_position() you can do:
if down_flag:
move_player_down()
elif right_flag:
move_player_right()
This example assumes four-directional movement, but you could extend it to eight-directional fairly easily by just using if down_flag and right_flag instead.
You can get keydown event repeatedly if you use pygame.key.set_repeat(# millisecond) to set the time limitation for each key event. Quote: when the keyboard repeat is enabled, keys that are held down will generate multiple pygame.KEYDOWN events. The delay is the number of milliseconds before the first repeated pygame.KEYDOWN will be sent. After that another pygame.KEYDOWN will be sent every interval milliseconds. If no arguments are passed the key repeat is disabled. When pygame is initialized the key repeat is disabled. please see following link for detail http://www.pygame.org/docs/ref/key.html#pygame.key.set_repeat
I am using a different approach on holding down a key that I am using in the specific task of moving an object left or right.
I do not care about the computer knowing that a key is actually held down.
When I press a key, a variable that I define for that key (EG: left arrow) is set to True.
Until that key is unpressed (with the event pygame.KEYUP) the "movement to left" is performed.
Dominik's solution is the perfect one (separating event.get() from the keyboard ones). It works perfectly! Finally, no more problems with pygame's input.
Flags:
flag = False # The flag is essential.
while not done:
for e in event.get(): # At each frame it loops through all possible events.
keys = key.get_pressed() # It gets the states of all keyboard keys.
if e.type == QUIT:
done = True
if e.type == KEYDOWN: # If the type is KEYDOWN (DIFFERENT FROM "HELD").
if keys[K_DOWN]: # And if the key is K_DOWN:
flag = True # The flag is set to true.
elif e.type == KEYUP: # The very opposite.
if keys[K_DOWN]:
flag = False
if flag == True: # DON'T USE "while flag == true", it'll crash everything. At every frame, it'll check if the flag is true up there. It's important to remember that the KEYDOWN worked ONLY ONCE, and it was enough to set that flag. So, at every frame, THE FLAG is checked, NOT THE KEYDOWN STATE. Every "player movement" while a key is being held MUST be done through flags.
print "DOWN"
This is basically How I did it, well Go to www.cswonders.com, and it might help you . Go to level 3, then 3.6, and go through the tutorial. I learned it from there. Here is my code.
def update(self):
self.speedx = 0
self.speedy = 0
# If left or right key is pressed, move left or right
pressed_key = pygame.key.get_pressed()
if pressed_key[pygame.K_LEFT]:
self.speedx = -10
if pressed_key[pygame.K_RIGHT]:
self.speedx = 10
if pressed_key[pygame.K_UP]:
self.speedy = -10
if pressed_key[pygame.K_DOWN]:
self.speedy = 10
self.rect.x += self.speedx
self.rect.y += self.speedy
# No further move if off screen
if self.rect.right > SCREEN_WIDTH:
self.rect.right = SCREEN_WIDTH
if self.rect.left < 0:
self.rect.left = 0